# Psl Ast Layers

How to use the PSL syntax tree layers (green tree, red tree, strongly-typed AST...

- Repository: https://github.com/prisma/orm (47661 stars)
- Category: Business
- License: Apache-2.0
- Last updated: 2026-10-31
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add prisma/orm --skill psl-ast-layers -g -a claude-code -y`
Codex: `npx skills add prisma/orm --skill psl-ast-layers -g -a codex -y`
Cursor: `npx skills add prisma/orm --skill psl-ast-layers -g -a cursor -y`
Gemini CLI: `npx skills add prisma/orm --skill psl-ast-layers -g -a gemini-cli -y`

## Description

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.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# 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

1. **You know what the node is** (you hold a `ModelDeclarationAst`, a `FieldAttributeAst`, …) and want its parts → call the typed getters. Never dig through children yourself.
2. **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`:

   ```ts
   // 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:

The rest: https://github.com/prisma/orm/blob/HEAD/skills-contrib/psl-ast-layers/SKILL.md
