Skip to content

Commit e682b72

Browse files
SevInfclaude
andcommitted
refactor(scalar-arrays): expose raw expression AST on ResolvedAttributeArg (slice 2 N1)
Replace the decoded ResolvedExpr union with the parser ExpressionAst node directly on ResolvedAttributeArg, per code-owner review. List-default parsing narrows via the AST classes (ArrayLiteralAst/StringLiteralExprAst/...) instead of a bespoke decoded shape. The stringified value is kept, as it is still consumed by scalar/function-call/relation-name/authoring-arg parsing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
1 parent 34d9a92 commit e682b72

5 files changed

Lines changed: 30 additions & 79 deletions

File tree

packages/1-framework/2-authoring/psl-parser/src/exports/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export type {
5454
NamespaceSymbol,
5555
ResolvedAttribute,
5656
ResolvedAttributeArg,
57-
ResolvedExpr,
5857
ResolvedNamedTypeBinding,
5958
ResolvedTypeConstructorCall,
6059
ScalarSymbol,

packages/1-framework/2-authoring/psl-parser/src/resolve.ts

Lines changed: 4 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,17 @@ import type {
55
FieldAttributeAst,
66
ModelAttributeAst,
77
} from './syntax/ast/attributes';
8-
import {
9-
ArrayLiteralAst,
10-
BooleanLiteralExprAst,
11-
type ExpressionAst,
12-
FunctionCallAst,
13-
NumberLiteralExprAst,
14-
ObjectLiteralExprAst,
15-
StringLiteralExprAst,
16-
} from './syntax/ast/expressions';
17-
import { IdentifierAst } from './syntax/ast/identifier';
8+
import type { ExpressionAst } from './syntax/ast/expressions';
189
import type { QualifiedNameAst } from './syntax/ast/qualified-name';
1910
import type { TypeAnnotationAst } from './syntax/ast/type-annotation';
2011
import { printSyntax } from './syntax/ast-helpers';
2112
import type { SyntaxNode } from './syntax/red';
2213

23-
/**
24-
* A structurally decoded attribute-argument expression. Consumers that need the
25-
* shape of an expression (e.g. array-literal `@default([...])`) read this instead
26-
* of re-parsing the stringified {@link ResolvedAttributeArg.value}.
27-
*/
28-
export type ResolvedExpr =
29-
| { readonly kind: 'string'; readonly value: string }
30-
| { readonly kind: 'number'; readonly value: number }
31-
| { readonly kind: 'boolean'; readonly value: boolean }
32-
| { readonly kind: 'array'; readonly elements: readonly ResolvedExpr[] }
33-
| { readonly kind: 'object' }
34-
| { readonly kind: 'call'; readonly path: readonly string[] }
35-
| { readonly kind: 'identifier'; readonly name: string };
36-
3714
export interface ResolvedAttributeArg {
3815
readonly kind: 'positional' | 'named';
3916
readonly name?: string;
4017
readonly value: string;
41-
readonly expression?: ResolvedExpr;
18+
readonly expression?: ExpressionAst;
4219
readonly span: PslSpan;
4320
}
4421

@@ -93,55 +70,18 @@ function readResolvedArgList(
9370
const args: ResolvedAttributeArg[] = [];
9471
for (const arg of argList.args()) {
9572
const name = arg.name()?.name();
96-
const value = arg.value();
97-
const expression = decodeExpression(value);
73+
const expression = arg.value();
9874
args.push({
9975
kind: name !== undefined ? 'named' : 'positional',
10076
...(name !== undefined ? { name } : {}),
101-
value: renderExpression(value),
77+
value: renderExpression(expression),
10278
...(expression !== undefined ? { expression } : {}),
10379
span: nodePslSpan(arg.syntax, sourceFile),
10480
});
10581
}
10682
return args;
10783
}
10884

109-
function decodeExpression(expression: ExpressionAst | undefined): ResolvedExpr | undefined {
110-
if (expression === undefined) return undefined;
111-
if (expression instanceof StringLiteralExprAst) {
112-
const value = expression.value();
113-
return value === undefined ? undefined : { kind: 'string', value };
114-
}
115-
if (expression instanceof NumberLiteralExprAst) {
116-
const value = expression.value();
117-
return value === undefined ? undefined : { kind: 'number', value };
118-
}
119-
if (expression instanceof BooleanLiteralExprAst) {
120-
const value = expression.value();
121-
return value === undefined ? undefined : { kind: 'boolean', value };
122-
}
123-
if (expression instanceof ArrayLiteralAst) {
124-
const elements: ResolvedExpr[] = [];
125-
for (const element of expression.elements()) {
126-
const decoded = decodeExpression(element);
127-
if (decoded === undefined) return undefined;
128-
elements.push(decoded);
129-
}
130-
return { kind: 'array', elements };
131-
}
132-
if (expression instanceof FunctionCallAst) {
133-
return { kind: 'call', path: expression.path() };
134-
}
135-
if (expression instanceof IdentifierAst) {
136-
const name = expression.name();
137-
return name === undefined ? undefined : { kind: 'identifier', name };
138-
}
139-
if (expression instanceof ObjectLiteralExprAst) {
140-
return { kind: 'object' };
141-
}
142-
return undefined;
143-
}
144-
14585
function attributeName(name: QualifiedNameAst | undefined): string {
14686
return name?.path().join('.') ?? '';
14787
}

packages/1-framework/2-authoring/psl-parser/src/symbol-table.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import type { SyntaxNode } from './syntax/red';
2727
export type {
2828
ResolvedAttribute,
2929
ResolvedAttributeArg,
30-
ResolvedExpr,
3130
ResolvedTypeConstructorCall,
3231
} from './resolve';
3332

packages/2-sql/2-authoring/contract-psl/src/psl-attribute-parsing.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { ContractSourceDiagnostic } from '@prisma-next/config/config-types';
22
import type { ControlPolicy } from '@prisma-next/contract/types';
3-
import type { PslSpan, ResolvedAttribute, ResolvedExpr } from '@prisma-next/psl-parser';
3+
import type { PslSpan, ResolvedAttribute } from '@prisma-next/psl-parser';
44
import { parseQuotedStringLiteral } from '@prisma-next/psl-parser';
5+
import type { ExpressionAst } from '@prisma-next/psl-parser/syntax';
56

67
export { parseQuotedStringLiteral };
78

@@ -32,7 +33,7 @@ export function getNamedArgument(attribute: ResolvedAttribute, name: string): st
3233
export function getPositionalArgumentEntry(
3334
attribute: ResolvedAttribute,
3435
index = 0,
35-
): { value: string; expression?: ResolvedExpr; span: PslSpan } | undefined {
36+
): { value: string; expression?: ExpressionAst; span: PslSpan } | undefined {
3637
const entries = attribute.args.filter((arg) => arg.kind === 'positional');
3738
const entry = entries[index];
3839
if (!entry || entry.kind !== 'positional') {

packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ import type {
2323
FieldSymbol,
2424
PslSpan,
2525
ResolvedAttribute,
26-
ResolvedExpr,
2726
ResolvedTypeConstructorCall,
2827
} from '@prisma-next/psl-parser';
28+
import {
29+
ArrayLiteralAst,
30+
BooleanLiteralExprAst,
31+
type ExpressionAst,
32+
NumberLiteralExprAst,
33+
StringLiteralExprAst,
34+
} from '@prisma-next/psl-parser/syntax';
2935
import { blindCast } from '@prisma-next/utils/casts';
3036

3137
import {
@@ -718,22 +724,28 @@ type ListDefaultParse =
718724
| { readonly kind: 'scalar' }
719725
| undefined;
720726

721-
function parseListDefaultExpression(expression: ResolvedExpr | undefined): ListDefaultParse {
727+
function decodeLiteralElement(element: ExpressionAst): string | number | boolean | undefined {
728+
if (element instanceof StringLiteralExprAst) return element.value();
729+
if (element instanceof NumberLiteralExprAst) return element.value();
730+
if (element instanceof BooleanLiteralExprAst) return element.value();
731+
return undefined;
732+
}
733+
734+
function parseListDefaultExpression(expression: ExpressionAst | undefined): ListDefaultParse {
722735
if (expression === undefined) return undefined;
723736
if (
724-
expression.kind === 'string' ||
725-
expression.kind === 'number' ||
726-
expression.kind === 'boolean'
737+
expression instanceof StringLiteralExprAst ||
738+
expression instanceof NumberLiteralExprAst ||
739+
expression instanceof BooleanLiteralExprAst
727740
) {
728741
return { kind: 'scalar' };
729742
}
730-
if (expression.kind !== 'array') return undefined;
743+
if (!(expression instanceof ArrayLiteralAst)) return undefined;
731744
const value: (string | number | boolean)[] = [];
732-
for (const element of expression.elements) {
733-
if (element.kind !== 'string' && element.kind !== 'number' && element.kind !== 'boolean') {
734-
return undefined;
735-
}
736-
value.push(element.value);
745+
for (const element of expression.elements()) {
746+
const decoded = decodeLiteralElement(element);
747+
if (decoded === undefined) return undefined;
748+
value.push(decoded);
737749
}
738750
return { kind: 'array', value };
739751
}

0 commit comments

Comments
 (0)