grammar

package
v0.34.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

grammar — maintainer notes

This document is the long-form companion to the grammar package code.

The source files keep godoc concise; the full grammar contract, pipeline rules, keyword tables, body-termination rules, and quirks live here.


Table of contents


§overview — what the package parses

grammar is the annotation parser for codescan. It consumes one Go comment group (*ast.CommentGroup) at a time and produces a typed Block carrying:

  • the recognised annotation (swagger:model, swagger:route, …) as an AnnotationKind;
  • per-Block fields for the annotation's positional arguments (e.g. RouteBlock.Method, ParametersBlock.OperationIDs);
  • Property entries for every recognised body keyword (maximum:, pattern:, consumes:, …) carrying the keyword's lexer-typed value or raw body bytes;
  • prose lines split into Title() / Description() using line-shape heuristics;
  • diagnostics for malformed inputs (the parser never aborts — it emits warnings/errors and continues).

Comment groups without a swagger:<name> line surface as an UnboundBlock so the schema builder can still consume the prose and any field-level annotations.

The annotation vocabulary is the go-swagger convention: swagger:model, swagger:response, swagger:parameters, swagger:route, swagger:operation, swagger:meta, plus the classifier annotations swagger:strfmt, swagger:alias, swagger:name, swagger:allOf, swagger:enum, swagger:ignore, swagger:default, swagger:type, swagger:file.

AnnotationPrefix is the literal "swagger:". It is a constant rather than configurable today.


§pipeline — Preprocess → Lex → Parse

*ast.CommentGroup
     │
     ▼
  Preprocess  → []Line       (comment-marker stripping)
     │
     ▼
     Lex      → []Token      (line classifier + body accumulator + prose classifier)
     │
     ▼
    Parse     → Block        (dispatch by annotation family)

Three stages, each a pure function:

  1. Preprocess strips comment markers and normalises line endings (§preprocess-contract).
  2. Lex runs three sub-stages — line classifier, body accumulator, prose classifier — producing the terminal token stream (§lexer-contract).
  3. Parse dispatches the stream to a family-specific parser per the recognised AnnotationKind (§parser-contract).

The Token vocabulary is defined in token.go and matches the documented terminal alphabet for the four annotation families (schema / operation / meta / classifier) plus the family-shared keyword vocabulary.


§preprocess-contract — comment-marker stripping

Preprocess turns a *ast.CommentGroup into a position-tagged []Line. Per line:

  • Text has comment markers (//, /*, */) stripped along with godoc continuation decoration (leading whitespace, asterisks, slashes, optional markdown table pipe). This is the surface the keyword/annotation classifiers consume.
  • Raw is the same source line with only the comment marker removed — content whitespace, indentation, and list markers survive. The body accumulator reads Raw for YAML / nested-map indentation fidelity.
  • Pos points to the first character of Text in the source file so diagnostics can pinpoint the offending line.

Line endings are normalised before line splitting (\r\n → \n, lone \r → \n) so the lexer never sees \r.

The /* */ block-comment form yields one Line per physical source line; the godoc continuation decoration (\s*\*\s?) is stripped from each line.

Leading - is preserved on Text so the YAML fence --- survives intact.


§lexer-contract — token production

Lex runs three stages:

  1. Line classifier (classifyLines / lexLine). Pure function on a single line plus an in-fence flag carried between lines. Emits one preliminary token per line — TokenBlank, tokenYAMLFence, tokenRawLine (inside an active fence), TokenAnnotation, tokenKeywordPre, or tokenText.

  2. Body accumulator (accumulateBodies). State machine over the line stream, folding multi-line bodies (OPAQUE_YAML, RAW_BLOCK_*, RAW_VALUE_*) into single body tokens and finalising inline-value keywords with their typed payload. The output stream contains only tokens the parser actually consumes; internal kinds (tokenYAMLFence, tokenText, tokenKeywordPre, tokenRawLine, tokenDirective) are stripped here.

  3. Prose classifier (classifyProse). Re-types surviving tokenText tokens as TokenTitle or TokenDesc per the line-shape heuristics in §prose-classification.

The output stream terminates with a single TokenEOF.

Recognised annotation prefix

hasSwaggerPrefix matches AnnotationPrefix with only the first character case-permissive ([Ss]wagger:). The rest of the prefix is verbatim, matching authorial convention.

Godoc-prefix exception for swagger:route

A line of the form <GoIdent><WS>swagger:route <args> has the leading <GoIdent><WS> stripped before annotation lexing. matchGodocRoutePrefix implements the check; only swagger:route is granted this exception (the form is a long-standing godoc convenience — a function or constant identifier on the same line as the annotation). Other annotation names do not get the heuristic.

Go directives are dropped

Lines like //go:generate, //nolint:foo, //lint:ignore are recognised by isGoDirective (lowercase-word + : + immediate non-whitespace argument, no leading whitespace) and dropped from the prose surface so they never land in TITLE / DESC. The swagger-prefix check runs first, so //swagger:model (legal but non-idiomatic, no leading space) is not mistaken for a directive.

First-character case insensitivity on keywords

Consumes: and consumes: both lex as the consumes keyword. Only the leading character is lowercased before Lookup(name); the rest of the keyword name is matched verbatim. So Consumesconsumes matches, but CONSUMES does not.

items. prefix runs

Repeated items. segments before a keyword (e.g. items.items.maxLength:) are stripped and counted; ItemsDepth records the depth on the emitted keyword token. Bare items: (no separator) is not stripped — it is a legitimate keyword on its own (the items chain head). stripItemsPrefix implements the peel.

Trailing-dot elision

After extracting Value (Keyword) or Args (Annotation), a single trailing . is stripped. Source preservation lives in Raw. The rule allows authors to write keyword and annotation lines as English sentences without leaking the period into the parsed value.


§prose-classification — TITLE / DESC split

classifyProse re-types tokenText tokens as TokenTitle or TokenDesc using four heuristics applied to the first contiguous prose run:

  1. Blank inside the run splits title/desc. A blank line inside the run with text after it ends the title and starts the description.
  2. First line ends with Unicode punctuation (\p{Po}) — first line is title, the rest is description.
  3. First line is a markdown ATX heading (#+ followed by whitespace) — strip the marker, first line is title, the rest is description.
  4. Otherwise the entire run is description, title empty.

Later prose runs (after body / keyword tokens) become description unconditionally.

Heuristic 1 only fires when there is text after the blank — trailing blanks are separators between the prose run and the following non-prose token (annotation / EOF), not an internal title/desc divide. On a heuristic-1 split, an ATX marker on the first title line is also stripped so the rendered title doesn't carry the #+ prefix.

The classification fires regardless of whether the block carries an annotation — UnboundBlock-style comments (struct-field docstrings) still need title/description so the schema builder can consume them when the type is reached indirectly (e.g. an embedded interface in a swagger:model parent).

The state-machine in classifyTitleDescRun walks the run once and re-types text tokens; blanks stay as TokenBlank so consumers can reproduce paragraph structure.


§raw-block-terminators — sibling-terminator rule

Multi-line keyword bodies (consumes:, produces:, security:, responses:, parameters:, extensions:, default:, example:, enum:, …) end at the next sibling structural item or EOF. Blank lines never terminate a body — they are absorbed verbatim into the body content.

A "sibling structural item" is any of:

  • another TokenAnnotation;
  • a tokenKeywordPre whose canonical name shares a family context with the opening keyword (§context-legality);
  • a tokenYAMLFence outside an extensions body (the extensions body absorbs decorative fences silently — see §yaml-fence-handling).

The family-overlap rule lives in isSiblingTerminatorFor and familyOf:

  • bodies opened under a meta/route/operation context keyword (consumes, produces, security, securityDefinitions, responses, parameters, extensions, externalDocs, infoExtensions, tos, schemes) terminate on any sibling whose family overlaps with the meta/route/operation set;
  • bodies opened under a schema-context body keyword (default, example, enum) terminate on any sibling whose family overlaps with the schema set.

A keyword whose name is recognisable but whose family does not overlap is absorbed as body text — this matches the permissive shape of nested YAML-like content under e.g. security:.

Inline-value capture on raw-block heads

Consumes: application/json on a single line carries its value on the head token (head.Text). collectRawBlock prepends that value as the first body line so mixed inline-plus-indented forms work uniformly. Without this prepend, the post-colon payload would be silently lost.

Per-body indentation handling
  • extensions: and infoExtensions: bodies are YAML-parsed downstream (yaml.TypedExtensions), so every body line preserves its original indentation — collectRawBlock reads the Raw view (right-trimmed only).
  • Flat raw blocks (consumes:, produces:, security:, …) use the Text view (leading whitespace dropped, keyword lines reformatted via formatKeywordLine).

Both branches converge on the same bodyText slice.

Single-line raw-value path

collectRawValue has a trivial single-line path: when the head token carries an inline value, one TokenRawValueBody is emitted immediately with the inline value as the whole body. The multi-line path is reserved for the block-head case (head with empty inline value).


§yaml-fence-handling — opaque YAML bodies

collectFencedYAML scans from a --- opener and emits one TokenOpaqueYaml:

  • the body is joined with \n into Body;
  • Raw carries the verbatim content (indentation preserved);
  • Truncated = true is set when EOF is reached without a closing ---parser.consumeBodyToken then emits a CodeUnterminatedYAML error diagnostic.

Decorative --- fences inside an extensions: body are dropped silently — authors decorate extensions blocks with fences as a visual separator; the lexer absorbs the fence into the body and discards the fence markers themselves via absorbDecorativeFenceInto.

swagger:route does not allow OPAQUE_YAML bodies — only swagger:operation does. The parser flags an OPAQUE_YAML under route with CodeUnexpectedToken.


§disambiguation — value-shape dispatch

disambiguate.go centralises the value-shape rules so the lexer emits already-disambiguated typed tokens. The parser never re-decides.

swagger:default value

classifyDefaultValue tries JSON_VALUE first (full JSON validation via the stdlib decoder), falling back to RAW_VALUE. A leading quote / bracket / brace / sign / digit is the quick discriminant; true / false / null are also JSON-valid.

swagger:enum arguments

classifyEnumArgs implements the four-way dispatch on the post-name remainder:

  • empty → enumFormEmpty (multi-line body may follow);
  • leading [enumFormBracketedOnly (one JSON_VALUE arg, no name);
  • leading identifier + no rest → enumFormNameOnly;
  • leading identifier + leading [ rest → enumFormNamePlusBracketed;
  • otherwise → enumFormPlainOnly or enumFormNamePlusPlain.

Bracketed lists are emitted as a single TokenJSONValue; plain lists as a single TokenCommaListValue; the name (when present) as a separate TokenIdentName. Downstream parsing of the list items lives in the analyzer.

swagger:type argument

isTypeRef matches the closed type-reference vocabulary (string, integer, number, boolean, array, object, file, null). Anything else falls back to TokenIdentName, letting the parser diagnose CodeInvalidTypeRef.

HTTP method recognition

classifyHTTPMethod matches the closed HTTP-method vocabulary (GET / POST / PUT / PATCH / HEAD / DELETE / OPTIONS / TRACE) case-insensitively, emitting the canonical upper-case form on TokenHTTPMethod.

URL-path recognition

looksLikeURLPath is a coarse check (leading /). Full RFC 3986 conformance is left to the analyzer.


§parser-contract — Block construction

DefaultParser.Parse consumes a comment group end-to-end: preprocess → lex → parse. ParseAll returns one Block per annotation in source order; the partition rule splits at each TokenAnnotation index. The first annotation owns the pre-annotation prose; later annotations partition from one annotation header to the next.

ParseText and ParseAs are entry points for non-CommentGroup inputs (raw text from sub-parsers like routebody, or synthesised annotation headers for tests).

The parser interface is a stable seam (Parser) so tests can substitute mocks; the package ships *DefaultParser as the only production implementation.

Dispatch by family

parseTokens finds the first TokenAnnotation, looks up its AnnotationKind, and dispatches by family:

Family Annotations Parser entry
familySchema swagger:model, swagger:response, swagger:parameters, swagger:name parseSchemaBlock
familyOperation swagger:route, swagger:operation parseOperationBlock
familyMeta swagger:meta parseMetaBlock
familyClassifier swagger:strfmt, swagger:alias, swagger:allOf, swagger:enum, swagger:ignore, swagger:default, swagger:type, swagger:file parseClassifierBlock
familyUnknown unrecognised parseUnboundBlock

swagger:name dispatches through the schema family because its body accepts the same validation-keyword vocabulary as a schema field (min length, pattern, required, etc.). Surfacing those body keywords as Properties — rather than rejecting them as context-invalid under a classifier block — keeps the field-level walker uniform.

Body-token consumption

consumeBodyToken is the per-token sink shared across families:

  • TokenKeyword → typed Property via emitInlineKeyword (validation against the keyword's shape).
  • TokenRawBlockBody → raw Property via emitRawBlock. For extensions: / infoExtensions: the body is also fed through yaml.TypedExtensions to populate per-entry typed values (§typed-extensions). For security: the body is parsed into typed Requirements (§security-requirements).
  • TokenRawValueBody → raw Property via emitRawValue.
  • TokenOpaqueYamlRawYAML entry on the Block; emits CodeUnterminatedYAML if Truncated.
  • Stray value-only tokens (TokenIdentName outside an owning keyword) emit CodeUnexpectedToken.
Context-legality warnings

contextLegal reports whether a keyword may legally appear under the given annotation kind. A mismatch is non-fatal — the parser emits a CodeContextInvalid warning and still records the property. See §context-legality.

parseState scaffolding

parseState.peek / parseState.advance and the pos cursor are scaffolding for future order-sensitive productions (LSP partial parses, strict positional checks on EnumDeclBlock's annotation header → RAW_VALUE_ENUM body). Today's family parsers walk s.tokens via range loops because the token classifier already serialises the body — order between annotation header and body items is flat. When order-sensitive productions land, the per-family parsers will switch to peek/advance.


§block-shapes — typed Block kinds

Every Block implements the Block interface (one consumer contract for builders + LSP):

  • Pos(), Title(), Description(), Diagnostics(), AnnotationKind();
  • Properties(), YAMLBlocks(), Extensions(), SecurityRequirements(), Contact(), License();
  • Walk(w Walker) — the functional-visitor surface;
  • ProseLines(), PreambleLines(), PreambleTitle(), PreambleDescription(), Prose();
  • Has(name), GetFloat, GetInt, GetBool, GetString, GetList;
  • AnnotationArg() — single-word convergence accessor for the annotation's primary positional argument.

Typed Block kinds embed *baseBlock and add per-annotation fields:

Block Annotation Extra fields
ModelBlock swagger:model [Name] Name string
ResponseBlock swagger:response [Name] Name string
ParametersBlock swagger:parameters T1 T2 … OperationIDs []string
NameBlock swagger:name <ident> Name string
RouteBlock swagger:route METHOD /path [tags] opID Method, Path string; Tags []string; OpID string
InlineOperationBlock swagger:operation METHOD /path [tags] opID same as RouteBlock
MetaBlock swagger:meta
ClassifierBlock swagger:strfmt, swagger:type, … Args []Token
EnumDeclBlock swagger:enum [name] [values…] Name string; InlineForm enumArgsForm; InlineArgs []Token; BodyValues string
UnboundBlock no annotation
Preamble vs full prose

PreambleTitle / PreambleDescription / PreambleLines cover only the prose appearing before the block's annotation. Schema's top-level model builder consumes the preamble so post-annotation text reads as body content rather than as part of the title/description. Routes / operations / meta consult the full Title() / Description() (whole-block prose).

Prose() — single-string description

Prose() returns the entire prose surface (TITLE + DESC tokens in source order) joined with \n, internal blanks preserved as paragraph breaks, a single trailing blank dropped. Used by field-level callers (struct-field / interface-method docs) where the whole prose is the description.

AnnotationArg — convergence accessor

Returns the first single-word positional identifier argument of the block's primary annotation, or ("", false) for bare annotations / multi-word args. Replaces type-asserting on each typed Block kind to read its Name field. Used by Walker callbacks that don't care which classifier flavour they are looking at — only what its IDENT_NAME-style argument is.

ClassifierBlock.AnnotationArg filters to a single non-empty word, mirroring the legacy single-word capture: prose lines that happen to open with swagger:<kind> followed by a sentence are rejected at this layer.


§property-shape — Property and TypedValue

Property is one keyword:value (or keyword body) attached to a Block. Field population varies by shape:

  • Inline-value keywords (Number / Integer / Bool / String / EnumOption / CommaList): Value carries the raw string, Typed carries the lexically-typed form.
  • Body keywords (RawBlock / RawValue): Body holds the accumulated body content (joined with \n), Raw holds the verbatim source content (indentation preserved), and Typed.Type is ShapeRawBlock / ShapeRawValue.

ItemsDepth records the leading items.* depth from the keyword head — 0 for level-0 keywords, N for items.…N chain depth.

TypedValue.Op for comparison-bound numbers

A NumberValue may carry a leading comparison operator (<, <=, >, >=, =); the lexer strips it to TypedValue.Op so the analyzer can decide inclusive vs exclusive semantics (maximum: <5 is exclusive max; maximum: <=5 is inclusive). The Walker collapses < / > to an exclusive bool on the Number callback.

IsTyped — primitive-typed shortcut

Property.IsTyped() returns true when Typed.Type is one of the primitive shapes (Number / Integer / Bool / EnumOption) — i.e. a case where the matching Typed.<field> is populated and authoritative. Returns false otherwise (raw shapes, comma-list, string, ShapeNone). Consumers use it as a switch shortcut:

if p.IsTyped() {
    // read p.Typed.<field> matching p.Keyword.Shape
} else {
    // coerce p.Value against the resolved schema type
}
AsList — unified list extraction

Property.AsList() (also reachable via Block.GetList(name)) unifies every list-shaped surface form:

Schemes: http, https            # inline, comma-separated
Schemes:                        # multi-line, indented bare
  http
  https
Schemes:                        # multi-line, YAML `- ` markers
  - http
  - https
Schemes: http, https            # inline + indented continuation
  - ws

Algorithm: treat Value (if non-empty) as one input line, then each line of Body. For each line: trim, drop a leading - YAML marker if present, re-trim, comma-split, trim each token, drop empties. Aggregate.

The helper stops at "simple token lists" — it does not handle enum values (whose elements may be JSON arrays), the + name: Parameters chunk grammar (routebody-owned), or raw bodies that need YAML structural parsing (securityDefinitions, extensions, infoExtensions — those travel through yaml.TypedExtensions / json.Unmarshal directly).


§walker-contract — functional-visitor dispatch

Walker is the functional-visitor surface a Block exposes for bulk dispatch. Consumers wire only the callbacks they care about; nil callbacks are silent no-ops.

Dispatch order
  1. Block-level diagnostics fire first (before Title) so consumers see them regardless of which property callbacks they wired.

  2. Title fires once if non-empty.

  3. Description fires once if non-empty.

  4. Properties fire in source order — one callback per Property selected by Keyword.Shape:

    Keyword.Shape Callback Payload
    ShapeNumber Number (p, p.Typed.Number, exclusive)
    ShapeInt Integer (p, p.Typed.Integer)
    ShapeBool Bool (p, p.Typed.Boolean)
    ShapeString String (p, p.Value)
    ShapeEnumOption String (p, p.Typed.String)
    ShapeRawBlock Raw (p) — caller reads p.Body / p.Raw
    ShapeRawValue Raw (p)
    ShapeCommaList Raw (p) — caller splits via b.GetList
    ShapeNone Raw (p) — fallback

    An unknown keyword (Property.Keyword.Name empty) fires the Unknown callback instead.

  5. Extensions fire in source order, one callback per Extension entry.

Iteration scope

Walker walks one Block per call; ordering across blocks (multiple declarations, file order, discovery order) is the builder's concern, not the walker's.

Shape-based dispatch, not Typed.Type

When the lexer rejects an invalid value (e.g. maximum: notanumber) the parser leaves Typed.Type at ShapeNone and emits a CodeInvalidNumber diagnostic. Walker still dispatches based on Keyword.ShapeNumber/Integer/Bool callbacks fire with the zero value of the payload. Consumers treat the Diagnostic callback as authoritative for malformed values rather than re-validating.

FilterDepth — items-chain gating

FilterDepth gates property callbacks (Number / Integer / Bool / String / Raw / Unknown). Title / Description / Extension / Diagnostic are unaffected.

  • AllDepths (-1) admits every depth — use this explicitly for "fire every property" rather than -1 so the intent reads at the call site.
  • 0 admits level-0 only — the schema-side default.
  • N admits depth N only — used by items-chain walkers.

Zero-value gotcha: the Go zero value of FilterDepth is 0, which means "level-0 only". Items callers must explicitly set FilterDepth to the wanted depth; they cannot leave it at the zero value. Schema-side level-0 walkers can leave it at zero by accident-and-design.

Concurrency

Walk reads only from the Block — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.


§keyword-table — closed-vocabulary keywords

keywords.go defines the authoritative keyword table. Each entry declares a canonical name, optional aliases, a ValueShape, and the family contexts where it is legal (§context-legality).

Kw* constants (KwMaximum, KwSchemes, …) are the single source of truth for spelling: every Property's Keyword.Name compares equal to exactly one of them. Consumers that switch on Keyword.Name should reference the constants rather than re-declaring the strings — the schema walker and the bridge dispatchers in routes / parameters / responses / operations / items / spec all dispatch on these names.

ValueShape vocabulary
Shape Terminal Notes
ShapeNumber NUMBER_VALUE signed decimal, optional leading comparison operator
ShapeInt INT_VALUE unsigned decimal integer
ShapeBool BOOL_VALUE true / false (case-insensitive)
ShapeString STRING_VALUE verbatim non-LF text
ShapeCommaList COMMA_LIST_VALUE comma-separated list of strings (trim-stripped)
ShapeEnumOption ENUM_OPTION_VALUE closed-vocab choice (Values lists the allowed set)
ShapeRawBlock RAW_BLOCK_<KW> multi-line body terminal — caller reads Body/Raw
ShapeRawValue RAW_VALUE_<KW> multi-line OR single-line body terminal
ShapeNone no value shape (rare; ShapeNone keywords reach Walker's Raw callback)

ValueShape.IsBody() reports whether the shape is a multi-line body terminal (RawBlock or RawValue) — the lexer's body accumulator triggers on body shapes.

Lookup

Lookup(name) matches the canonical name or any alias, case-insensitively. Aliases cover common variants (max length, max-length, maxLen, maximum length, … all match KwMaxLength). The lexer applies first-character case folding before lookup; alias matching is fully case-insensitive.

Keywords() returns a defensive copy of the authoritative table for tooling that needs to enumerate it.

Multi-line raw-block keywords

KwConsumes, KwProduces, KwSecurity, KwSecurityDefinitions, KwResponses, KwParameters, KwExtensions, KwInfoExtensions, KwTOS, KwExternalDocs are all ShapeRawBlock. Their bodies travel through the lexer's body accumulator and surface on the Block as raw Properties; downstream sub-parsers (yaml, routebody, security) consume the body content.

in: is a parameter-location directive

KwIn is declared as ShapeEnumOption("query", "path", "header", "body", "formData") in CtxParam. It is not part of the formal schema-body grammar; the keyword table recognises it so the lexer can hand a typed token to the parameters dispatch path. The schema parser treats it as a context-invalid warning when seen outside that path.

Schemes: accepts both inline and multi-line

KwSchemes uses ShapeRawBlock so multi-line bodies (Schemes:\n - http\n - https) populate the same way they do for Consumes/Produces. The inline comma-list form (Schemes: http, https) still works via the inline-value capture in collectRawBlock (§raw-block-terminators). Block.GetList unifies both surfaces.


§context-legality — per-annotation keyword legality

KeywordContext enumerates the family-level contexts where a keyword may appear: CtxParam, CtxHeader, CtxSchema, CtxItems, CtxRoute, CtxOperation, CtxMeta, CtxResponse. Each Keyword.Contexts lists the contexts the keyword is legal in.

parser.allowedContexts(kind) maps each AnnotationKind to the context set legal under it:

AnnotationKind Allowed contexts
AnnModel CtxSchema, CtxItems
AnnParameters CtxParam, CtxSchema, CtxItems
AnnResponse CtxResponse, CtxSchema, CtxHeader, CtxItems
AnnOperation CtxOperation, CtxParam, CtxSchema, CtxHeader, CtxItems, CtxResponse
AnnRoute CtxRoute, CtxParam, CtxSchema, CtxHeader, CtxItems, CtxResponse
AnnMeta CtxMeta, CtxSchema
Classifier kinds & AnnUnknown nil (no parser-layer policy)

contextLegal(kw, kind) returns true when the keyword's contexts overlap with the kind's allowed contexts. A missing overlap is a CodeContextInvalid warning — the property is still recorded so the builder can decide policy.


§annotation-args — argument terminals

Per-annotation argument shapes are classified by classifyAnnotationArgs and emitted as typed Tokens on TokenAnnotation.Args:

Kind Argument tokens
AnnRoute, AnnOperation TokenHTTPMethod + TokenURLPath + TokenIdentName* (tags + trailing OpID)
AnnDefaultName one TokenJSONValue or TokenRawValue per classifyDefaultValue
AnnType one TokenTypeRef (or fallback TokenIdentName) per isTypeRef
AnnEnum per classifyEnumArgsTokenIdentName (name) + TokenJSONValue / TokenCommaListValue (values), in source order
AnnParameters TokenIdentName* (operation IDs)
AnnAllOf, AnnModel, AnnResponse, AnnStrfmt, AnnName one TokenIdentName (first identifier only — single-word capture)
AnnAlias, AnnIgnore, AnnFile, AnnMeta, AnnUnknown trailing fields as TokenIdentName* so the parser can diagnose
Operation arg extraction

parseOperationArgs extracts METHOD, /path, [tags…], OperationID. The trailing TokenIdentName is the OpID; any preceding TokenIdentNames are tags. Missing or invalid pieces emit CodeMalformedOperation.

Schema-family arg validation
  • AnnParameters requires at least one IDENT_NAME (operation id) — empty emits CodeMissingRequiredArg.
  • AnnName requires a single IDENT_NAME — empty emits CodeMissingRequiredArg.
Classifier-family arg validation
  • AnnStrfmt requires a name; empty emits CodeMissingRequiredArg.
  • AnnDefaultName requires a value; missing emits CodeMissingRequiredArg.
  • AnnType requires a TokenTypeRef; a non-closed-vocab value emits CodeInvalidTypeRef.
  • AnnEnum requires a name and/or value list and/or a body; empty across all three emits CodeMissingRequiredArg.
  • AnnAllOf, AnnIgnore, AnnAlias, AnnFile accept optional / no args.

§typed-extensions — extensions: body → typed map

collectExtensionsFromBody parses the body of an extensions: or infoExtensions: raw block through yaml.TypedExtensions and registers one Extension per top-level x-* entry, carrying its YAML-typed value (bool / float64 / string / []any / map[string]any).

Extension.Source carries the keyword that produced the entry: KwExtensions (top-level vendor extensions) vs KwInfoExtensions (Info-scoped, meta-only). Consumers that route entries to different targets — meta's swspec.Extensions vs swspec.Info.Extensions — switch on this field; consumers that treat extensions uniformly (routes, operations) can ignore it.

Drop policy
  • Non-x-* keys are dropped with a CodeInvalidAnnotation warning, so authors who typo a vendor-extension key (e.g. invalid-key: under Extensions:) get a signal rather than silent loss.
  • A YAML parse failure emits a CodeInvalidYAMLExtensions warning and the block is skipped (no Extension entries registered).
Position fidelity

Every Extension currently shares the extensions: keyword's position. Per-entry positions require *yaml.Node walking and can be added when LSP-grade diagnostics need them — see the YAML sub-parser package.

isExtensionName

A well-formed extension name starts with x- or X-, length ≥ 3. The check is local to this package; the JSON encoder layer applies its own validation.


§security-requirements — typed Requirements

A security: raw block in a meta / route / operation context is parsed at lex time into a []security.Requirement and made available via Block.SecurityRequirements(). Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on spec.Operation.Security.

parser.emitRawBlock calls security.Parse(body) when the keyword name is KwSecurity. Returns nil when no security: keyword appeared on the block.

The companion accessor — Block.Contact() / Block.License() — exposes the typed shapes parsed from inline contact: / license: values (§contact-license).


§contact-license — typed Contact / License

Contact is the typed shape of a contact: inline value on a swagger:meta block:

contact: <Name> <email> <URL>

Each part is optional in the order written: parseContact recognises a Name <email> head (Go's net/mail.ParseAddress form) followed by an optional URL. A bare email without a name is accepted. Empty / unrecognised input returns (Contact{}, nil). A malformed Name <email> head returns (Contact{}, err) — the caller decides whether to fail or warn.

License is the typed shape of a license: inline value:

license: <Name> <URL>

parseLicense splits on the URL prefix; Name may be empty when the line starts with the URL. Empty input returns (License{}, false).

splitURL recognises the leading URL prefix from a closed set: https://, http://, ftps://, ftp://, wss://, ws://.


§diagnostics — Code / Severity model

Diagnostic is one observation about a comment block:

  • Pos — source position;
  • SeveritySeverityError, SeverityWarning, or SeverityHint;
  • Code — stable identifier (parse.invalid-number, validate.shape-mismatch, …);
  • Message — human-readable text.

Errorf / Warnf / Hintf are convenience constructors. Diagnostic.String() renders compiler-style one-line form.

Code prefixes
  • parse.* — lexer / parser-level observations emitted by the grammar package itself.
  • validate.* — semantic-validation observations emitted by the builder layer (typically through internal/builders/validations).
Parser never aborts

The parser emits diagnostics and continues. Callers (analyzers, LSP, the CLI) decide policy by severity. The parser layer never returns an error to the consumer; diagnostics are observable on the returned Block (Block.Diagnostics()) and via the diagnostic-sink option (WithDiagnosticSink) for streaming.

Defined codes
Code Description
CodeInvalidNumber malformed number value
CodeInvalidInteger malformed integer value
CodeInvalidBoolean not true/false
CodeInvalidEnumOption not in the closed set
CodeContextInvalid keyword not legal under the current annotation
CodeInvalidExtension malformed extension name
CodeInvalidYAMLExtensions YAML parse failure on extensions body
CodeUnterminatedYAML --- opened, not closed
CodeInvalidAnnotation malformed annotation surface
CodeInvalidTypeRef not in the closed type-reference vocab
CodeUnexpectedToken stray token at body level
CodeMalformedOperation missing/invalid HTTP method / path / OpID
CodeMissingRequiredArg annotation requires an argument
CodeShapeMismatch builder-layer keyword vs schema-type mismatch
CodeAmbiguousEmbed builder-layer embed disambiguation diagnostic
CodeUnsupportedInSimpleSchema builder-layer SimpleSchema-exit violation

§synthetic-block — sub-parser construction

NewSyntheticBlock(pos, title, description, props) builds a Block from a manually-curated set of Properties. Used by sub-parsers (routebody, future input modes) that lower a non-grammar text surface into the standard Block shape so consumers can dispatch through the usual Walker.

title and description become the Block's Title() / Description(), also surfaced via Prose() with internal blank separation. pos is the source position of the synthetic block's head — Properties that lack their own Pos inherit it implicitly when consumers build diagnostics.

The returned Block exposes empty Diagnostics(), AnnotationKind() == AnnUnknown, no YAML blocks, no extensions, and no security requirements. AnnotationArg() returns ("", false). Walk fires Title / Description first when non-empty, then properties in slice order — the regular Walker contract.


§quirks-open — deferred follow-ups

Body-shape choices retained as-is

Body is a single string with embedded \n; Raw carries verbatim source indentation. Consumers that prefer a []string shape call strings.Split(body, "\n") themselves. Switching to []string at the Property level would force every consumer to re-join; the single-string form pays the cost where it is needed.

Position fidelity on multi-entry bodies

Extensions and security requirements share the parent keyword's position. Per-entry positions require walking the *yaml.Node tree from the YAML parser; LSP-grade diagnostics may want this in a later pass.

Closed-vocab annotation prefix

AnnotationPrefix is fixed at "swagger:". A configurable prefix would interact with the first-character case-insensitive fallback (which is tied to ASCII letter casing). A non-letter prefix character would not need the fallback. No current consumer asks for this; the constant is the minimal scaffolding for a future Option promotion.

Documentation

Overview

Package grammar is the annotation parser for codescan. It consumes one Go comment group at a time, recognises the swagger:<name> annotation header, and produces a typed Block carrying:

  • the recognised annotation as an AnnotationKind;
  • per-Block fields for the annotation's positional arguments;
  • Property entries for every recognised body keyword;
  • prose lines split into Title() / Description();
  • diagnostics for malformed inputs (the parser never aborts).

Pipeline:

*ast.CommentGroup
     │
     ▼
  Preprocess  → []Line       (comment-marker stripping)
     │
     ▼
     Lex      → []Token      (line classifier + body accumulator + prose classifier)
     │
     ▼
    Parse     → Block        (dispatch by annotation family)

The Token vocabulary is defined in token.go.

Details

See README.md in this package for the full contract: pipeline stages, lexer / parser rules, keyword table, walker dispatch table, body-termination rules, diagnostics codes, and known follow-ups.

Index

Constants

View Source
const (
	KwMaximum             = "maximum"
	KwMinimum             = "minimum"
	KwMultipleOf          = "multipleOf"
	KwMaxLength           = "maxLength"
	KwMinLength           = "minLength"
	KwPattern             = "pattern"
	KwMaxItems            = "maxItems"
	KwMinItems            = "minItems"
	KwUnique              = "unique"
	KwCollectionFormat    = "collectionFormat"
	KwDefault             = "default"
	KwExample             = "example"
	KwEnum                = "enum"
	KwRequired            = "required"
	KwReadOnly            = "readOnly"
	KwDiscriminator       = "discriminator"
	KwDeprecated          = "deprecated"
	KwIn                  = "in"
	KwSchemes             = "schemes"
	KwVersion             = "version"
	KwHost                = "host"
	KwBasePath            = "basePath"
	KwLicense             = "license"
	KwContact             = "contact"
	KwConsumes            = "consumes"
	KwProduces            = "produces"
	KwSecurity            = "security"
	KwSecurityDefinitions = "securityDefinitions"
	KwResponses           = "responses"
	KwParameters          = "parameters"
	KwExtensions          = "extensions"
	KwInfoExtensions      = "infoExtensions"
	KwTOS                 = "tos"
	KwExternalDocs        = "externalDocs"
)

Canonical keyword names. These constants are the single source of truth for spelling: every Property's Keyword.Name compares equal to exactly one of them. Consumers that switch on Keyword.Name should reference these constants rather than re-declaring the strings — schema/walker.go and the bridge dispatchers in routes/parameters/ responses/operations/items/spec all dispatch on these names.

Sections (numeric validations / length validations / schema decorators / boolean markers / param-location / meta single-line / raw-block) follow the same order as the keywords table below.

View Source
const AllDepths = -1

AllDepths is the FilterDepth sentinel meaning "fire property callbacks regardless of ItemsDepth". Use it explicitly rather than -1 so the intent reads at the call site.

View Source
const AnnotationPrefix = "swagger:"

AnnotationPrefix is the literal that introduces every codescan annotation header. Centralised so callers and tests reference the single source of truth rather than the bare literal.

Variables

This section is empty.

Functions

func FormatToken

func FormatToken(t Token) string

FormatToken renders a token compactly for diagnostics and tests. Avoids leaking internal kinds in production output.

func NormalizeIn

func NormalizeIn(raw string, allowFormAlias bool) (string, bool)

NormalizeIn returns the canonical OAS v2 parameter-location value matching raw, case-insensitively, against the closed vocabulary declared on KwIn (`query` / `path` / `header` / `body` / `formData`). Returns ("", false) when raw is not recognised.

When allowFormAlias is true, the routes-inline-param affordance from v1 is enabled: a raw value of `form` (case-insensitive) is accepted and normalised to `formData`. This is documented in observed-quirks Q27 and is intentionally contained to internal/parsers/routebody — every other capture site MUST pass allowFormAlias=false so the canonical OAS v2 vocabulary is the single source of truth.

The normalisation here mirrors what the grammar's enum-option parser does for typed KwIn properties (parser.go:740, strings.EqualFold). It exists as a public helper because three capture sites read `in:` by scanning doc text directly rather than going through grammar's typed property path:

  • internal/builders/parameters/doc_signals.go (scanInLocation)
  • internal/builders/responses/doc_signals.go (scanInLocation)
  • internal/parsers/routebody/parameters.go (applyParamLine)

All three route through this helper so case-insensitivity is enforced uniformly and the closed vocabulary lives in one place.

Q29 (2026-06-03) — go-swagger-generated code emits capitalised forms like `in: Body`; the pre-fix strict-case map lookup silently miscategorised them, dropping fields to the `query` default.

Types

type AnnotationKind

type AnnotationKind int

AnnotationKind identifies the top-level swagger:<name> directive.

const (
	AnnUnknown AnnotationKind = iota

	AnnModel       // swagger:model
	AnnResponse    // swagger:response
	AnnParameters  // swagger:parameters
	AnnRoute       // swagger:route
	AnnOperation   // swagger:operation
	AnnMeta        // swagger:meta
	AnnStrfmt      // swagger:strfmt
	AnnAlias       // swagger:alias
	AnnName        // swagger:name
	AnnAllOf       // swagger:allOf
	AnnEnum        // swagger:enum
	AnnIgnore      // swagger:ignore
	AnnDefaultName // swagger:default — value-only classifier annotation
	AnnType        // swagger:type
	AnnFile        // swagger:file
)

func AnnotationKindFromName

func AnnotationKindFromName(name string) AnnotationKind

AnnotationKindFromName resolves the swagger:<name> label to its kind. Returns AnnUnknown for labels outside the recognised set.

func (AnnotationKind) String

func (a AnnotationKind) String() string

String renders an AnnotationKind as its source label.

type Block

type Block interface {
	Pos() token.Position
	Title() string
	Description() string
	Diagnostics() []Diagnostic
	AnnotationKind() AnnotationKind

	Properties() iter.Seq[Property]
	YAMLBlocks() iter.Seq[RawYAML]
	Extensions() iter.Seq[Extension]
	SecurityRequirements() []security.Requirement
	Contact() (Contact, error)
	License() (License, bool)

	// Walk dispatches properties / prose / extensions / diagnostics
	// through the callbacks set on w. See walker.go for the contract.
	Walk(w Walker)

	// ProseLines returns the cleaned prose lines (TITLE + DESC) in
	// source order, with blank lines preserved as empty strings.
	ProseLines() []string

	// PreambleLines returns prose lines that appear BEFORE the block's
	// annotation. For UnboundBlock (no annotation), returns the same as
	// ProseLines. Schema and meta builders consume only pre-annotation
	// prose for title/description; routes/operations consult ProseLines()
	// and observe post-annotation text too.
	PreambleLines() []string

	// PreambleTitle / PreambleDescription return the
	// title/description computed from PreambleLines (pre-annotation
	// prose only). Schema's top-level model builder uses these so
	// post-annotation prose reads as body content rather than
	// title/description.
	PreambleTitle() string
	PreambleDescription() string

	// Prose returns the entire prose surface (TITLE + DESC tokens
	// in source order) joined with "\n", with internal blanks
	// preserved as paragraph breaks and a single trailing blank
	// dropped.
	//
	// Used by field-level callers (struct field / interface method
	// docs) where the entire prose is the description and there's
	// no separate title concept.
	Prose() string

	Has(name string) bool
	GetFloat(name string) (float64, bool)
	GetInt(name string) (int64, bool)
	GetBool(name string) (bool, bool)
	GetString(name string) (string, bool)
	GetList(name string) ([]string, bool)

	// AnnotationArg returns the first positional identifier argument
	// of the block's primary annotation (e.g. "Pet" for
	// `swagger:model Pet`, "date-time" for `swagger:strfmt
	// date-time`, the IDENT_NAME for `swagger:name fooBar`).
	// Returns ("", false) when the annotation has no arg or carries
	// only an empty/whitespace one. Bare annotations (e.g.
	// `swagger:model` without a name) return ("", false); callers
	// distinguish "annotation present" via AnnotationKind().
	//
	// Convergence accessor — replaces type-asserting on each typed
	// Block kind to read its Name / Args[0] field. Used by Walker
	// callbacks that don't care which classifier flavour they're
	// looking at, only what its IDENT_NAME-style argument is.
	// See README §block-shapes.
	AnnotationArg() (string, bool)
}

Block is the interface every typed AST node implements. One Block corresponds to one Go comment group's parsed content.

The typed Block hierarchy matches the family productions:

  • SchemaBlock variants: ModelBlock, ResponseBlock, ParametersBlock, NameBlock
  • OperationFamilyBlock: RouteBlock, InlineOperationBlock
  • MetaBlock
  • ClassifierBlock variants for the classifier annotations
  • UnboundBlock for the no-annotation case

Details

See README §parser-contract and §block-shapes for the per-Block contracts.

func NewSyntheticBlock

func NewSyntheticBlock(pos token.Position, title, description string, props []Property) Block

NewSyntheticBlock builds a Block from a manually-curated set of properties. Used by sub-parsers (routebody, future input modes) that lower a non-grammar text surface into the standard Block shape so consumers can dispatch through the usual Walker.

title and description become the Block's Title()/Description(), also surfaced via Prose() with internal blank separation. pos is the source position of the synthetic block's head — Properties that lack their own Pos inherit it implicitly when consumers build diagnostics.

The returned Block exposes empty Diagnostics(), AnnotationKind() == AnnUnknown, no YAML blocks, no extensions, and no security requirements. AnnotationArg() returns ("", false). Walk fires Title/Description first when non-empty, then properties in slice order — the regular Walker contract. See README §synthetic-block.

func Parse

func Parse(cg *ast.CommentGroup, fset *token.FileSet) Block

Parse is the convenience wrapper around NewParser(fset).Parse(cg).

func ParseAll

func ParseAll(cg *ast.CommentGroup, fset *token.FileSet) []Block

ParseAll is the convenience wrapper around NewParser(fset).ParseAll(cg).

func ParseTokens

func ParseTokens(tokens []Token) Block

ParseTokens runs the parser on a pre-lexed token stream. Useful for tests and LSP scenarios.

type ClassifierBlock

type ClassifierBlock struct {
	Args []Token
	// contains filtered or unexported fields
}

ClassifierBlock covers single-line classifier annotations: strfmt / allOf / ignore / alias / file / type / default. The Args slice carries the lexer-tokenised positional arguments.

func (*ClassifierBlock) AnnotationArg

func (b *ClassifierBlock) AnnotationArg() (string, bool)

AnnotationArg returns the first positional argument's text when non-empty (single-word capture). Bare classifier annotations (e.g. `swagger:ignore`, `swagger:alias`) return ("", false).

func (ClassifierBlock) AnnotationKind

func (b ClassifierBlock) AnnotationKind() AnnotationKind

func (ClassifierBlock) Contact

func (b ClassifierBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (ClassifierBlock) Description

func (b ClassifierBlock) Description() string

func (ClassifierBlock) Diagnostics

func (b ClassifierBlock) Diagnostics() []Diagnostic

func (ClassifierBlock) Extensions

func (b ClassifierBlock) Extensions() iter.Seq[Extension]

func (ClassifierBlock) GetBool

func (b ClassifierBlock) GetBool(name string) (bool, bool)

func (ClassifierBlock) GetFloat

func (b ClassifierBlock) GetFloat(name string) (float64, bool)

func (ClassifierBlock) GetInt

func (b ClassifierBlock) GetInt(name string) (int64, bool)

func (ClassifierBlock) GetList

func (b ClassifierBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (ClassifierBlock) GetString

func (b ClassifierBlock) GetString(name string) (string, bool)

func (ClassifierBlock) Has

func (b ClassifierBlock) Has(name string) bool

func (ClassifierBlock) License

func (b ClassifierBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (ClassifierBlock) Pos

func (b ClassifierBlock) Pos() token.Position

func (ClassifierBlock) PreambleDescription

func (b ClassifierBlock) PreambleDescription() string

func (ClassifierBlock) PreambleLines

func (b ClassifierBlock) PreambleLines() []string

func (ClassifierBlock) PreambleTitle

func (b ClassifierBlock) PreambleTitle() string

func (ClassifierBlock) Properties

func (b ClassifierBlock) Properties() iter.Seq[Property]

func (ClassifierBlock) Prose

func (b ClassifierBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (ClassifierBlock) ProseLines

func (b ClassifierBlock) ProseLines() []string

func (ClassifierBlock) SecurityRequirements

func (b ClassifierBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (ClassifierBlock) Title

func (b ClassifierBlock) Title() string

func (ClassifierBlock) Walk

func (b ClassifierBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (ClassifierBlock) YAMLBlocks

func (b ClassifierBlock) YAMLBlocks() iter.Seq[RawYAML]

type Code

type Code string

Code is a stable identifier for a class of Diagnostic.

const (
	CodeInvalidNumber     Code = "parse.invalid-number"
	CodeInvalidInteger    Code = "parse.invalid-integer"
	CodeInvalidBoolean    Code = "parse.invalid-boolean"
	CodeInvalidEnumOption Code = "parse.invalid-enum-option"
	CodeContextInvalid    Code = "parse.context-invalid"
	CodeInvalidExtension  Code = "parse.invalid-extension-name"
	// CodeInvalidYAMLExtensions fires when the body of an
	// `extensions:` raw block fails YAML parsing. The block is
	// skipped (no Extension entries emitted) and a warning is raised.
	CodeInvalidYAMLExtensions Code = "parse.invalid-yaml-extensions"
	CodeUnterminatedYAML      Code = "parse.unterminated-yaml"
	CodeInvalidAnnotation     Code = "parse.invalid-annotation"
	CodeInvalidTypeRef        Code = "parse.invalid-type-ref"
	CodeUnexpectedToken       Code = "parse.unexpected-token"
	CodeMalformedOperation    Code = "parse.malformed-operation"
	CodeMissingRequiredArg    Code = "parse.missing-required-arg"

	// CodeShapeMismatch fires when a keyword is applied to a schema
	// whose resolved Swagger type doesn't match the keyword's domain
	// (e.g. `pattern: ^a$` on an integer field). Emitted by
	// internal/builders/validations.IsLegalForType callers.
	CodeShapeMismatch Code = "validate.shape-mismatch"

	// CodeAmbiguousEmbed fires when two embedded types of a parent
	// struct (or struct embed-chains at the same depth) both promote
	// a property with the same JSON name but different Go names. Go's
	// own rule is to not promote such ambiguous fields; codescan
	// currently emits a last-write-wins schema regardless. The
	// diagnostic surfaces the case so authors can disambiguate.
	CodeAmbiguousEmbed Code = "validate.ambiguous-embed"

	// CodeUnsupportedInSimpleSchema fires when the schema builder
	// running in SimpleSchema mode produces an outcome that OAS v2
	// does not allow on a parameter/header (object type, $ref, allOf,
	// properties, …). The diagnostic is emitted at exit and the
	// target is reset to empty `{}` — honest over lossy. Reaching
	// this code path typically means a non-body parameter or response
	// header was typed as a struct or interface that the recognizer
	// cascade couldn't reduce to a primitive.
	CodeUnsupportedInSimpleSchema Code = "validate.unsupported-in-simple-schema"
)

Diagnostic codes. The `parse.*` prefix marks lexer/parser-level observations; `validate.*` marks semantic-validation observations emitted by the builder layer (typically through the internal/builders/validations package).

type Contact

type Contact struct {
	Name, Email, URL string
}

Contact is the typed shape of a `contact:` inline value on a swagger:meta block. The convention is:

contact: <Name> <email> <URL>

where each part is optional in the order written: the parser recognises a `Name <email>` head (Go's net/mail.ParseAddress form) followed by an optional URL. A bare email without a name is also accepted. Empty or unrecognised inputs return (Contact{}, false) from Block.Contact().

type DefaultParser

type DefaultParser struct {
	// contains filtered or unexported fields
}

DefaultParser is the concrete Parser implementation.

func NewParser

func NewParser(fset *token.FileSet, opts ...Option) *DefaultParser

NewParser constructs a DefaultParser bound to a FileSet (needed to map *ast.CommentGroup positions to absolute source positions).

func (*DefaultParser) Parse

func (p *DefaultParser) Parse(cg *ast.CommentGroup) Block

func (*DefaultParser) ParseAll

func (p *DefaultParser) ParseAll(cg *ast.CommentGroup) []Block

ParseAll returns one Block per annotation in cg, in source order. A comment group with no annotation yields a single-element slice holding an UnboundBlock; a single-annotation comment yields the same Block as Parse, wrapped in a slice; multi-annotation comments yield one Block per annotation.

Token partition: each annotation owns the slice of tokens from its index up to (but excluding) the next annotation. The first annotation also owns the pre-annotation prose, so its PreambleTitle / PreambleDescription match Parse(cg)'s. Body tokens between annotations attach to the *preceding* annotation.

Multi-annotation comments like

// swagger:model
// swagger:strfmt date-time

pair a schema-family annotation with a classifier; the schema builder's Walker dispatches on each Block's AnnotationKind() without further partitioning.

func (*DefaultParser) ParseAs

func (p *DefaultParser) ParseAs(kind AnnotationKind, text string, pos token.Position) Block

func (*DefaultParser) ParseText

func (p *DefaultParser) ParseText(text string, pos token.Position) Block

type Diagnostic

type Diagnostic struct {
	Pos      token.Position
	Severity Severity
	Code     Code
	Message  string
}

Diagnostic is one observation about a comment block.

func Errorf

func Errorf(pos token.Position, code Code, format string, args ...any) Diagnostic

Errorf builds a SeverityError Diagnostic with a formatted message.

func Hintf

func Hintf(pos token.Position, code Code, format string, args ...any) Diagnostic

Hintf builds a SeverityHint Diagnostic.

func Warnf

func Warnf(pos token.Position, code Code, format string, args ...any) Diagnostic

Warnf builds a SeverityWarning Diagnostic.

func (Diagnostic) String

func (d Diagnostic) String() string

String renders a Diagnostic in compiler-style one-line form.

type EnumDeclBlock

type EnumDeclBlock struct {
	Name       string // "" when absent
	InlineForm enumArgsForm
	InlineArgs []Token // values fragment as a typed token, when inline values were given
	BodyValues string  // populated when a multi-line RAW_VALUE_ENUM body was present
	// contains filtered or unexported fields
}

EnumDeclBlock is produced by `swagger:enum [name] [values…]`. The optional multi-line value-list is carried in BodyValues when present (lexer accumulates the body as RAW_VALUE_ENUM).

func (*EnumDeclBlock) AnnotationArg

func (b *EnumDeclBlock) AnnotationArg() (string, bool)

AnnotationArg returns the IDENT_NAME arg (`status` for `swagger:enum status`) or ("", false) when the enum was declared inline-only (`swagger:enum red green blue`) or as a pure body block.

func (EnumDeclBlock) AnnotationKind

func (b EnumDeclBlock) AnnotationKind() AnnotationKind

func (EnumDeclBlock) Contact

func (b EnumDeclBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (EnumDeclBlock) Description

func (b EnumDeclBlock) Description() string

func (EnumDeclBlock) Diagnostics

func (b EnumDeclBlock) Diagnostics() []Diagnostic

func (EnumDeclBlock) Extensions

func (b EnumDeclBlock) Extensions() iter.Seq[Extension]

func (EnumDeclBlock) GetBool

func (b EnumDeclBlock) GetBool(name string) (bool, bool)

func (EnumDeclBlock) GetFloat

func (b EnumDeclBlock) GetFloat(name string) (float64, bool)

func (EnumDeclBlock) GetInt

func (b EnumDeclBlock) GetInt(name string) (int64, bool)

func (EnumDeclBlock) GetList

func (b EnumDeclBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (EnumDeclBlock) GetString

func (b EnumDeclBlock) GetString(name string) (string, bool)

func (EnumDeclBlock) Has

func (b EnumDeclBlock) Has(name string) bool

func (EnumDeclBlock) License

func (b EnumDeclBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (EnumDeclBlock) Pos

func (b EnumDeclBlock) Pos() token.Position

func (EnumDeclBlock) PreambleDescription

func (b EnumDeclBlock) PreambleDescription() string

func (EnumDeclBlock) PreambleLines

func (b EnumDeclBlock) PreambleLines() []string

func (EnumDeclBlock) PreambleTitle

func (b EnumDeclBlock) PreambleTitle() string

func (EnumDeclBlock) Properties

func (b EnumDeclBlock) Properties() iter.Seq[Property]

func (EnumDeclBlock) Prose

func (b EnumDeclBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (EnumDeclBlock) ProseLines

func (b EnumDeclBlock) ProseLines() []string

func (EnumDeclBlock) SecurityRequirements

func (b EnumDeclBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (EnumDeclBlock) Title

func (b EnumDeclBlock) Title() string

func (EnumDeclBlock) Walk

func (b EnumDeclBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (EnumDeclBlock) YAMLBlocks

func (b EnumDeclBlock) YAMLBlocks() iter.Seq[RawYAML]

type Extension

type Extension struct {
	Name   string
	Source string
	Pos    token.Position
	Value  any
}

Extension is one x-* vendor entry under an extensions: block.

Value carries the YAML-typed nested value (`bool` / `float64` / `string` / `[]any` / `map[string]any`). The parser pipes the body through `internal/parsers/yaml.TypedExtensions`, so consumers can rely on JSON-normalised types — never `map[any]any`.

Source carries the keyword that produced the entry: KwExtensions for `extensions:` blocks (top-level vendor extensions) or KwInfoExtensions for `infoExtensions:` blocks (Info-scoped vendor extensions, meta-only). Consumers that need to route entries to different targets — meta's swspec.Extensions vs swspec.Info.Extensions — switch on this field; consumers that treat extensions uniformly (routes / operations) can ignore it.

type InlineOperationBlock

type InlineOperationBlock struct {
	Method string
	Path   string
	Tags   []string
	OpID   string
	// contains filtered or unexported fields
}

InlineOperationBlock is produced by `swagger:operation METHOD /path [tags] opID`. Body may carry an OPAQUE_YAML in addition to inline keyword raw-blocks.

func (InlineOperationBlock) AnnotationArg

func (b InlineOperationBlock) AnnotationArg() (string, bool)

AnnotationArg default — Block kinds whose annotation takes no identifier argument (UnboundBlock, MetaBlock, RouteBlock, InlineOperationBlock, ParametersBlock — the latter has multiple args but conceptually it's a list, not a single IDENT_NAME). Typed kinds with a single IDENT_NAME override this method.

func (InlineOperationBlock) AnnotationKind

func (b InlineOperationBlock) AnnotationKind() AnnotationKind

func (InlineOperationBlock) Contact

func (b InlineOperationBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (InlineOperationBlock) Description

func (b InlineOperationBlock) Description() string

func (InlineOperationBlock) Diagnostics

func (b InlineOperationBlock) Diagnostics() []Diagnostic

func (InlineOperationBlock) Extensions

func (b InlineOperationBlock) Extensions() iter.Seq[Extension]

func (InlineOperationBlock) GetBool

func (b InlineOperationBlock) GetBool(name string) (bool, bool)

func (InlineOperationBlock) GetFloat

func (b InlineOperationBlock) GetFloat(name string) (float64, bool)

func (InlineOperationBlock) GetInt

func (b InlineOperationBlock) GetInt(name string) (int64, bool)

func (InlineOperationBlock) GetList

func (b InlineOperationBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (InlineOperationBlock) GetString

func (b InlineOperationBlock) GetString(name string) (string, bool)

func (InlineOperationBlock) Has

func (b InlineOperationBlock) Has(name string) bool

func (InlineOperationBlock) License

func (b InlineOperationBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (InlineOperationBlock) Pos

func (b InlineOperationBlock) Pos() token.Position

func (InlineOperationBlock) PreambleDescription

func (b InlineOperationBlock) PreambleDescription() string

func (InlineOperationBlock) PreambleLines

func (b InlineOperationBlock) PreambleLines() []string

func (InlineOperationBlock) PreambleTitle

func (b InlineOperationBlock) PreambleTitle() string

func (InlineOperationBlock) Properties

func (b InlineOperationBlock) Properties() iter.Seq[Property]

func (InlineOperationBlock) Prose

func (b InlineOperationBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (InlineOperationBlock) ProseLines

func (b InlineOperationBlock) ProseLines() []string

func (InlineOperationBlock) SecurityRequirements

func (b InlineOperationBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (InlineOperationBlock) Title

func (b InlineOperationBlock) Title() string

func (InlineOperationBlock) Walk

func (b InlineOperationBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (InlineOperationBlock) YAMLBlocks

func (b InlineOperationBlock) YAMLBlocks() iter.Seq[RawYAML]

type Keyword

type Keyword struct {
	Name    string
	Aliases []string
	Shape   ValueShape
	Values  []string // populated when Shape == ShapeEnumOption
	// Contexts is the set of family contexts the keyword is legal in.
	// Used by the parser layer for non-fatal context-invalid warnings.
	Contexts []KeywordContext
}

Keyword describes one recognisable keyword: <value> form.

func Keywords

func Keywords() []Keyword

Keywords returns a copy of the authoritative table.

func Lookup

func Lookup(name string) (Keyword, bool)

Lookup returns the Keyword matching name (canonical or alias), case-insensitive on the canonical/alias spellings. The second return value reports whether a match was found.

type KeywordContext

type KeywordContext int

KeywordContext is a family-level context where a keyword is legal. Distinct from AnnotationKind because validations are legal across several annotation kinds (model + parameters + response, etc.).

const (
	CtxParam KeywordContext = iota
	CtxHeader
	CtxSchema
	CtxItems
	CtxRoute
	CtxOperation
	CtxMeta
	CtxResponse
)

func (KeywordContext) String

func (c KeywordContext) String() string

String renders a KeywordContext for diagnostics.

type License

type License struct {
	Name, URL string
}

License is the typed shape of a `license:` inline value:

license: <Name> <URL>

where Name is everything before the URL prefix and URL is the scheme-anchored remainder. A line without a URL keeps Name and leaves URL empty. Empty input returns (License{}, false) from Block.License().

type Line

type Line struct {
	Text string
	Raw  string
	Pos  token.Position
}

Line is one preprocessed comment line ready for the lexer.

Text has the Go comment markers (// /* */) stripped along with the godoc continuation decoration (leading whitespace, asterisks, slashes, optional markdown table pipe). Used for keyword and annotation classification.

Raw is the same source line with only the comment marker removed — content whitespace, indentation, and list markers are preserved. Used by the body accumulator so YAML / nested-map indentation survives intact.

Pos points to the first character of Text in the source file.

func Preprocess

func Preprocess(cg *ast.CommentGroup, fset *token.FileSet) []Line

Preprocess turns a Go comment group into a position-tagged []Line. Returns nil for nil inputs. Pure: no syscalls, no side-effects.

Line endings are normalised before line splitting (\r\n → \n, lone \r → \n). See README §preprocess-contract.

type MetaBlock

type MetaBlock struct {
	// contains filtered or unexported fields
}

MetaBlock is produced by `swagger:meta`.

func (MetaBlock) AnnotationArg

func (b MetaBlock) AnnotationArg() (string, bool)

AnnotationArg default — Block kinds whose annotation takes no identifier argument (UnboundBlock, MetaBlock, RouteBlock, InlineOperationBlock, ParametersBlock — the latter has multiple args but conceptually it's a list, not a single IDENT_NAME). Typed kinds with a single IDENT_NAME override this method.

func (MetaBlock) AnnotationKind

func (b MetaBlock) AnnotationKind() AnnotationKind

func (MetaBlock) Contact

func (b MetaBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (MetaBlock) Description

func (b MetaBlock) Description() string

func (MetaBlock) Diagnostics

func (b MetaBlock) Diagnostics() []Diagnostic

func (MetaBlock) Extensions

func (b MetaBlock) Extensions() iter.Seq[Extension]

func (MetaBlock) GetBool

func (b MetaBlock) GetBool(name string) (bool, bool)

func (MetaBlock) GetFloat

func (b MetaBlock) GetFloat(name string) (float64, bool)

func (MetaBlock) GetInt

func (b MetaBlock) GetInt(name string) (int64, bool)

func (MetaBlock) GetList

func (b MetaBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (MetaBlock) GetString

func (b MetaBlock) GetString(name string) (string, bool)

func (MetaBlock) Has

func (b MetaBlock) Has(name string) bool

func (MetaBlock) License

func (b MetaBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (MetaBlock) Pos

func (b MetaBlock) Pos() token.Position

func (MetaBlock) PreambleDescription

func (b MetaBlock) PreambleDescription() string

func (MetaBlock) PreambleLines

func (b MetaBlock) PreambleLines() []string

func (MetaBlock) PreambleTitle

func (b MetaBlock) PreambleTitle() string

func (MetaBlock) Properties

func (b MetaBlock) Properties() iter.Seq[Property]

func (MetaBlock) Prose

func (b MetaBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (MetaBlock) ProseLines

func (b MetaBlock) ProseLines() []string

func (MetaBlock) SecurityRequirements

func (b MetaBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (MetaBlock) Title

func (b MetaBlock) Title() string

func (MetaBlock) Walk

func (b MetaBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (MetaBlock) YAMLBlocks

func (b MetaBlock) YAMLBlocks() iter.Seq[RawYAML]

type ModelBlock

type ModelBlock struct {
	Name string
	// contains filtered or unexported fields
}

ModelBlock is produced by `swagger:model [Name]`.

func (*ModelBlock) AnnotationArg

func (b *ModelBlock) AnnotationArg() (string, bool)

AnnotationArg returns the IDENT_NAME arg (`Pet` for `swagger:model Pet`) or ("", false) for a bare `swagger:model`.

func (ModelBlock) AnnotationKind

func (b ModelBlock) AnnotationKind() AnnotationKind

func (ModelBlock) Contact

func (b ModelBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (ModelBlock) Description

func (b ModelBlock) Description() string

func (ModelBlock) Diagnostics

func (b ModelBlock) Diagnostics() []Diagnostic

func (ModelBlock) Extensions

func (b ModelBlock) Extensions() iter.Seq[Extension]

func (ModelBlock) GetBool

func (b ModelBlock) GetBool(name string) (bool, bool)

func (ModelBlock) GetFloat

func (b ModelBlock) GetFloat(name string) (float64, bool)

func (ModelBlock) GetInt

func (b ModelBlock) GetInt(name string) (int64, bool)

func (ModelBlock) GetList

func (b ModelBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (ModelBlock) GetString

func (b ModelBlock) GetString(name string) (string, bool)

func (ModelBlock) Has

func (b ModelBlock) Has(name string) bool

func (ModelBlock) License

func (b ModelBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (ModelBlock) Pos

func (b ModelBlock) Pos() token.Position

func (ModelBlock) PreambleDescription

func (b ModelBlock) PreambleDescription() string

func (ModelBlock) PreambleLines

func (b ModelBlock) PreambleLines() []string

func (ModelBlock) PreambleTitle

func (b ModelBlock) PreambleTitle() string

func (ModelBlock) Properties

func (b ModelBlock) Properties() iter.Seq[Property]

func (ModelBlock) Prose

func (b ModelBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (ModelBlock) ProseLines

func (b ModelBlock) ProseLines() []string

func (ModelBlock) SecurityRequirements

func (b ModelBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (ModelBlock) Title

func (b ModelBlock) Title() string

func (ModelBlock) Walk

func (b ModelBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (ModelBlock) YAMLBlocks

func (b ModelBlock) YAMLBlocks() iter.Seq[RawYAML]

type NameBlock

type NameBlock struct {
	Name string
	// contains filtered or unexported fields
}

NameBlock is produced by `swagger:name <member-name>`. The annotation overrides a struct field's / interface method's JSON property name (or schema member name) without mutating the surrounding Go identifier. Name carries the IDENT_NAME argument.

func (*NameBlock) AnnotationArg

func (b *NameBlock) AnnotationArg() (string, bool)

AnnotationArg returns the IDENT_NAME arg. Bare `swagger:name` is rejected at parse time (CodeMissingRequiredArg); ("", false) only appears on the diagnostic path.

func (NameBlock) AnnotationKind

func (b NameBlock) AnnotationKind() AnnotationKind

func (NameBlock) Contact

func (b NameBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (NameBlock) Description

func (b NameBlock) Description() string

func (NameBlock) Diagnostics

func (b NameBlock) Diagnostics() []Diagnostic

func (NameBlock) Extensions

func (b NameBlock) Extensions() iter.Seq[Extension]

func (NameBlock) GetBool

func (b NameBlock) GetBool(name string) (bool, bool)

func (NameBlock) GetFloat

func (b NameBlock) GetFloat(name string) (float64, bool)

func (NameBlock) GetInt

func (b NameBlock) GetInt(name string) (int64, bool)

func (NameBlock) GetList

func (b NameBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (NameBlock) GetString

func (b NameBlock) GetString(name string) (string, bool)

func (NameBlock) Has

func (b NameBlock) Has(name string) bool

func (NameBlock) License

func (b NameBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (NameBlock) Pos

func (b NameBlock) Pos() token.Position

func (NameBlock) PreambleDescription

func (b NameBlock) PreambleDescription() string

func (NameBlock) PreambleLines

func (b NameBlock) PreambleLines() []string

func (NameBlock) PreambleTitle

func (b NameBlock) PreambleTitle() string

func (NameBlock) Properties

func (b NameBlock) Properties() iter.Seq[Property]

func (NameBlock) Prose

func (b NameBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (NameBlock) ProseLines

func (b NameBlock) ProseLines() []string

func (NameBlock) SecurityRequirements

func (b NameBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (NameBlock) Title

func (b NameBlock) Title() string

func (NameBlock) Walk

func (b NameBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (NameBlock) YAMLBlocks

func (b NameBlock) YAMLBlocks() iter.Seq[RawYAML]

type Option

type Option func(*DefaultParser)

Option configures a DefaultParser.

func WithDiagnosticSink

func WithDiagnosticSink(sink func(Diagnostic)) Option

WithDiagnosticSink streams diagnostics to a callback in addition to accumulating them on the returned Block.

type ParametersBlock

type ParametersBlock struct {
	OperationIDs []string
	// contains filtered or unexported fields
}

ParametersBlock is produced by `swagger:parameters T1 T2 …`.

func (ParametersBlock) AnnotationArg

func (b ParametersBlock) AnnotationArg() (string, bool)

AnnotationArg default — Block kinds whose annotation takes no identifier argument (UnboundBlock, MetaBlock, RouteBlock, InlineOperationBlock, ParametersBlock — the latter has multiple args but conceptually it's a list, not a single IDENT_NAME). Typed kinds with a single IDENT_NAME override this method.

func (ParametersBlock) AnnotationKind

func (b ParametersBlock) AnnotationKind() AnnotationKind

func (ParametersBlock) Contact

func (b ParametersBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (ParametersBlock) Description

func (b ParametersBlock) Description() string

func (ParametersBlock) Diagnostics

func (b ParametersBlock) Diagnostics() []Diagnostic

func (ParametersBlock) Extensions

func (b ParametersBlock) Extensions() iter.Seq[Extension]

func (ParametersBlock) GetBool

func (b ParametersBlock) GetBool(name string) (bool, bool)

func (ParametersBlock) GetFloat

func (b ParametersBlock) GetFloat(name string) (float64, bool)

func (ParametersBlock) GetInt

func (b ParametersBlock) GetInt(name string) (int64, bool)

func (ParametersBlock) GetList

func (b ParametersBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (ParametersBlock) GetString

func (b ParametersBlock) GetString(name string) (string, bool)

func (ParametersBlock) Has

func (b ParametersBlock) Has(name string) bool

func (ParametersBlock) License

func (b ParametersBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (ParametersBlock) Pos

func (b ParametersBlock) Pos() token.Position

func (ParametersBlock) PreambleDescription

func (b ParametersBlock) PreambleDescription() string

func (ParametersBlock) PreambleLines

func (b ParametersBlock) PreambleLines() []string

func (ParametersBlock) PreambleTitle

func (b ParametersBlock) PreambleTitle() string

func (ParametersBlock) Properties

func (b ParametersBlock) Properties() iter.Seq[Property]

func (ParametersBlock) Prose

func (b ParametersBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (ParametersBlock) ProseLines

func (b ParametersBlock) ProseLines() []string

func (ParametersBlock) SecurityRequirements

func (b ParametersBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (ParametersBlock) Title

func (b ParametersBlock) Title() string

func (ParametersBlock) Walk

func (b ParametersBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (ParametersBlock) YAMLBlocks

func (b ParametersBlock) YAMLBlocks() iter.Seq[RawYAML]

type Parser

type Parser interface {
	Parse(cg *ast.CommentGroup) Block
	ParseAll(cg *ast.CommentGroup) []Block
	ParseText(text string, pos token.Position) Block
	ParseAs(kind AnnotationKind, text string, pos token.Position) Block
}

Parser is the consumer contract for the grammar parser. The package ships *DefaultParser; the interface exists so tests can substitute a mock that fabricates Block values without running the full lex pipeline.

Details

See README §parser-contract for the family dispatch table and the body-token consumption rules.

type Property

type Property struct {
	Keyword    Keyword
	Pos        token.Position
	Value      string
	Body       string
	Raw        string
	Typed      TypedValue
	ItemsDepth int
}

Property is one keyword:value (or keyword body) attached to a Block.

For inline-value keywords (Number / Integer / Bool / String / EnumOption / CommaList shapes), Value is the raw string and Typed carries the lexically-typed form.

For body keywords (ShapeRawBlock / ShapeRawValue), Body holds the accumulated body content, Raw holds the verbatim source content, and Typed.Type indicates the body shape.

ItemsDepth records the leading items.* depth from the keyword head.

Details

See README §property-shape for the field-population matrix and the AsList unification rule.

func (Property) AsList

func (p Property) AsList() []string

AsList returns the token list represented by p, unifying every list-shaped surface form the grammar accepts (inline comma list, multi-line indented, YAML `- ` markers, or any combination).

Details

See README §property-shape (`AsList — unified list extraction`) for the surface forms, the algorithm, and the explicit non-targets (enum values, routebody Parameters chunks, raw YAML bodies).

func (Property) IsTyped

func (p Property) IsTyped() bool

IsTyped reports whether the property carries a primitive-typed value the caller can consume directly without further coercion.

True when Typed.Type is one of the primitive shapes — Number, Integer, Bool, EnumOption — meaning the matching Typed.* field is populated and authoritative. False otherwise:

  • ShapeNone — typing was not applied (ShapeString keywords like `pattern` keep the raw value in Property.Value), or typing failed and a diagnostic was emitted.
  • ShapeRawBlock / ShapeRawValue / ShapeCommaList / ShapeString — the value's interpretation depends on the resolved spec type (e.g. `default: 1.5` against a float-typed schema), so the consumer must coerce against schemaType+schemaFormat.

Canonical use site:

if p.IsTyped() {
    // read p.Typed.<field> matching p.Keyword.Shape
} else {
    // coerce p.Value against the resolved schema type
}

Replaces the explicit `switch p.Typed.Type` boilerplate consumers would otherwise need at every call site.

type RawYAML

type RawYAML struct {
	Pos       token.Position
	Text      string
	Truncated bool
}

RawYAML is one captured `--- … ---` body. The parser does not parse the YAML — it isolates the body so the analyzer can hand it to internal/parsers/yaml/.

type ResponseBlock

type ResponseBlock struct {
	Name string
	// contains filtered or unexported fields
}

ResponseBlock is produced by `swagger:response [Name]`.

func (*ResponseBlock) AnnotationArg

func (b *ResponseBlock) AnnotationArg() (string, bool)

AnnotationArg returns the IDENT_NAME arg or ("", false) for a bare `swagger:response`.

func (ResponseBlock) AnnotationKind

func (b ResponseBlock) AnnotationKind() AnnotationKind

func (ResponseBlock) Contact

func (b ResponseBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (ResponseBlock) Description

func (b ResponseBlock) Description() string

func (ResponseBlock) Diagnostics

func (b ResponseBlock) Diagnostics() []Diagnostic

func (ResponseBlock) Extensions

func (b ResponseBlock) Extensions() iter.Seq[Extension]

func (ResponseBlock) GetBool

func (b ResponseBlock) GetBool(name string) (bool, bool)

func (ResponseBlock) GetFloat

func (b ResponseBlock) GetFloat(name string) (float64, bool)

func (ResponseBlock) GetInt

func (b ResponseBlock) GetInt(name string) (int64, bool)

func (ResponseBlock) GetList

func (b ResponseBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (ResponseBlock) GetString

func (b ResponseBlock) GetString(name string) (string, bool)

func (ResponseBlock) Has

func (b ResponseBlock) Has(name string) bool

func (ResponseBlock) License

func (b ResponseBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (ResponseBlock) Pos

func (b ResponseBlock) Pos() token.Position

func (ResponseBlock) PreambleDescription

func (b ResponseBlock) PreambleDescription() string

func (ResponseBlock) PreambleLines

func (b ResponseBlock) PreambleLines() []string

func (ResponseBlock) PreambleTitle

func (b ResponseBlock) PreambleTitle() string

func (ResponseBlock) Properties

func (b ResponseBlock) Properties() iter.Seq[Property]

func (ResponseBlock) Prose

func (b ResponseBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (ResponseBlock) ProseLines

func (b ResponseBlock) ProseLines() []string

func (ResponseBlock) SecurityRequirements

func (b ResponseBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (ResponseBlock) Title

func (b ResponseBlock) Title() string

func (ResponseBlock) Walk

func (b ResponseBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (ResponseBlock) YAMLBlocks

func (b ResponseBlock) YAMLBlocks() iter.Seq[RawYAML]

type RouteBlock

type RouteBlock struct {
	Method string
	Path   string
	Tags   []string
	OpID   string
	// contains filtered or unexported fields
}

RouteBlock is produced by `swagger:route METHOD /path [tags] opID`. swagger:route's body is inline keyword raw-blocks and **never** an OPAQUE_YAML.

func (RouteBlock) AnnotationArg

func (b RouteBlock) AnnotationArg() (string, bool)

AnnotationArg default — Block kinds whose annotation takes no identifier argument (UnboundBlock, MetaBlock, RouteBlock, InlineOperationBlock, ParametersBlock — the latter has multiple args but conceptually it's a list, not a single IDENT_NAME). Typed kinds with a single IDENT_NAME override this method.

func (RouteBlock) AnnotationKind

func (b RouteBlock) AnnotationKind() AnnotationKind

func (RouteBlock) Contact

func (b RouteBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (RouteBlock) Description

func (b RouteBlock) Description() string

func (RouteBlock) Diagnostics

func (b RouteBlock) Diagnostics() []Diagnostic

func (RouteBlock) Extensions

func (b RouteBlock) Extensions() iter.Seq[Extension]

func (RouteBlock) GetBool

func (b RouteBlock) GetBool(name string) (bool, bool)

func (RouteBlock) GetFloat

func (b RouteBlock) GetFloat(name string) (float64, bool)

func (RouteBlock) GetInt

func (b RouteBlock) GetInt(name string) (int64, bool)

func (RouteBlock) GetList

func (b RouteBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (RouteBlock) GetString

func (b RouteBlock) GetString(name string) (string, bool)

func (RouteBlock) Has

func (b RouteBlock) Has(name string) bool

func (RouteBlock) License

func (b RouteBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (RouteBlock) Pos

func (b RouteBlock) Pos() token.Position

func (RouteBlock) PreambleDescription

func (b RouteBlock) PreambleDescription() string

func (RouteBlock) PreambleLines

func (b RouteBlock) PreambleLines() []string

func (RouteBlock) PreambleTitle

func (b RouteBlock) PreambleTitle() string

func (RouteBlock) Properties

func (b RouteBlock) Properties() iter.Seq[Property]

func (RouteBlock) Prose

func (b RouteBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (RouteBlock) ProseLines

func (b RouteBlock) ProseLines() []string

func (RouteBlock) SecurityRequirements

func (b RouteBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (RouteBlock) Title

func (b RouteBlock) Title() string

func (RouteBlock) Walk

func (b RouteBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (RouteBlock) YAMLBlocks

func (b RouteBlock) YAMLBlocks() iter.Seq[RawYAML]

type Severity

type Severity int

Severity classifies a Diagnostic's seriousness. The parser never aborts; callers (analyzers, LSP, the CLI) decide policy by severity. See README §diagnostics.

const (
	SeverityError Severity = iota
	SeverityWarning
	SeverityHint
)

func (Severity) String

func (s Severity) String() string

String renders a Severity for logs and CLI output.

type Token

type Token struct {
	Kind TokenKind
	Pos  token.Position

	Name       string  // for TokenAnnotation / TokenKeyword: canonical label
	SourceName string  // keyword: literal as written (alias / case preserved)
	Text       string  // payload for value tokens, prose lines, idents
	Args       []Token // for TokenAnnotation: positional argument tokens
	ItemsDepth int     // leading "items." depth (keyword tokens only)
	Keyword    string  // for body tokens: parent keyword name
	Body       string  // for body tokens: body content joined by "\n"
	Raw        string  // verbatim source content (indentation preserved)
	Truncated  bool    // body lexed without a closer (e.g. unmatched ---)
}

Token is one lexer-emitted item. Field population varies by kind:

  • TokenAnnotation: Name = annotation label, Args are pre-tokenised argument tokens already disambiguated by the lexer (IDENT_NAME, JSON_VALUE, RAW_VALUE, TYPE_REF, HTTP_METHOD, URL_PATH, etc. — see README §annotation-args).
  • TokenKeyword: Name = canonical keyword label; SourceName = the literal as it appeared (case / alias preserved); ItemsDepth = number of leading "items." segments stripped.
  • TokenRawBlockBody / TokenRawValueBody: Keyword = the parent keyword; Body = body content joined by "\n"; Raw = verbatim source-indented content; ItemsDepth carried from the head if applicable.
  • TokenOpaqueYaml: Body = body content (between fences).
  • TokenIdentName / value-typed tokens: Text = payload string.
  • TokenTitle / TokenDesc: Text = prose line content.

Pos is the source position of the first meaningful payload character.

func Lex

func Lex(lines []Line) []Token

Lex turns a preprocessed []Line into the token stream consumed by the grammar parser. Pipeline:

  1. Line classifier (lexLine): per-line classification into raw Tokens (annotation / keyword / fence / blank / text).
  2. Body accumulator: folds multi-line bodies (OPAQUE_YAML, RAW_BLOCK, RAW_VALUE) into single body tokens.
  3. Prose classifier: re-types surviving text tokens as TITLE / DESC.

The output stream ends in a single TokenEOF.

Details

See README §lexer-contract for the per-stage rules, §raw-block-terminators for body-termination rules, and §prose-classification for the TITLE / DESC split heuristics.

func (Token) HasArg

func (t Token) HasArg(n int) bool

HasArg reports whether the annotation token has at least n positional arguments. Convenience used by the parser dispatchers.

type TokenKind

type TokenKind int

TokenKind classifies a lexer-emitted token. The kinds map onto the grammar's terminal vocabulary:

  • ANN_* → TokenAnnotation, Name carries the annotation label
  • KW_* → TokenKeyword, Name carries the keyword label
  • IDENT_NAME, JSON_VALUE, RAW_VALUE, TYPE_REF, HTTP_METHOD, URL_PATH → distinct kinds, payload in Text
  • NUMBER_VALUE / INT_VALUE / BOOL_VALUE / STRING_VALUE / COMMA_LIST_VALUE / ENUM_OPTION_VALUE → distinct kinds
  • RAW_BLOCK_<KW> → TokenRawBlockBody with Keyword = "consumes"/"produces"/…
  • RAW_VALUE_<KW> → TokenRawValueBody with Keyword = "default"/"example"/"enum"
  • OPAQUE_YAML → TokenOpaqueYaml
  • TITLE / DESC / BLANK / EOF → TokenTitle / TokenDesc / TokenBlank / TokenEOF

The lexer also emits a few internal kinds (tokenYAMLFence, tokenText, tokenKeywordPre, tokenRawLine, tokenDirective) that are consumed by intermediate stages and never appear in the final stream the parser consumes.

const (
	TokenEOF TokenKind = iota

	TokenBlank
	TokenTitle
	TokenDesc

	TokenAnnotation     // ANN_*  — Name = annotation label
	TokenKeyword        // KW_*   — Name = keyword label
	TokenIdentName      // IDENT_NAME
	TokenJSONValue      // JSON_VALUE
	TokenRawValue       // RAW_VALUE
	TokenTypeRef        // TYPE_REF
	TokenHTTPMethod     // HTTP_METHOD
	TokenURLPath        // URL_PATH
	TokenNumberValue    // NUMBER_VALUE
	TokenIntValue       // INT_VALUE
	TokenBoolValue      // BOOL_VALUE
	TokenStringValue    // STRING_VALUE
	TokenCommaListValue // COMMA_LIST_VALUE
	TokenEnumOption     // ENUM_OPTION_VALUE
	TokenRawBlockBody   // RAW_BLOCK_<KW>  — Name = parent keyword
	TokenRawValueBody   // RAW_VALUE_<KW>  — Name = parent keyword
	TokenOpaqueYaml     // OPAQUE_YAML

)

func (TokenKind) String

func (k TokenKind) String() string

String renders a TokenKind for diagnostics.

type TypedValue

type TypedValue struct {
	Type    ValueShape
	Op      string
	Number  float64
	Integer int64
	Boolean bool
	String  string
}

TypedValue carries the lexer-recognised value shape.

Op is the leading comparison operator stripped from a NumberValue ("<", "<=", ">", ">=", "="). Empty for non-Number values or when no operator was present. Accepts e.g. `maximum: <5`; the analyzer interprets Op + Number to decide inclusive vs exclusive semantics.

type UnboundBlock

type UnboundBlock struct {
	// contains filtered or unexported fields
}

UnboundBlock represents a comment group with no annotation line — e.g. a struct field's docstring.

func (UnboundBlock) AnnotationArg

func (b UnboundBlock) AnnotationArg() (string, bool)

AnnotationArg default — Block kinds whose annotation takes no identifier argument (UnboundBlock, MetaBlock, RouteBlock, InlineOperationBlock, ParametersBlock — the latter has multiple args but conceptually it's a list, not a single IDENT_NAME). Typed kinds with a single IDENT_NAME override this method.

func (UnboundBlock) AnnotationKind

func (b UnboundBlock) AnnotationKind() AnnotationKind

func (UnboundBlock) Contact

func (b UnboundBlock) Contact() (Contact, error)

Contact returns the typed Contact value parsed from the block's `contact:` inline keyword. Returns (Contact{}, nil) when no `contact:` appeared, or when its value is empty. A non-nil error signals a malformed `Name <email>` head — the caller decides whether to fail or warn. Parses on call.

func (UnboundBlock) Description

func (b UnboundBlock) Description() string

func (UnboundBlock) Diagnostics

func (b UnboundBlock) Diagnostics() []Diagnostic

func (UnboundBlock) Extensions

func (b UnboundBlock) Extensions() iter.Seq[Extension]

func (UnboundBlock) GetBool

func (b UnboundBlock) GetBool(name string) (bool, bool)

func (UnboundBlock) GetFloat

func (b UnboundBlock) GetFloat(name string) (float64, bool)

func (UnboundBlock) GetInt

func (b UnboundBlock) GetInt(name string) (int64, bool)

func (UnboundBlock) GetList

func (b UnboundBlock) GetList(name string) ([]string, bool)

GetList returns the token list represented by the named keyword, unifying every list-shaped surface form the grammar accepts. Delegates to Property.AsList; see that method for the algorithm and accepted surface forms.

Returns (nil, false) when the keyword is absent. Returns (nil, true) when the keyword is present but every line trims to empty — the bool reports presence, not non-emptiness.

func (UnboundBlock) GetString

func (b UnboundBlock) GetString(name string) (string, bool)

func (UnboundBlock) Has

func (b UnboundBlock) Has(name string) bool

func (UnboundBlock) License

func (b UnboundBlock) License() (License, bool)

License returns the typed License value parsed from the block's `license:` inline keyword, or (License{}, false) when no `license:` keyword appeared. Parses on call.

func (UnboundBlock) Pos

func (b UnboundBlock) Pos() token.Position

func (UnboundBlock) PreambleDescription

func (b UnboundBlock) PreambleDescription() string

func (UnboundBlock) PreambleLines

func (b UnboundBlock) PreambleLines() []string

func (UnboundBlock) PreambleTitle

func (b UnboundBlock) PreambleTitle() string

func (UnboundBlock) Properties

func (b UnboundBlock) Properties() iter.Seq[Property]

func (UnboundBlock) Prose

func (b UnboundBlock) Prose() string

Prose joins the cached proseLines (TITLE + DESC + internal blanks, in source order) with "\n" and drops a single whitespace-only trailing line. The trim and join happen on the cached slice; no extra parse work.

func (UnboundBlock) ProseLines

func (b UnboundBlock) ProseLines() []string

func (UnboundBlock) SecurityRequirements

func (b UnboundBlock) SecurityRequirements() []security.Requirement

SecurityRequirements returns the typed list of `Security:` requirements parsed at lex time from the block's `security:` raw body, or nil when no `security:` keyword appeared. Each entry is a single-key map from scheme name → scope list, mirroring the shape OAS v2 expects on `spec.Operation.Security`.

func (UnboundBlock) Title

func (b UnboundBlock) Title() string

func (UnboundBlock) Walk

func (b UnboundBlock) Walk(w Walker)

Walk dispatches one Block through w. Nil callbacks are silently ignored. See Walker for the dispatch contract.

Walk reads only from b — it never mutates the Block or its properties. Walk is safe to call concurrently on the same Block from multiple goroutines as long as the Walker callbacks are themselves safe.

func (UnboundBlock) YAMLBlocks

func (b UnboundBlock) YAMLBlocks() iter.Seq[RawYAML]

type ValueShape

type ValueShape int

ValueShape names the lexical shape of a keyword's value, mapping directly onto the value terminals:

  • ShapeNumber → NUMBER_VALUE
  • ShapeInt → INT_VALUE
  • ShapeBool → BOOL_VALUE
  • ShapeString → STRING_VALUE
  • ShapeCommaList → COMMA_LIST_VALUE
  • ShapeEnumOption → ENUM_OPTION_VALUE (Values lists the closed set)
  • ShapeRawBlock → RAW_BLOCK_<KW> (multi-line body terminal)
  • ShapeRawValue → RAW_VALUE_<KW> (multi-line OR single-line body terminal)

See README §keyword-table for the per-shape callback dispatched by Walker.

const (
	ShapeNone ValueShape = iota
	ShapeNumber
	ShapeInt
	ShapeBool
	ShapeString
	ShapeCommaList
	ShapeEnumOption
	ShapeRawBlock
	ShapeRawValue
)

func (ValueShape) IsBody

func (v ValueShape) IsBody() bool

IsBody reports whether the keyword's value is a multi-line body terminal (RAW_BLOCK_* or RAW_VALUE_*). Body keywords trigger the lexer's body accumulator.

func (ValueShape) String

func (v ValueShape) String() string

String renders a ValueShape for diagnostics.

type Walker

type Walker struct {
	Title       func(s string)
	Description func(s string)

	Number  func(p Property, val float64, exclusive bool)
	Integer func(p Property, val int64)
	Bool    func(p Property, val bool)
	String  func(p Property, val string)
	Raw     func(p Property)
	Unknown func(p Property)

	Extension  func(ext Extension)
	Diagnostic func(d Diagnostic)

	FilterDepth int
}

Walker is the functional-visitor surface a Block exposes for bulk dispatch. Consumers wire only the callbacks they care about — every nil field is a silent no-op.

Details

See README §walker-contract for the dispatch order, the per- Keyword.Shape callback table, the Number/Integer/Bool typing- failure rule, the FilterDepth gating, and the concurrency contract.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL