@prisma-next/psl-parser 0.14.0-dev.4 → 0.14.0-dev.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -10
- package/dist/declarations-CPDuQm7x.mjs +1055 -0
- package/dist/declarations-CPDuQm7x.mjs.map +1 -0
- package/dist/format.d.mts +1 -1
- package/dist/format.mjs +2 -1
- package/dist/format.mjs.map +1 -1
- package/dist/index.d.mts +137 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +474 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{parse-BjZ1LPe6.d.mts → parse-BKN_-hW8.d.mts} +126 -51
- package/dist/parse-BKN_-hW8.d.mts.map +1 -0
- package/dist/parse-Du30Ca0-.mjs +626 -0
- package/dist/parse-Du30Ca0-.mjs.map +1 -0
- package/dist/syntax.d.mts +20 -2
- package/dist/syntax.d.mts.map +1 -1
- package/dist/syntax.mjs +43 -2
- package/dist/syntax.mjs.map +1 -0
- package/package.json +5 -6
- package/src/block-reconstruction.ts +139 -0
- package/src/exports/index.ts +27 -3
- package/src/exports/syntax.ts +25 -5
- package/src/extension-block.ts +107 -0
- package/src/parse.ts +23 -5
- package/src/resolve.ts +123 -0
- package/src/source-file.ts +25 -0
- package/src/symbol-table.ts +446 -0
- package/src/syntax/ast/attributes.ts +5 -6
- package/src/syntax/ast/declarations.ts +51 -26
- package/src/syntax/ast/expressions.ts +12 -13
- package/src/syntax/ast/identifier.ts +2 -3
- package/src/syntax/ast/qualified-name.ts +22 -19
- package/src/syntax/ast/type-annotation.ts +4 -5
- package/src/syntax/ast-helpers.ts +27 -3
- package/src/syntax/navigation.ts +55 -0
- package/src/syntax/red.ts +317 -42
- package/dist/parse-B_3gIEFd.mjs +0 -1421
- package/dist/parse-B_3gIEFd.mjs.map +0 -1
- package/dist/parse-BjZ1LPe6.d.mts.map +0 -1
- package/dist/parser-CaplKvRs.mjs +0 -1145
- package/dist/parser-CaplKvRs.mjs.map +0 -1
- package/dist/parser-Dfi3Wfdq.d.mts +0 -7
- package/dist/parser-Dfi3Wfdq.d.mts.map +0 -1
- package/dist/parser.d.mts +0 -2
- package/dist/parser.mjs +0 -2
- package/src/exports/parser.ts +0 -1
- package/src/parser.ts +0 -1642
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-Du30Ca0-.mjs","names":["#text","#lineStarts","#lineIndexAt","#lineStartAt","#lineEndAt","#stack","#tokenizer","#sourceFile","#builder","#diagnostics","#offset","#depth","#advance"],"sources":["../src/source-file.ts","../src/syntax/green.ts","../src/syntax/green-builder.ts","../src/parse.ts"],"sourcesContent":["const CARRIAGE_RETURN = 13;\nconst LINE_FEED = 10;\n\nexport interface Position {\n readonly line: number;\n readonly character: number;\n}\n\nexport interface Range {\n readonly start: Position;\n readonly end: Position;\n}\n\nexport class SourceFile {\n readonly #text: string;\n readonly #lineStarts: readonly number[];\n\n constructor(text: string) {\n this.#text = text;\n const lineStarts: number[] = [0];\n for (let offset = 0; offset < text.length; offset++) {\n if (text.charCodeAt(offset) === LINE_FEED) {\n lineStarts.push(offset + 1);\n }\n }\n this.#lineStarts = lineStarts;\n }\n\n get text(): string {\n return this.#text;\n }\n\n get length(): number {\n return this.#text.length;\n }\n\n get lineCount(): number {\n return this.#lineStarts.length;\n }\n\n lineStartOffsets(): readonly number[] {\n return this.#lineStarts;\n }\n\n lineStartOffset(line: number): number {\n if (line <= 0) {\n return 0;\n }\n return this.#lineStarts[line] ?? this.#text.length;\n }\n\n lineEndOffset(line: number): number {\n if (line < 0) {\n return 0;\n }\n\n const nextLineStart = this.#lineStarts[line + 1];\n if (nextLineStart === undefined) {\n return this.#text.length;\n }\n\n const lineFeedOffset = nextLineStart - 1;\n const carriageReturnOffset = lineFeedOffset - 1;\n return this.#text.charCodeAt(carriageReturnOffset) === CARRIAGE_RETURN\n ? carriageReturnOffset\n : lineFeedOffset;\n }\n\n positionAt(offset: number): Position {\n const clamped = clamp(offset, 0, this.#text.length);\n const line = this.#lineIndexAt(clamped);\n return { line, character: clamped - this.#lineStartAt(line) };\n }\n\n offsetAt(position: Position): number {\n const line = clamp(position.line, 0, this.#lineStarts.length - 1);\n const lineStart = this.#lineStartAt(line);\n const lineEnd = this.#lineEndAt(line);\n return clamp(lineStart + position.character, lineStart, lineEnd);\n }\n\n #lineStartAt(line: number): number {\n return this.#lineStarts[line] ?? 0;\n }\n\n #lineEndAt(line: number): number {\n return line + 1 < this.#lineStarts.length ? this.#lineStartAt(line + 1) - 1 : this.#text.length;\n }\n\n #lineIndexAt(offset: number): number {\n const lineStarts = this.#lineStarts;\n let low = 0;\n let high = lineStarts.length - 1;\n while (low < high) {\n const mid = (low + high + 1) >>> 1;\n if ((lineStarts[mid] ?? 0) <= offset) {\n low = mid;\n } else {\n high = mid - 1;\n }\n }\n return low;\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n if (value < min) {\n return min;\n }\n if (value > max) {\n return max;\n }\n return value;\n}\n","import type { TokenKind } from '../tokenizer';\nimport type { SyntaxKind } from './syntax-kind';\n\nexport interface GreenToken {\n readonly type: 'token';\n readonly kind: TokenKind;\n readonly text: string;\n}\n\nexport interface GreenNode {\n readonly type: 'node';\n readonly kind: SyntaxKind;\n readonly children: ReadonlyArray<GreenElement>;\n readonly textLength: number;\n}\n\nexport type GreenElement = GreenNode | GreenToken;\n\nexport function greenToken(kind: TokenKind, text: string): GreenToken {\n return { type: 'token', kind, text };\n}\n\nexport function greenNode(kind: SyntaxKind, children: ReadonlyArray<GreenElement>): GreenNode {\n let textLength = 0;\n for (const child of children) {\n textLength += child.type === 'token' ? child.text.length : child.textLength;\n }\n return { type: 'node', kind, children, textLength };\n}\n","import type { TokenKind } from '../tokenizer';\nimport type { GreenElement, GreenNode } from './green';\nimport { greenNode, greenToken } from './green';\nimport type { SyntaxKind } from './syntax-kind';\n\nexport class GreenNodeBuilder {\n readonly #stack: Array<{ kind: SyntaxKind; children: GreenElement[] }> = [];\n\n startNode(kind: SyntaxKind): void {\n this.#stack.push({ kind, children: [] });\n }\n\n token(kind: TokenKind, text: string): void {\n const current = this.#stack.at(-1);\n if (!current) {\n throw new Error('GreenNodeBuilder: token() called with no open node');\n }\n current.children.push(greenToken(kind, text));\n }\n\n finishNode(): GreenNode {\n const completed = this.#stack.pop();\n if (!completed) {\n throw new Error('GreenNodeBuilder: finishNode() called with no open node');\n }\n const node = greenNode(completed.kind, completed.children);\n const parent = this.#stack.at(-1);\n if (parent) {\n parent.children.push(node);\n }\n return node;\n }\n}\n","import type { PslDiagnosticCode } from '@prisma-next/framework-components/psl-ast';\nimport { UNSPECIFIED_PSL_NAMESPACE_ID } from '@prisma-next/framework-components/psl-ast';\nimport { type Range, SourceFile } from './source-file';\nimport { DocumentAst } from './syntax/ast/declarations';\nimport type { GreenNode } from './syntax/green';\nimport { GreenNodeBuilder } from './syntax/green-builder';\nimport { createSyntaxTree } from './syntax/red';\nimport type { SyntaxKind } from './syntax/syntax-kind';\nimport { isTerminatedStringLiteral, type Token, Tokenizer, type TokenKind } from './tokenizer';\n\nexport interface ParseDiagnostic {\n readonly code: PslDiagnosticCode;\n readonly message: string;\n readonly range: Range;\n}\n\nexport interface ParseResult {\n readonly document: DocumentAst;\n readonly diagnostics: readonly ParseDiagnostic[];\n readonly sourceFile: SourceFile;\n}\n\nconst TRIVIA_KINDS: ReadonlySet<TokenKind> = new Set<TokenKind>([\n 'Whitespace',\n 'Newline',\n 'Comment',\n]);\n\n/**\n * The source span of a token, captured eagerly so it stays valid after the\n * cursor advances past the token it points at.\n */\nexport interface DiagnosticMark {\n readonly offset: number;\n readonly length: number;\n}\n\n/**\n * The fault-tolerant parser substrate the grammars drive. Trivia is flushed\n * into the enclosing open node, so every child node spans exactly its first\n * through last significant token.\n */\nexport class Cursor {\n readonly #tokenizer: Tokenizer;\n readonly #sourceFile: SourceFile;\n readonly #builder = new GreenNodeBuilder();\n readonly #diagnostics: ParseDiagnostic[] = [];\n #offset = 0;\n #depth = 0;\n\n constructor(source: string) {\n this.#tokenizer = new Tokenizer(source);\n this.#sourceFile = new SourceFile(source);\n }\n\n get diagnostics(): readonly ParseDiagnostic[] {\n return this.#diagnostics;\n }\n\n get sourceFile(): SourceFile {\n return this.#sourceFile;\n }\n\n peekKind(ahead = 0): TokenKind {\n return this.peekToken(ahead).kind;\n }\n\n peekToken(ahead = 0): Token {\n let rawIndex = 0;\n let remaining = ahead;\n for (;;) {\n const token = this.#tokenizer.peek(rawIndex);\n if (token.kind === 'Eof') return token;\n if (TRIVIA_KINDS.has(token.kind)) {\n rawIndex++;\n continue;\n }\n if (remaining === 0) return token;\n remaining--;\n rawIndex++;\n }\n }\n\n /** Span of the significant token `lookahead` positions ahead (`mark(0)` = the next). */\n mark(lookahead = 0): DiagnosticMark {\n let rawIndex = 0;\n let offset = this.#offset;\n let remaining = lookahead;\n for (;;) {\n const token = this.#tokenizer.peek(rawIndex);\n if (token.kind === 'Eof') {\n return { offset, length: token.text.length };\n }\n if (!TRIVIA_KINDS.has(token.kind) && remaining === 0) {\n return { offset, length: token.text.length };\n }\n if (!TRIVIA_KINDS.has(token.kind)) {\n remaining--;\n }\n offset += token.text.length;\n rawIndex++;\n }\n }\n\n /**\n * Zero-width mark just past the last consumed significant token — anchors an\n * \"expected here\" diagnostic, e.g. the `{` missing after a declaration's name.\n */\n markAfterLastToken(): DiagnosticMark {\n return { offset: this.#offset, length: 0 };\n }\n\n startNode(kind: SyntaxKind): void {\n if (this.#depth > 0) {\n this.flushTrivia();\n }\n this.#builder.startNode(kind);\n this.#depth++;\n }\n\n finishNode(): GreenNode {\n this.#depth--;\n return this.#builder.finishNode();\n }\n\n bump(): Token {\n this.flushTrivia();\n const token = this.#tokenizer.peek();\n if (token.kind === 'Eof') return token;\n this.#builder.token(token.kind, token.text);\n this.#advance();\n return token;\n }\n\n recoverToSyncPoint(): void {\n for (;;) {\n const token = this.#tokenizer.peek();\n if (token.kind === 'Eof' || token.kind === 'Newline' || token.kind === 'RBrace') {\n return;\n }\n this.#builder.token(token.kind, token.text);\n this.#advance();\n }\n }\n\n flushTrivia(): void {\n for (;;) {\n const token = this.#tokenizer.peek();\n if (!TRIVIA_KINDS.has(token.kind)) return;\n this.#builder.token(token.kind, token.text);\n this.#advance();\n }\n }\n\n diagnostic(code: PslDiagnosticCode, message: string, mark: DiagnosticMark): void {\n const start = mark.offset;\n const end = start + mark.length;\n this.#diagnostics.push({\n code,\n message,\n range: {\n start: this.#sourceFile.positionAt(start),\n end: this.#sourceFile.positionAt(end),\n },\n });\n }\n\n #advance(): void {\n this.#offset += this.#tokenizer.next().text.length;\n }\n}\n\nfunction parseIdentifier(cursor: Cursor): void {\n cursor.startNode('Identifier');\n cursor.bump();\n cursor.finishNode();\n}\n\n/**\n * Returns `undefined` when the next significant token does not start a\n * recognised expression, leaving recovery to the caller.\n */\nexport function parseExpression(cursor: Cursor): GreenNode | undefined {\n return (\n parseStringLiteralExpr(cursor) ??\n parseNumberLiteralExpr(cursor) ??\n parseArrayLiteral(cursor) ??\n parseObjectLiteralExpr(cursor) ??\n parseFunctionCall(cursor) ??\n parseBooleanLiteralExpr(cursor) ??\n parseIdentifierExpr(cursor)\n );\n}\n\nexport function parseStringLiteralExpr(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'StringLiteral') return undefined;\n const stringMark = cursor.mark();\n const text = cursor.peekToken().text;\n cursor.startNode('StringLiteralExpr');\n cursor.bump();\n if (!isTerminatedStringLiteral(text)) {\n cursor.diagnostic('PSL_UNTERMINATED_STRING', 'Unterminated string literal', stringMark);\n }\n return cursor.finishNode();\n}\n\nexport function parseNumberLiteralExpr(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'NumberLiteral') return undefined;\n cursor.startNode('NumberLiteralExpr');\n cursor.bump();\n return cursor.finishNode();\n}\n\n/**\n * Parses a namespace-qualified name `[space ':']? Ident ('.' Ident)*`. The\n * caller guarantees a leading `Ident`.\n *\n * Parsing the whole chain up front lets a position decide\n * constructor-vs-reference by peeking exactly one token for `(`, with no scan of\n * the dotted chain's length.\n */\nexport function parseQualifiedName(cursor: Cursor): void {\n cursor.startNode('QualifiedName');\n parseIdentifier(cursor); // first segment: the space, namespace, or bare name\n parseQualifiedSegments(cursor, 'Colon');\n parseQualifiedSegments(cursor, 'Dot');\n cursor.finishNode();\n}\n\n/**\n * A well-formed name carries at most one colon space and one dot namespace, so\n * each separator past the first of its kind reports `PSL_INVALID_QUALIFIED_NAME`.\n * The separator is consumed regardless, keeping the lossless round-trip intact.\n */\nfunction parseQualifiedSegments(cursor: Cursor, separator: 'Colon' | 'Dot'): void {\n let seen = 0;\n while (cursor.peekKind() === separator) {\n seen++;\n const separatorMark = cursor.mark();\n cursor.bump();\n if (seen > 1) {\n cursor.diagnostic(\n 'PSL_INVALID_QUALIFIED_NAME',\n 'Qualified name has too many segments',\n separatorMark,\n );\n }\n if (cursor.peekKind() === 'Ident') {\n parseIdentifier(cursor);\n } else {\n cursor.diagnostic(\n 'PSL_INVALID_QUALIFIED_NAME',\n 'Qualified name is missing a name after the separator',\n cursor.mark(),\n );\n }\n }\n}\n\n// Ordering among the `Ident`-leading alternatives is load-bearing: the\n// `LParen`/`Dot` lookahead of `parseCall` must win before the boolean check, so\n// `true(` stays a function call named `true` rather than a boolean literal.\nexport function parseBooleanLiteralExpr(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'Ident') return undefined;\n const text = cursor.peekToken().text;\n if (text !== 'true' && text !== 'false') return undefined;\n cursor.startNode('BooleanLiteralExpr');\n cursor.bump();\n return cursor.finishNode();\n}\n\nexport function parseIdentifierExpr(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'Ident') return undefined;\n cursor.startNode('Identifier');\n cursor.bump();\n return cursor.finishNode();\n}\n\nexport function parseArrayLiteral(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'LBracket') return undefined;\n cursor.startNode('ArrayLiteral');\n cursor.bump();\n while (cursor.peekKind() !== 'RBracket' && cursor.peekKind() !== 'Eof') {\n const element = parseExpression(cursor);\n if (!element) break;\n if (cursor.peekKind() === 'Comma') {\n cursor.bump();\n } else {\n break;\n }\n }\n if (cursor.peekKind() === 'RBracket') {\n cursor.bump();\n }\n return cursor.finishNode();\n}\n\nexport function parseObjectLiteralExpr(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'LBrace') return undefined;\n const braceMark = cursor.mark();\n cursor.startNode('ObjectLiteralExpr');\n cursor.bump();\n while (cursor.peekKind() !== 'RBrace' && cursor.peekKind() !== 'Eof') {\n parseObjectField(cursor);\n if (cursor.peekKind() === 'Comma') {\n cursor.bump();\n } else if (cursor.peekKind() === 'Ident') {\n // A following identifier key with no comma re-enters the loop; the next\n // parseObjectField consumes ≥1 token, so progress is guaranteed.\n cursor.diagnostic(\n 'PSL_INVALID_OBJECT_LITERAL',\n 'Expected \",\" between object-literal fields',\n cursor.markAfterLastToken(),\n );\n } else {\n break;\n }\n }\n if (cursor.peekKind() === 'RBrace') {\n cursor.bump();\n } else {\n cursor.diagnostic('PSL_INVALID_OBJECT_LITERAL', 'Unterminated object literal', braceMark);\n }\n return cursor.finishNode();\n}\n\nexport function parseObjectField(cursor: Cursor): GreenNode {\n cursor.startNode('ObjectField');\n const keyMark = cursor.mark();\n const keyText = cursor.peekToken().text;\n if (cursor.peekKind() === 'Ident') {\n parseIdentifier(cursor);\n } else if (cursor.peekKind() === 'StringLiteral') {\n // A string-literal key (e.g. `{ \"length\": 35 }`) is accepted; its logical\n // name is the unquoted string.\n parseStringLiteralExpr(cursor);\n }\n if (cursor.peekKind() === 'Colon') {\n cursor.bump(); // Colon\n const value = parseExpression(cursor);\n if (!value) {\n cursor.diagnostic('PSL_INVALID_OBJECT_LITERAL', 'Expected a value after \":\"', cursor.mark());\n }\n } else {\n cursor.diagnostic('PSL_INVALID_OBJECT_LITERAL', `Expected \":\" after \"${keyText}\"`, keyMark);\n const followsWithKey = cursor.peekKind() === 'Ident' && cursor.peekKind(1) === 'Colon';\n if (!followsWithKey) {\n parseExpression(cursor); // best-effort: consume a value if one follows\n }\n }\n return cursor.finishNode();\n}\n\n/**\n * Whether the next tokens open a call: a bare `Ident(` or a namespace-qualified\n * `Ident.Ident(`. The lookahead is deliberately bounded so a bare dotted\n * reference like `a.b` is not mistaken for a call, rather than scanning an\n * unbounded dotted chain ahead to find the paren.\n */\nfunction isCallAhead(cursor: Cursor): boolean {\n if (cursor.peekKind() !== 'Ident') return false;\n if (cursor.peekKind(1) === 'LParen') return true;\n return (\n cursor.peekKind(1) === 'Dot' &&\n cursor.peekKind(2) === 'Ident' &&\n cursor.peekKind(3) === 'LParen'\n );\n}\n\n/**\n * Parses a function/constructor call — bare `autoincrement()` or qualified\n * `temporal.updatedAt()`. Returns `undefined` unless {@link isCallAhead}\n * confirms a trailing `(`, so the `parseExpression` chain falls through to the\n * boolean and bare-identifier forms.\n */\nexport function parseFunctionCall(cursor: Cursor): GreenNode | undefined {\n if (!isCallAhead(cursor)) return undefined;\n cursor.startNode('FunctionCall');\n parseQualifiedName(cursor);\n if (cursor.peekKind() === 'LParen') {\n parseParenArgs(cursor);\n }\n return cursor.finishNode();\n}\n\n/** Parses a parenthesised, comma-separated `AttributeArg` list into the currently open node. */\nfunction parseParenArgs(cursor: Cursor): void {\n cursor.bump();\n while (cursor.peekKind() !== 'RParen' && cursor.peekKind() !== 'Eof') {\n parseAttributeArg(cursor);\n if (cursor.peekKind() === 'Comma') {\n cursor.bump();\n } else {\n break;\n }\n }\n if (cursor.peekKind() === 'RParen') {\n cursor.bump();\n }\n}\n\nexport function parseAttributeArg(cursor: Cursor): void {\n const kind = cursor.peekKind();\n if (\n kind !== 'Ident' &&\n kind !== 'StringLiteral' &&\n kind !== 'NumberLiteral' &&\n kind !== 'LBracket' &&\n kind !== 'LBrace'\n ) {\n return;\n }\n cursor.startNode('AttributeArg');\n if (cursor.peekKind() === 'Ident' && cursor.peekKind(1) === 'Colon') {\n parseIdentifier(cursor);\n cursor.bump();\n }\n parseArgValue(cursor);\n cursor.finishNode();\n}\n\nfunction parseArgValue(cursor: Cursor): void {\n parseExpression(cursor);\n}\n\nexport function parseAttributeArgList(cursor: Cursor): GreenNode {\n cursor.startNode('AttributeArgList');\n parseParenArgs(cursor);\n return cursor.finishNode();\n}\n\nexport function parseAttribute(cursor: Cursor): GreenNode {\n const isBlockAttribute = cursor.peekKind() === 'DoubleAt';\n const attributeMark = cursor.mark();\n cursor.startNode(isBlockAttribute ? 'ModelAttribute' : 'FieldAttribute');\n cursor.bump();\n if (cursor.peekKind() === 'Ident') {\n parseQualifiedName(cursor);\n } else {\n cursor.diagnostic('PSL_INVALID_ATTRIBUTE_SYNTAX', 'Attribute name expected', attributeMark);\n }\n if (cursor.peekKind() === 'LParen') {\n parseAttributeArgList(cursor);\n }\n return cursor.finishNode();\n}\n\n/**\n * A type annotation: `QualifiedName (argList)? ([])? (?)?`, e.g.\n * `pgvector.Vector(1536)[]?`. When the field has no type, no node is emitted —\n * a missing type is the absence of a `TypeAnnotation`, not a zero-width one.\n */\nexport function parseTypeAnnotation(cursor: Cursor): void {\n const kind = cursor.peekKind();\n if (kind !== 'Ident' && kind !== 'LBracket' && kind !== 'Question') {\n return;\n }\n cursor.startNode('TypeAnnotation');\n if (cursor.peekKind() === 'Ident') {\n parseQualifiedName(cursor);\n if (cursor.peekKind() === 'LParen') {\n parseAttributeArgList(cursor);\n }\n }\n if (cursor.peekKind() === 'LBracket') {\n cursor.bump();\n if (cursor.peekKind() === 'RBracket') {\n cursor.bump();\n }\n }\n if (cursor.peekKind() === 'Question') {\n cursor.bump();\n }\n cursor.finishNode();\n}\n\ntype MemberParser = (cursor: Cursor) => void;\n\n/**\n * Parses a full PSL document. Never throws — malformed input yields diagnostics\n * and a recovered tree, not an exception.\n */\nexport function parse(source: string): ParseResult {\n const cursor = new Cursor(source);\n const green = parseDocument(cursor);\n const root = createSyntaxTree(green);\n const document = DocumentAst.cast(root) ?? new DocumentAst(root);\n return { document, diagnostics: cursor.diagnostics, sourceFile: cursor.sourceFile };\n}\n\nfunction parseDocument(cursor: Cursor): GreenNode {\n cursor.startNode('Document');\n while (cursor.peekKind() !== 'Eof') {\n parseDeclaration(cursor, false);\n }\n cursor.flushTrivia(); // attach trailing trivia so the round-trip stays lossless\n return cursor.finishNode();\n}\n\nconst RESERVED_BLOCK_KEYWORDS: ReadonlySet<string> = new Set([\n 'model',\n 'namespace',\n 'type',\n 'types',\n]);\n\nfunction keywordIs(cursor: Cursor, keyword: string): boolean {\n return cursor.peekKind() === 'Ident' && cursor.peekToken().text === keyword;\n}\n\n/**\n * Each alternative is a no-op on non-match, consuming nothing, so the\n * forward-only cursor is never left half-consumed by a rejected alternative.\n * Recovery runs via the `if (!node)` tail rather than as a `??` arm, because it\n * appends raw tokens to the open parent instead of returning a child node.\n */\nfunction parseDeclaration(cursor: Cursor, insideNamespace: boolean): void {\n const name = cursor.peekKind(1) === 'Ident' ? cursor.peekToken(1).text : '';\n if (insideNamespace && keywordIs(cursor, 'namespace')) {\n cursor.diagnostic(\n 'PSL_INVALID_NAMESPACE_BLOCK',\n `Recursive \"namespace ${name}\" block is not allowed; namespace blocks may not nest`,\n cursor.mark(),\n );\n } else if (insideNamespace && keywordIs(cursor, 'types')) {\n cursor.diagnostic(\n 'PSL_INVALID_NAMESPACE_BLOCK',\n '`types` blocks must be declared at the document top level, not inside a namespace block',\n cursor.mark(),\n );\n } else if (keywordIs(cursor, 'namespace') && name === UNSPECIFIED_PSL_NAMESPACE_ID) {\n cursor.diagnostic(\n 'PSL_INVALID_NAMESPACE_BLOCK',\n `Namespace name \"${UNSPECIFIED_PSL_NAMESPACE_ID}\" is reserved for the parser-synthesised bucket for top-level declarations`,\n cursor.mark(1),\n );\n }\n\n const node =\n parseModel(cursor) ??\n parseNamespace(cursor) ??\n parseCompositeType(cursor) ??\n parseTypesBlock(cursor) ??\n parseGenericBlock(cursor);\n if (!node) {\n parseUnsupportedTopLevel(cursor);\n }\n}\n\n/**\n * Reports only the first missing piece — a missing name suppresses the\n * missing-brace diagnostic. `nameRequired` is false only for the `types` block.\n */\nfunction parseBlock(\n cursor: Cursor,\n kind: SyntaxKind,\n nameRequired: boolean,\n parseMember: MemberParser,\n): GreenNode {\n const keyword = cursor.peekToken().text;\n const keywordMark = cursor.mark();\n cursor.startNode(kind);\n cursor.bump();\n const hasName = nameRequired && cursor.peekKind() === 'Ident';\n if (hasName) {\n parseIdentifier(cursor);\n }\n if (nameRequired && !hasName) {\n cursor.diagnostic('PSL_INVALID_DECLARATION', `Expected a name after \"${keyword}\"`, keywordMark);\n } else if (cursor.peekKind() !== 'LBrace') {\n cursor.diagnostic(\n 'PSL_INVALID_DECLARATION',\n `Expected \"{\" to open the \"${keyword}\" block`,\n cursor.markAfterLastToken(),\n );\n }\n if (cursor.peekKind() === 'LBrace') {\n parseBlockBody(cursor, parseMember);\n } else {\n cursor.recoverToSyncPoint();\n }\n return cursor.finishNode();\n}\n\nexport function parseModel(cursor: Cursor): GreenNode | undefined {\n if (!keywordIs(cursor, 'model')) return undefined;\n return parseBlock(cursor, 'ModelDeclaration', true, parseModelMember);\n}\n\n/**\n * Excluding the reserved keywords keeps a malformed reserved block (e.g. `model\n * {` with no name) routed to its dedicated parser. The generic keyword set is\n * open, so a bare identifier with no brace (e.g. `oops`) is read as an unfinished\n * custom declaration rather than unsupported content.\n */\nexport function parseGenericBlock(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'Ident') return undefined;\n const keyword = cursor.peekToken().text;\n if (RESERVED_BLOCK_KEYWORDS.has(keyword)) return undefined;\n const hasName = cursor.peekKind(1) === 'Ident' && cursor.peekKind(2) === 'LBrace';\n cursor.startNode('GenericBlockDeclaration');\n cursor.bump();\n if (hasName) {\n parseIdentifier(cursor);\n }\n if (cursor.peekKind() === 'LBrace') {\n parseBlockBody(cursor, parseKeyValueMember);\n } else {\n cursor.diagnostic(\n 'PSL_INVALID_DECLARATION',\n `Expected \"{\" to open the \"${keyword}\" block`,\n cursor.markAfterLastToken(),\n );\n cursor.recoverToSyncPoint();\n }\n return cursor.finishNode();\n}\n\nexport function parseNamespace(cursor: Cursor): GreenNode | undefined {\n if (!keywordIs(cursor, 'namespace')) return undefined;\n return parseBlock(cursor, 'Namespace', true, (inner) => parseDeclaration(inner, true));\n}\n\nexport function parseCompositeType(cursor: Cursor): GreenNode | undefined {\n if (!keywordIs(cursor, 'type')) return undefined;\n return parseBlock(cursor, 'CompositeTypeDeclaration', true, parseModelMember);\n}\n\n/** `types` (plural) is the no-name types block; the singular `type` is the composite type above. */\nexport function parseTypesBlock(cursor: Cursor): GreenNode | undefined {\n if (!keywordIs(cursor, 'types')) return undefined;\n return parseBlock(cursor, 'TypesBlock', false, parseNamedTypeMember);\n}\n\n/** Every `parseMember` consumes at least one significant token, so the loop always terminates. */\nfunction parseBlockBody(cursor: Cursor, parseMember: MemberParser): void {\n const braceMark = cursor.mark();\n cursor.bump();\n for (;;) {\n const kind = cursor.peekKind();\n if (kind === 'RBrace' || kind === 'Eof') break;\n parseMember(cursor);\n }\n if (cursor.peekKind() === 'RBrace') {\n cursor.bump();\n } else {\n cursor.diagnostic('PSL_UNTERMINATED_BLOCK', 'Unterminated block declaration', braceMark);\n }\n}\n\nfunction parseUnsupportedTopLevel(cursor: Cursor): void {\n const offending = cursor.peekToken().text;\n const message =\n cursor.peekKind(1) === 'LBrace'\n ? `Unsupported top-level block \"${offending}\"`\n : `Unsupported top-level declaration \"${offending}\"`;\n cursor.diagnostic('PSL_UNSUPPORTED_TOP_LEVEL_BLOCK', message, cursor.mark());\n cursor.bump();\n cursor.recoverToSyncPoint();\n}\n\n/**\n * Matches a leading `@@` block attribute, a no-op otherwise. Single-`@`\n * attributes belong to fields and are parsed inside `parseField`.\n */\nexport function parseBlockAttribute(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'DoubleAt') return undefined;\n return parseAttribute(cursor);\n}\n\nfunction parseModelMember(cursor: Cursor): void {\n const node = parseBlockAttribute(cursor) ?? parseField(cursor);\n if (!node) {\n invalidMember(\n cursor,\n 'PSL_INVALID_MODEL_MEMBER',\n `Invalid model member declaration \"${cursor.peekToken().text}\"`,\n );\n }\n}\n\nfunction parseNamedTypeMember(cursor: Cursor): void {\n const node = parseNamedType(cursor);\n if (!node) {\n invalidMember(\n cursor,\n 'PSL_INVALID_TYPES_MEMBER',\n `Invalid types declaration \"${cursor.peekToken().text}\"`,\n );\n }\n}\n\n/**\n * A generic-block member is either a `@@`-block attribute or a `key = value`\n * entry. The block-attribute alternative is purely syntactic — it does not judge\n * whether the attribute is valid for the block's kind.\n */\nfunction parseKeyValueMember(cursor: Cursor): void {\n const node = parseBlockAttribute(cursor) ?? parseKeyValue(cursor);\n if (!node) {\n invalidMember(cursor, 'PSL_INVALID_EXTENSION_BLOCK_MEMBER', 'Invalid block entry');\n }\n}\n\nfunction invalidMember(cursor: Cursor, code: PslDiagnosticCode, message: string): void {\n cursor.diagnostic(code, message, cursor.mark());\n cursor.bump(); // consume the offending token so the member loop makes progress\n cursor.recoverToSyncPoint();\n}\n\nexport function parseField(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'Ident') return undefined;\n cursor.startNode('FieldDeclaration');\n const nameMark = cursor.mark();\n const nameText = cursor.peekToken().text;\n parseIdentifier(cursor);\n if (cursor.peekKind() !== 'Ident') {\n cursor.diagnostic(\n 'PSL_INVALID_MODEL_MEMBER',\n `Expected a type after field \"${nameText}\"`,\n nameMark,\n );\n }\n parseTypeAnnotation(cursor);\n while (cursor.peekKind() === 'At') {\n parseAttribute(cursor);\n }\n return cursor.finishNode();\n}\n\nexport function parseNamedType(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'Ident') return undefined;\n cursor.startNode('NamedTypeDeclaration');\n const nameMark = cursor.mark();\n const nameText = cursor.peekToken().text;\n parseIdentifier(cursor);\n if (cursor.peekKind() === 'Equals') {\n cursor.bump();\n } else {\n cursor.diagnostic('PSL_INVALID_TYPES_MEMBER', `Expected \"=\" after \"${nameText}\"`, nameMark);\n }\n parseTypeAnnotation(cursor);\n while (cursor.peekKind() === 'At') {\n parseAttribute(cursor);\n }\n return cursor.finishNode();\n}\n\n/**\n * A generic-block entry is either `key = value` or a bare `key` (committing a\n * `KeyValuePair` carrying only the key). A `key =` with no following expression\n * is flagged.\n */\nexport function parseKeyValue(cursor: Cursor): GreenNode | undefined {\n if (cursor.peekKind() !== 'Ident') return undefined;\n cursor.startNode('KeyValuePair');\n parseIdentifier(cursor);\n if (cursor.peekKind() === 'Equals') {\n cursor.bump();\n if (!parseExpression(cursor)) {\n cursor.diagnostic(\n 'PSL_INVALID_EXTENSION_BLOCK_MEMBER',\n 'Expected a value after \"=\"',\n cursor.mark(),\n );\n }\n }\n return cursor.finishNode();\n}\n"],"mappings":"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"}
|
package/dist/syntax.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { $ as Range, A as StringLiteralExprAst, B as printSyntax, C as AttributeArgAst, D as NumberLiteralExprAst, E as FunctionCallAst, F as BracedBlock, G as createSyntaxTree, H as SyntaxNode, I as any, J as GreenToken, K as GreenElement, L as filterChildren, M as QualifiedNameAst, N as IdentifierAst, O as ObjectFieldAst, P as AstNode, Q as Position, R as findChildToken, S as ArrayLiteralAst, T as ExpressionAst, U as SyntaxToken, V as SyntaxElement, W as TokenAtOffset, X as greenToken, Y as greenNode, Z as SyntaxKind, _ as TypesBlockAst, a as BlockMemberAst, b as FieldAttributeAst, c as DocumentAst, d as GenericBlockMemberAst, et as SourceFile, f as KeyValuePairAst, g as NamespaceMemberAst, h as NamespaceDeclarationAst, i as AttributeAst, j as castExpression, k as ObjectLiteralExprAst, l as FieldDeclarationAst, m as NamedTypeDeclarationAst, n as ParseResult, o as CompositeTypeDeclarationAst, p as ModelDeclarationAst, q as GreenNode, r as parse, s as DeclarationAst, t as ParseDiagnostic, u as GenericBlockDeclarationAst, v as TypeAnnotationAst, w as BooleanLiteralExprAst, x as ModelAttributeAst, y as AttributeArgListAst, z as findFirstChild } from "./parse-BKN_-hW8.mjs";
|
|
2
2
|
import { n as TokenKind } from "./tokenizer-DcYI0Xrq.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/syntax/green-builder.d.ts
|
|
@@ -9,5 +9,23 @@ declare class GreenNodeBuilder {
|
|
|
9
9
|
finishNode(): GreenNode;
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
|
-
|
|
12
|
+
//#region src/syntax/navigation.d.ts
|
|
13
|
+
/** Direction of a sibling/token walk. */
|
|
14
|
+
type Direction = 'next' | 'prev';
|
|
15
|
+
/** Whether a token kind is trivia (whitespace, newline, or comment). */
|
|
16
|
+
declare function isTriviaKind(kind: TokenKind): boolean;
|
|
17
|
+
/** Whether a token is trivia. */
|
|
18
|
+
declare function isTrivia(token: SyntaxToken): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The first non-trivia token at or beyond `token` in `direction`. Returns
|
|
21
|
+
* `token` itself when it is already significant.
|
|
22
|
+
*/
|
|
23
|
+
declare function skipTriviaToken(token: SyntaxToken, direction: Direction): SyntaxToken | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* The nearest sibling of `element` (within the same parent) in `direction` that
|
|
26
|
+
* is not a trivia token. Nodes are always significant.
|
|
27
|
+
*/
|
|
28
|
+
declare function nonTriviaSibling(element: SyntaxElement, direction: Direction): SyntaxElement | undefined;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { ArrayLiteralAst, type AstNode, AttributeArgAst, AttributeArgListAst, type AttributeAst, type BlockMemberAst, BooleanLiteralExprAst, type BracedBlock, CompositeTypeDeclarationAst, type DeclarationAst, type Direction, DocumentAst, type ExpressionAst, FieldAttributeAst, FieldDeclarationAst, FunctionCallAst, GenericBlockDeclarationAst, type GenericBlockMemberAst, type GreenElement, type GreenNode, GreenNodeBuilder, type GreenToken, IdentifierAst, KeyValuePairAst, ModelAttributeAst, ModelDeclarationAst, NamedTypeDeclarationAst, NamespaceDeclarationAst, type NamespaceMemberAst, NumberLiteralExprAst, ObjectFieldAst, ObjectLiteralExprAst, type ParseDiagnostic, type ParseResult, type Position, QualifiedNameAst, type Range, SourceFile, StringLiteralExprAst, type SyntaxElement, type SyntaxKind, SyntaxNode, SyntaxToken, TokenAtOffset, TypeAnnotationAst, TypesBlockAst, any, castExpression, createSyntaxTree, filterChildren, findChildToken, findFirstChild, greenNode, greenToken, isTrivia, isTriviaKind, nonTriviaSibling, parse, printSyntax, skipTriviaToken };
|
|
13
31
|
//# sourceMappingURL=syntax.d.mts.map
|
package/dist/syntax.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syntax.d.mts","names":[],"sources":["../src/syntax/green-builder.ts"],"mappings":";;;;cAKa,gBAAA;EAAA;EAGX,SAAA,CAAU,IAAA,EAAM,UAAA;EAIhB,KAAA,CAAM,IAAA,EAAM,SAAA,EAAW,IAAA;EAQvB,UAAA,IAAc,SAAA;AAAA"}
|
|
1
|
+
{"version":3,"file":"syntax.d.mts","names":[],"sources":["../src/syntax/green-builder.ts","../src/syntax/navigation.ts"],"mappings":";;;;cAKa,gBAAA;EAAA;EAGX,SAAA,CAAU,IAAA,EAAM,UAAA;EAIhB,KAAA,CAAM,IAAA,EAAM,SAAA,EAAW,IAAA;EAQvB,UAAA,IAAc,SAAA;AAAA;;;;KChBJ,SAAA;ADCZ;AAAA,iBCQgB,YAAA,CAAa,IAAe,EAAT,SAAS;;iBAK5B,QAAA,CAAS,KAAkB,EAAX,WAAW;;;;;iBAQ3B,eAAA,CAAgB,KAAA,EAAO,WAAA,EAAa,SAAA,EAAW,SAAA,GAAY,WAAA;;;;;iBAY3D,gBAAA,CACd,OAAA,EAAS,aAAA,EACT,SAAA,EAAW,SAAA,GACV,aAAA"}
|
package/dist/syntax.mjs
CHANGED
|
@@ -1,2 +1,43 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
|
|
1
|
+
import { A as SyntaxNode, C as QualifiedNameAst, D as findChildToken, E as filterChildren, M as TokenAtOffset, N as createSyntaxTree, O as findFirstChild, S as castExpression, T as any, _ as FunctionCallAst, a as KeyValuePairAst, b as ObjectLiteralExprAst, c as NamespaceDeclarationAst, d as AttributeArgListAst, f as FieldAttributeAst, g as BooleanLiteralExprAst, h as AttributeArgAst, i as GenericBlockDeclarationAst, j as SyntaxToken, k as printSyntax, l as TypesBlockAst, m as ArrayLiteralAst, n as DocumentAst, o as ModelDeclarationAst, p as ModelAttributeAst, r as FieldDeclarationAst, s as NamedTypeDeclarationAst, t as CompositeTypeDeclarationAst, u as TypeAnnotationAst, v as NumberLiteralExprAst, w as IdentifierAst, x as StringLiteralExprAst, y as ObjectFieldAst } from "./declarations-CPDuQm7x.mjs";
|
|
2
|
+
import { a as SourceFile, i as greenToken, n as GreenNodeBuilder, r as greenNode, t as parse } from "./parse-Du30Ca0-.mjs";
|
|
3
|
+
//#region src/syntax/navigation.ts
|
|
4
|
+
const TRIVIA_KINDS = new Set([
|
|
5
|
+
"Whitespace",
|
|
6
|
+
"Newline",
|
|
7
|
+
"Comment"
|
|
8
|
+
]);
|
|
9
|
+
/** Whether a token kind is trivia (whitespace, newline, or comment). */
|
|
10
|
+
function isTriviaKind(kind) {
|
|
11
|
+
return TRIVIA_KINDS.has(kind);
|
|
12
|
+
}
|
|
13
|
+
/** Whether a token is trivia. */
|
|
14
|
+
function isTrivia(token) {
|
|
15
|
+
return isTriviaKind(token.kind);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The first non-trivia token at or beyond `token` in `direction`. Returns
|
|
19
|
+
* `token` itself when it is already significant.
|
|
20
|
+
*/
|
|
21
|
+
function skipTriviaToken(token, direction) {
|
|
22
|
+
let current = token;
|
|
23
|
+
while (current !== void 0 && isTrivia(current)) current = direction === "next" ? current.nextToken : current.prevToken;
|
|
24
|
+
return current;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The nearest sibling of `element` (within the same parent) in `direction` that
|
|
28
|
+
* is not a trivia token. Nodes are always significant.
|
|
29
|
+
*/
|
|
30
|
+
function nonTriviaSibling(element, direction) {
|
|
31
|
+
let sibling = step(element, direction);
|
|
32
|
+
while (sibling !== void 0) {
|
|
33
|
+
if (!(sibling instanceof SyntaxToken) || !isTrivia(sibling)) return sibling;
|
|
34
|
+
sibling = step(sibling, direction);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function step(element, direction) {
|
|
38
|
+
return direction === "next" ? element.nextSiblingOrToken : element.prevSiblingOrToken;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { ArrayLiteralAst, AttributeArgAst, AttributeArgListAst, BooleanLiteralExprAst, CompositeTypeDeclarationAst, DocumentAst, FieldAttributeAst, FieldDeclarationAst, FunctionCallAst, GenericBlockDeclarationAst, GreenNodeBuilder, IdentifierAst, KeyValuePairAst, ModelAttributeAst, ModelDeclarationAst, NamedTypeDeclarationAst, NamespaceDeclarationAst, NumberLiteralExprAst, ObjectFieldAst, ObjectLiteralExprAst, QualifiedNameAst, SourceFile, StringLiteralExprAst, SyntaxNode, SyntaxToken, TokenAtOffset, TypeAnnotationAst, TypesBlockAst, any, castExpression, createSyntaxTree, filterChildren, findChildToken, findFirstChild, greenNode, greenToken, isTrivia, isTriviaKind, nonTriviaSibling, parse, printSyntax, skipTriviaToken };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=syntax.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syntax.mjs","names":[],"sources":["../src/syntax/navigation.ts"],"sourcesContent":["import type { TokenKind } from '../tokenizer';\nimport { type SyntaxElement, SyntaxToken } from './red';\n\n/** Direction of a sibling/token walk. */\nexport type Direction = 'next' | 'prev';\n\nconst TRIVIA_KINDS: ReadonlySet<TokenKind> = new Set<TokenKind>([\n 'Whitespace',\n 'Newline',\n 'Comment',\n]);\n\n/** Whether a token kind is trivia (whitespace, newline, or comment). */\nexport function isTriviaKind(kind: TokenKind): boolean {\n return TRIVIA_KINDS.has(kind);\n}\n\n/** Whether a token is trivia. */\nexport function isTrivia(token: SyntaxToken): boolean {\n return isTriviaKind(token.kind);\n}\n\n/**\n * The first non-trivia token at or beyond `token` in `direction`. Returns\n * `token` itself when it is already significant.\n */\nexport function skipTriviaToken(token: SyntaxToken, direction: Direction): SyntaxToken | undefined {\n let current: SyntaxToken | undefined = token;\n while (current !== undefined && isTrivia(current)) {\n current = direction === 'next' ? current.nextToken : current.prevToken;\n }\n return current;\n}\n\n/**\n * The nearest sibling of `element` (within the same parent) in `direction` that\n * is not a trivia token. Nodes are always significant.\n */\nexport function nonTriviaSibling(\n element: SyntaxElement,\n direction: Direction,\n): SyntaxElement | undefined {\n let sibling = step(element, direction);\n while (sibling !== undefined) {\n if (!(sibling instanceof SyntaxToken) || !isTrivia(sibling)) {\n return sibling;\n }\n sibling = step(sibling, direction);\n }\n return undefined;\n}\n\nfunction step(element: SyntaxElement, direction: Direction): SyntaxElement | undefined {\n return direction === 'next' ? element.nextSiblingOrToken : element.prevSiblingOrToken;\n}\n"],"mappings":";;;AAMA,MAAM,eAAuC,IAAI,IAAe;CAC9D;CACA;CACA;AACF,CAAC;;AAGD,SAAgB,aAAa,MAA0B;CACrD,OAAO,aAAa,IAAI,IAAI;AAC9B;;AAGA,SAAgB,SAAS,OAA6B;CACpD,OAAO,aAAa,MAAM,IAAI;AAChC;;;;;AAMA,SAAgB,gBAAgB,OAAoB,WAA+C;CACjG,IAAI,UAAmC;CACvC,OAAO,YAAY,KAAA,KAAa,SAAS,OAAO,GAC9C,UAAU,cAAc,SAAS,QAAQ,YAAY,QAAQ;CAE/D,OAAO;AACT;;;;;AAMA,SAAgB,iBACd,SACA,WAC2B;CAC3B,IAAI,UAAU,KAAK,SAAS,SAAS;CACrC,OAAO,YAAY,KAAA,GAAW;EAC5B,IAAI,EAAE,mBAAmB,gBAAgB,CAAC,SAAS,OAAO,GACxD,OAAO;EAET,UAAU,KAAK,SAAS,SAAS;CACnC;AAEF;AAEA,SAAS,KAAK,SAAwB,WAAiD;CACrF,OAAO,cAAc,SAAS,QAAQ,qBAAqB,QAAQ;AACrE"}
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/psl-parser",
|
|
3
|
-
"version": "0.14.0-dev.
|
|
3
|
+
"version": "0.14.0-dev.41",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "Reusable parser for Prisma Schema Language (PSL)",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/framework-components": "0.14.0-dev.
|
|
10
|
-
"@prisma-next/utils": "0.14.0-dev.
|
|
9
|
+
"@prisma-next/framework-components": "0.14.0-dev.41",
|
|
10
|
+
"@prisma-next/utils": "0.14.0-dev.41"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@prisma-next/tsconfig": "0.14.0-dev.
|
|
14
|
-
"@prisma-next/tsdown": "0.14.0-dev.
|
|
13
|
+
"@prisma-next/tsconfig": "0.14.0-dev.41",
|
|
14
|
+
"@prisma-next/tsdown": "0.14.0-dev.41",
|
|
15
15
|
"tsdown": "0.22.1",
|
|
16
16
|
"typescript": "5.9.3",
|
|
17
17
|
"vitest": "4.1.8"
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"types": "./dist/index.d.mts",
|
|
32
32
|
"exports": {
|
|
33
33
|
".": "./dist/index.mjs",
|
|
34
|
-
"./parser": "./dist/parser.mjs",
|
|
35
34
|
"./format": "./dist/format.mjs",
|
|
36
35
|
"./syntax": "./dist/syntax.mjs",
|
|
37
36
|
"./tokenizer": "./dist/tokenizer.mjs",
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { AuthoringPslBlockDescriptor } from '@prisma-next/framework-components/authoring';
|
|
2
|
+
import type {
|
|
3
|
+
PslBlockParam,
|
|
4
|
+
PslExtensionBlock,
|
|
5
|
+
PslExtensionBlockAttribute,
|
|
6
|
+
PslExtensionBlockParamValue,
|
|
7
|
+
PslSpan,
|
|
8
|
+
} from '@prisma-next/framework-components/psl-ast';
|
|
9
|
+
import type { ParseDiagnostic } from './parse';
|
|
10
|
+
import { nodePslSpan } from './resolve';
|
|
11
|
+
import type { SourceFile } from './source-file';
|
|
12
|
+
import type { GenericBlockDeclarationAst, KeyValuePairAst } from './syntax/ast/declarations';
|
|
13
|
+
import { ArrayLiteralAst, type ExpressionAst } from './syntax/ast/expressions';
|
|
14
|
+
import { printSyntax } from './syntax/ast-helpers';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Descriptor-free and unknown parameters become `value` stubs so validation can
|
|
18
|
+
* report them via key-set comparison. Duplicate member names are first-wins.
|
|
19
|
+
*/
|
|
20
|
+
export function reconstructExtensionBlock(
|
|
21
|
+
node: GenericBlockDeclarationAst,
|
|
22
|
+
descriptor: AuthoringPslBlockDescriptor | undefined,
|
|
23
|
+
sourceFile: SourceFile,
|
|
24
|
+
diagnostics: ParseDiagnostic[],
|
|
25
|
+
): PslExtensionBlock {
|
|
26
|
+
const keyword = node.keyword()?.text ?? '';
|
|
27
|
+
const blockName = node.name()?.name() ?? '';
|
|
28
|
+
|
|
29
|
+
const blockAttributes: PslExtensionBlockAttribute[] = [];
|
|
30
|
+
for (const attribute of node.attributes()) {
|
|
31
|
+
const name = attribute.name()?.path().join('.') ?? '';
|
|
32
|
+
const args = Array.from(attribute.argList()?.args() ?? [], (arg) => {
|
|
33
|
+
const value = arg.value();
|
|
34
|
+
return {
|
|
35
|
+
kind: 'positional' as const,
|
|
36
|
+
value: value === undefined ? '' : printSyntax(value.syntax).trim(),
|
|
37
|
+
span: nodePslSpan(arg.syntax, sourceFile),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
blockAttributes.push({
|
|
41
|
+
name,
|
|
42
|
+
args,
|
|
43
|
+
span: nodePslSpan(attribute.syntax, sourceFile),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const parameters: Record<string, PslExtensionBlockParamValue> = {};
|
|
48
|
+
for (const entry of node.entries()) {
|
|
49
|
+
const key = entry.key()?.name();
|
|
50
|
+
if (key === undefined) continue;
|
|
51
|
+
const span = nodePslSpan(entry.syntax, sourceFile);
|
|
52
|
+
if (Object.hasOwn(parameters, key)) {
|
|
53
|
+
diagnostics.push({
|
|
54
|
+
code: 'PSL_EXTENSION_DUPLICATE_PARAMETER',
|
|
55
|
+
message: `Duplicate parameter "${key}" in "${keyword}" block "${blockName}"; first occurrence wins`,
|
|
56
|
+
range: {
|
|
57
|
+
start: sourceFile.positionAt(entry.syntax.offset),
|
|
58
|
+
end: sourceFile.positionAt(entry.syntax.offset + entry.syntax.green.textLength),
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
parameters[key] = reconstructParamValue(
|
|
64
|
+
entry,
|
|
65
|
+
descriptor?.parameters[key],
|
|
66
|
+
span,
|
|
67
|
+
sourceFile,
|
|
68
|
+
diagnostics,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
kind: descriptor?.discriminator ?? keyword,
|
|
74
|
+
name: blockName,
|
|
75
|
+
parameters,
|
|
76
|
+
blockAttributes,
|
|
77
|
+
span: nodePslSpan(node.syntax, sourceFile),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function reconstructParamValue(
|
|
82
|
+
entry: KeyValuePairAst,
|
|
83
|
+
param: PslBlockParam | undefined,
|
|
84
|
+
span: PslSpan,
|
|
85
|
+
sourceFile: SourceFile,
|
|
86
|
+
diagnostics: ParseDiagnostic[],
|
|
87
|
+
): PslExtensionBlockParamValue {
|
|
88
|
+
const value = entry.value();
|
|
89
|
+
if (value === undefined) {
|
|
90
|
+
return { kind: 'bare', span };
|
|
91
|
+
}
|
|
92
|
+
return reconstructFromExpression(value, param, span, sourceFile, diagnostics);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function reconstructFromExpression(
|
|
96
|
+
value: ExpressionAst,
|
|
97
|
+
param: PslBlockParam | undefined,
|
|
98
|
+
span: PslSpan,
|
|
99
|
+
sourceFile: SourceFile,
|
|
100
|
+
diagnostics?: ParseDiagnostic[],
|
|
101
|
+
): PslExtensionBlockParamValue {
|
|
102
|
+
const raw = printSyntax(value.syntax).trim();
|
|
103
|
+
if (param?.kind === 'list') {
|
|
104
|
+
const array = ArrayLiteralAst.cast(value.syntax);
|
|
105
|
+
if (!array) {
|
|
106
|
+
diagnostics?.push({
|
|
107
|
+
code: 'PSL_EXTENSION_INVALID_VALUE',
|
|
108
|
+
message: `List parameter expects an array literal, got ${raw}`,
|
|
109
|
+
range: {
|
|
110
|
+
start: sourceFile.positionAt(value.syntax.offset),
|
|
111
|
+
end: sourceFile.positionAt(value.syntax.offset + value.syntax.green.textLength),
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
return { kind: 'value', raw, span };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const items: PslExtensionBlockParamValue[] = [];
|
|
118
|
+
for (const element of array.elements()) {
|
|
119
|
+
items.push(
|
|
120
|
+
reconstructFromExpression(
|
|
121
|
+
element,
|
|
122
|
+
param.of,
|
|
123
|
+
nodePslSpan(element.syntax, sourceFile),
|
|
124
|
+
sourceFile,
|
|
125
|
+
diagnostics,
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return { kind: 'list', items, span };
|
|
130
|
+
}
|
|
131
|
+
switch (param?.kind) {
|
|
132
|
+
case 'ref':
|
|
133
|
+
return { kind: 'ref', identifier: raw, span };
|
|
134
|
+
case 'option':
|
|
135
|
+
return { kind: 'option', token: raw, span };
|
|
136
|
+
default:
|
|
137
|
+
return { kind: 'value', raw, span };
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/exports/index.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
export type {
|
|
2
|
-
ParsePslDocumentInput,
|
|
3
|
-
ParsePslDocumentResult,
|
|
4
2
|
PslAttribute,
|
|
5
3
|
PslAttributeArgument,
|
|
6
4
|
PslAttributeNamedArgument,
|
|
@@ -38,4 +36,30 @@ export {
|
|
|
38
36
|
namespacePslExtensionBlocks,
|
|
39
37
|
} from '@prisma-next/framework-components/psl-ast';
|
|
40
38
|
export { getPositionalArgument, parseQuotedStringLiteral } from '../attribute-helpers';
|
|
41
|
-
export {
|
|
39
|
+
export { findBlockDescriptor, validateExtensionBlockFromSymbol } from '../extension-block';
|
|
40
|
+
export {
|
|
41
|
+
keywordPslSpan,
|
|
42
|
+
nodePslSpan,
|
|
43
|
+
rangeToPslSpan,
|
|
44
|
+
readResolvedAttribute,
|
|
45
|
+
readResolvedAttributes,
|
|
46
|
+
readResolvedConstructorCall,
|
|
47
|
+
} from '../resolve';
|
|
48
|
+
export type {
|
|
49
|
+
BlockSymbol,
|
|
50
|
+
BuildSymbolTableOptions,
|
|
51
|
+
CompositeTypeSymbol,
|
|
52
|
+
FieldSymbol,
|
|
53
|
+
ModelSymbol,
|
|
54
|
+
NamespaceSymbol,
|
|
55
|
+
ResolvedAttribute,
|
|
56
|
+
ResolvedAttributeArg,
|
|
57
|
+
ResolvedNamedTypeBinding,
|
|
58
|
+
ResolvedTypeConstructorCall,
|
|
59
|
+
ScalarSymbol,
|
|
60
|
+
SymbolTable,
|
|
61
|
+
SymbolTableResult,
|
|
62
|
+
TopLevelScope,
|
|
63
|
+
TypeAliasSymbol,
|
|
64
|
+
} from '../symbol-table';
|
|
65
|
+
export { buildSymbolTable } from '../symbol-table';
|
package/src/exports/syntax.ts
CHANGED
|
@@ -7,7 +7,13 @@ export {
|
|
|
7
7
|
FieldAttributeAst,
|
|
8
8
|
ModelAttributeAst,
|
|
9
9
|
} from '../syntax/ast/attributes';
|
|
10
|
-
export type {
|
|
10
|
+
export type {
|
|
11
|
+
AttributeAst,
|
|
12
|
+
BlockMemberAst,
|
|
13
|
+
DeclarationAst,
|
|
14
|
+
GenericBlockMemberAst,
|
|
15
|
+
NamespaceMemberAst,
|
|
16
|
+
} from '../syntax/ast/declarations';
|
|
11
17
|
export {
|
|
12
18
|
CompositeTypeDeclarationAst,
|
|
13
19
|
DocumentAst,
|
|
@@ -35,12 +41,26 @@ export {
|
|
|
35
41
|
export { IdentifierAst } from '../syntax/ast/identifier';
|
|
36
42
|
export { QualifiedNameAst } from '../syntax/ast/qualified-name';
|
|
37
43
|
export { TypeAnnotationAst } from '../syntax/ast/type-annotation';
|
|
38
|
-
export type { AstNode } from '../syntax/ast-helpers';
|
|
39
|
-
export {
|
|
44
|
+
export type { AstNode, BracedBlock } from '../syntax/ast-helpers';
|
|
45
|
+
export {
|
|
46
|
+
any,
|
|
47
|
+
filterChildren,
|
|
48
|
+
findChildToken,
|
|
49
|
+
findFirstChild,
|
|
50
|
+
printSyntax,
|
|
51
|
+
} from '../syntax/ast-helpers';
|
|
40
52
|
export type { GreenElement, GreenNode, GreenToken } from '../syntax/green';
|
|
41
53
|
export { greenNode, greenToken } from '../syntax/green';
|
|
42
54
|
export { GreenNodeBuilder } from '../syntax/green-builder';
|
|
55
|
+
// Navigation helpers
|
|
56
|
+
export type { Direction } from '../syntax/navigation';
|
|
57
|
+
export {
|
|
58
|
+
isTrivia,
|
|
59
|
+
isTriviaKind,
|
|
60
|
+
nonTriviaSibling,
|
|
61
|
+
skipTriviaToken,
|
|
62
|
+
} from '../syntax/navigation';
|
|
43
63
|
// Red layer
|
|
44
|
-
export type { SyntaxElement
|
|
45
|
-
export { createSyntaxTree, SyntaxNode } from '../syntax/red';
|
|
64
|
+
export type { SyntaxElement } from '../syntax/red';
|
|
65
|
+
export { createSyntaxTree, SyntaxNode, SyntaxToken, TokenAtOffset } from '../syntax/red';
|
|
46
66
|
export type { SyntaxKind } from '../syntax/syntax-kind';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AuthoringPslBlockDescriptor,
|
|
3
|
+
type AuthoringPslBlockDescriptorNamespace,
|
|
4
|
+
isAuthoringPslBlockDescriptor,
|
|
5
|
+
} from '@prisma-next/framework-components/authoring';
|
|
6
|
+
import type { CodecLookup } from '@prisma-next/framework-components/codec';
|
|
7
|
+
import {
|
|
8
|
+
makePslNamespace,
|
|
9
|
+
makePslNamespaceEntries,
|
|
10
|
+
type PslDiagnostic,
|
|
11
|
+
type PslModel,
|
|
12
|
+
type PslSpan,
|
|
13
|
+
UNSPECIFIED_PSL_NAMESPACE_ID,
|
|
14
|
+
validateExtensionBlock,
|
|
15
|
+
} from '@prisma-next/framework-components/psl-ast';
|
|
16
|
+
import type { SourceFile } from './source-file';
|
|
17
|
+
import type { BlockSymbol, ModelSymbol, SymbolTable } from './symbol-table';
|
|
18
|
+
|
|
19
|
+
export function findBlockDescriptor(
|
|
20
|
+
descriptors: AuthoringPslBlockDescriptorNamespace | undefined,
|
|
21
|
+
keyword: string,
|
|
22
|
+
): AuthoringPslBlockDescriptor | undefined {
|
|
23
|
+
if (descriptors === undefined) return undefined;
|
|
24
|
+
for (const value of Object.values(descriptors)) {
|
|
25
|
+
if (value === undefined) continue;
|
|
26
|
+
if (isAuthoringPslBlockDescriptor(value)) {
|
|
27
|
+
if (value.keyword === keyword) return value;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const nested = findBlockDescriptor(value, keyword);
|
|
31
|
+
if (nested !== undefined) return nested;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function validateExtensionBlockFromSymbol(input: {
|
|
37
|
+
readonly block: BlockSymbol;
|
|
38
|
+
readonly descriptor: AuthoringPslBlockDescriptor;
|
|
39
|
+
readonly symbolTable: SymbolTable;
|
|
40
|
+
readonly sourceFile: SourceFile;
|
|
41
|
+
readonly sourceId: string;
|
|
42
|
+
readonly codecLookup: CodecLookup;
|
|
43
|
+
}): readonly PslDiagnostic[] {
|
|
44
|
+
const refCtx = buildRefResolutionContext(input.symbolTable, input.block);
|
|
45
|
+
return validateExtensionBlock(
|
|
46
|
+
input.block.block,
|
|
47
|
+
input.descriptor,
|
|
48
|
+
input.sourceId,
|
|
49
|
+
input.codecLookup,
|
|
50
|
+
refCtx,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const ZERO_SPAN: PslSpan = {
|
|
55
|
+
start: { offset: 0, line: 1, column: 1 },
|
|
56
|
+
end: { offset: 0, line: 1, column: 1 },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function buildRefResolutionContext(
|
|
60
|
+
symbolTable: SymbolTable,
|
|
61
|
+
block: BlockSymbol,
|
|
62
|
+
): {
|
|
63
|
+
ownerNamespace: ReturnType<typeof makePslNamespace>;
|
|
64
|
+
allNamespaces: readonly ReturnType<typeof makePslNamespace>[];
|
|
65
|
+
} {
|
|
66
|
+
const unspecifiedNamespace = makeNamespace(
|
|
67
|
+
UNSPECIFIED_PSL_NAMESPACE_ID,
|
|
68
|
+
Object.values(symbolTable.topLevel.models),
|
|
69
|
+
);
|
|
70
|
+
const namedNamespaces = Object.values(symbolTable.topLevel.namespaces).map((namespace) =>
|
|
71
|
+
makeNamespace(namespace.name, Object.values(namespace.models)),
|
|
72
|
+
);
|
|
73
|
+
const allNamespaces = [unspecifiedNamespace, ...namedNamespaces];
|
|
74
|
+
const ownerNamespaceName = findOwnerNamespaceName(symbolTable, block);
|
|
75
|
+
const ownerNamespace =
|
|
76
|
+
allNamespaces.find((namespace) => namespace.name === ownerNamespaceName) ??
|
|
77
|
+
unspecifiedNamespace;
|
|
78
|
+
return { ownerNamespace, allNamespaces };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function makeNamespace(
|
|
82
|
+
name: string,
|
|
83
|
+
models: readonly ModelSymbol[],
|
|
84
|
+
): ReturnType<typeof makePslNamespace> {
|
|
85
|
+
const modelStubs: PslModel[] = models.map((model) => ({
|
|
86
|
+
kind: 'model',
|
|
87
|
+
name: model.name,
|
|
88
|
+
fields: [],
|
|
89
|
+
attributes: [],
|
|
90
|
+
span: ZERO_SPAN,
|
|
91
|
+
}));
|
|
92
|
+
return makePslNamespace({
|
|
93
|
+
kind: 'namespace',
|
|
94
|
+
name,
|
|
95
|
+
entries: makePslNamespaceEntries(modelStubs, [], []),
|
|
96
|
+
span: ZERO_SPAN,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function findOwnerNamespaceName(symbolTable: SymbolTable, block: BlockSymbol): string {
|
|
101
|
+
for (const namespace of Object.values(symbolTable.topLevel.namespaces)) {
|
|
102
|
+
if (Object.values(namespace.blocks).some((candidate) => candidate === block)) {
|
|
103
|
+
return namespace.name;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return UNSPECIFIED_PSL_NAMESPACE_ID;
|
|
107
|
+
}
|
package/src/parse.ts
CHANGED
|
@@ -399,14 +399,24 @@ function parseParenArgs(cursor: Cursor): void {
|
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
-
export function parseAttributeArg(cursor: Cursor):
|
|
402
|
+
export function parseAttributeArg(cursor: Cursor): void {
|
|
403
|
+
const kind = cursor.peekKind();
|
|
404
|
+
if (
|
|
405
|
+
kind !== 'Ident' &&
|
|
406
|
+
kind !== 'StringLiteral' &&
|
|
407
|
+
kind !== 'NumberLiteral' &&
|
|
408
|
+
kind !== 'LBracket' &&
|
|
409
|
+
kind !== 'LBrace'
|
|
410
|
+
) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
403
413
|
cursor.startNode('AttributeArg');
|
|
404
414
|
if (cursor.peekKind() === 'Ident' && cursor.peekKind(1) === 'Colon') {
|
|
405
415
|
parseIdentifier(cursor);
|
|
406
416
|
cursor.bump();
|
|
407
417
|
}
|
|
408
418
|
parseArgValue(cursor);
|
|
409
|
-
|
|
419
|
+
cursor.finishNode();
|
|
410
420
|
}
|
|
411
421
|
|
|
412
422
|
function parseArgValue(cursor: Cursor): void {
|
|
@@ -435,8 +445,16 @@ export function parseAttribute(cursor: Cursor): GreenNode {
|
|
|
435
445
|
return cursor.finishNode();
|
|
436
446
|
}
|
|
437
447
|
|
|
438
|
-
/**
|
|
439
|
-
|
|
448
|
+
/**
|
|
449
|
+
* A type annotation: `QualifiedName (argList)? ([])? (?)?`, e.g.
|
|
450
|
+
* `pgvector.Vector(1536)[]?`. When the field has no type, no node is emitted —
|
|
451
|
+
* a missing type is the absence of a `TypeAnnotation`, not a zero-width one.
|
|
452
|
+
*/
|
|
453
|
+
export function parseTypeAnnotation(cursor: Cursor): void {
|
|
454
|
+
const kind = cursor.peekKind();
|
|
455
|
+
if (kind !== 'Ident' && kind !== 'LBracket' && kind !== 'Question') {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
440
458
|
cursor.startNode('TypeAnnotation');
|
|
441
459
|
if (cursor.peekKind() === 'Ident') {
|
|
442
460
|
parseQualifiedName(cursor);
|
|
@@ -453,7 +471,7 @@ export function parseTypeAnnotation(cursor: Cursor): GreenNode {
|
|
|
453
471
|
if (cursor.peekKind() === 'Question') {
|
|
454
472
|
cursor.bump();
|
|
455
473
|
}
|
|
456
|
-
|
|
474
|
+
cursor.finishNode();
|
|
457
475
|
}
|
|
458
476
|
|
|
459
477
|
type MemberParser = (cursor: Cursor) => void;
|