
Psl Ast Layers
How to use the PSL syntax tree layers (green tree, red tree, strongly-typed AST...
Install
npx skills add prisma/orm --skill psl-ast-layers -g -a claude-code -y- Repository
- prisma/orm47.7k
- Category
- Business
- Updated
- 4d ago
- License
- Apache-2.0
- Context cost
- Mediumabout 2,163 tokens
- Runs scripts
- No, instructions only
What it does
How to use the PSL syntax tree layers (green tree, red tree, strongly-typed AST classes) correctly. Use for any PSL-related work: PSL interpreters (contract-psl), helpers inside the psl-parser package, the language server, formatters, or anything else that consumes `parse()` output from @internal/psl-parser.
PSL AST Layers
The PSL parser (packages/1-framework/2-authoring/psl-parser) produces a three-layer syntax tree. Each layer has exactly one job, pick the right one and the code stays lossless, typed, and cheap.
| Layer | Types | Job | Use in consumer code? |
|---|---|---|---|
| Green tree | GreenNode, GreenToken (syntax/green.ts) | Immutable, position-independent storage. Foundation only. | Never |
| Red tree | SyntaxNode, SyntaxToken (syntax/red.ts, syntax/navigation.ts) | Navigation with offsets and parents: findAncestor(), tokenAtOffset(), nextToken/prevToken, nonTriviaSibling() | Navigation outside the current node |
| Typed AST | ModelDeclarationAst, FieldDeclarationAst, … (syntax/ast/) | Structural information about a known node via getters (name(), fields(), lbrace(), value()) | Default choice |
Everything is exported from @internal/psl-parser/syntax. parse(source) returns { document: DocumentAst, diagnostics, sourceFile }, you start in the typed layer.
Choosing a layer
-
You know what the node is (you hold a
ModelDeclarationAst, aFieldAttributeAst, …) and want its parts → call the typed getters. Never dig through children yourself. -
You need to move outward or sideways (find the enclosing model, the previous declaration, the token after the cursor) → use the red tree's navigation helpers, then immediately re-enter the typed layer with a static
cast:// enclosing model (tests the node itself first, then walks ancestors) const model = node.syntax.findAncestor(ModelDeclarationAst.cast); // enclosing model OR composite type, combine casts with any(…) const owner = node.syntax.findAncestor( any(ModelDeclarationAst.cast, CompositeTypeDeclarationAst.cast), );Sideways and token-level movement all have dedicated helpers, do not hand-roll the walks:


