Tya v0.56 Specification
Status: shipped. The
tya versionconstant is0.56.0. v0.56 unifies the public signatures ofparser.Parse/codegen.EmitC*/runner.RunFilearound the(X, []diag.Diagnostic, error)shape and adds expression-level recovery for the parser. The language surface is unchanged from v0.55.
Theme
v0.54 migrated every compiler stage’s errors to the structured
diag.Diagnostic type and added statement-level recovery in the
parser. v0.56 finishes that migration:
- Public Parser / Codegen / Runner entry points return the same
three-value shape
(result, []diag.Diagnostic, error)so callers can iteratediagsdirectly instead of unwrapping*XxxErrorviaerrors.As. - Expression-level recovery in the parser keeps
CallExpr,ArrayLit, andDictLitelement parsing going past per-arg errors — one bad expression no longer hides its siblings.
*ParserError / *CodegenError / *RunnerError are retained
as error wrappers for backwards compatibility; new code can
simply read the diags slice.
New API signatures
Parser
func Parse(toks []token.Token) (*ast.Program, []diag.Diagnostic, error)
func ParseWithComments(toks []token.Token, comments []CommentInfo) (*ast.Program, []diag.Diagnostic, error)
diagscarries every recoverable diagnostic (statement-level- expression-level). Empty when the parse is clean.
erris*ParserError{Diags: diags}for compatibility witherrors.As(err, &perr). It isnilwhendiagsis empty.- Invariant:
len(diags) == 0 ⟺ err == nil.
Codegen
func EmitC(prog *ast.Program) (string, []diag.Diagnostic, error)
func EmitCWithPath(prog *ast.Program, sourcePath string) (string, []diag.Diagnostic, error)
func EmitCWithCoverage(prog *ast.Program, sourcePath string, opt *CoverageOptions) (string, *CoverageRegistry, []diag.Diagnostic, error)
Codegen remains fail-fast in v0.56 — diags is nil on success
or a single-entry slice (copied from
err.(*CodegenError).Diags) on failure. Multi-error codegen is
scoped out to v0.57+.
Runner
func RunFile(path string, in io.Reader, out io.Writer, args []string) ([]diag.Diagnostic, error)
- Returns the parser’s recoverable diagnostic slice alongside the first fatal error (lex / parser / checker / eval).
RunnerErrorwidens from a singleDiagfield to aDiags []diag.Diagnosticslice. ADiag()method returns the first entry for pre-v0.56 call sites.AsRunnerErrorcontinues to unwrap from theerrorchain.
Expression-level recovery
The parser now recovers at three expression-list boundaries:
CallExprargument list (f(a, b, c)) — each bad argument records a diagnostic and the parser skips to the next,or).ArrayLitelements ([a, b, c]) — same shape, with]as the close marker.DictLitprop values ({k1: v1, k2: v2}) — only the prop value triggers recovery; a missing or malformed key still aborts the literal.
Nested brackets are skipped over so an inner stray , cannot
fool the outer recovery loop into stopping early.
Binary chains (a + broken + c) and member chains
(a.broken.c) remain whole-expression failures in v0.56 and are
scope-out for v0.57+.
New helper
// internal/parser/recovery.go
func (p *Parser) skipToCommaOrClose(closes ...token.Type)
func (p *Parser) skipBalanced(open, close token.Type)
Called from CallExpr / ArrayLit / DictLit parse loops. NEWLINE, DEDENT, EOF, and the supplied close brackets halt the scan; the helper does NOT consume the boundary token.
Migration guide
The new signatures break source compatibility for direct callers. The mechanical migration is:
// Pre-v0.56
prog, err := parser.Parse(toks)
// v0.56
prog, _, err := parser.Parse(toks)
// or, if you want multi-error visibility:
prog, diags, err := parser.Parse(toks)
for _, d := range diags { renderDiag(d) }
Same shape for EmitC / EmitCWithPath, with an extra _ in
the destructure. EmitCWithCoverage gains a fourth []Diag
return value before error. RunFile gains a leading
[]Diag return value before error.
errors.As(err, &perr) continues to work, so callers that
prefer the wrapper-based path do not need to migrate.
Scope-out (v0.57+)
- Codegen multi-error (
unsupported AST shape全件 collect instead of bailing on the first). - Runner multi-error (low priority — eval is inherently fail-fast).
- Binary-chain / MemberExpr-chain expression-level recovery.
- Gradual deprecation of
*ParserError/*CodegenError/*RunnerErroronce all callers iterate diags directly.