| |
| |
|
|
| import { SimplifyDeep } from '../types/types' |
| import { JsonPathToAccessor } from './utils' |
|
|
| |
| |
| |
| |
| |
| |
| |
| export type ParseQuery<Query extends string> = string extends Query |
| ? GenericStringError |
| : ParseNodes<EatWhitespace<Query>> extends [infer Nodes, `${infer Remainder}`] |
| ? Nodes extends Ast.Node[] |
| ? EatWhitespace<Remainder> extends '' |
| ? SimplifyDeep<Nodes> |
| : ParserError<`Unexpected input: ${Remainder}`> |
| : ParserError<'Invalid nodes array structure'> |
| : ParseNodes<EatWhitespace<Query>> |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| type ParseNodes<Input extends string> = string extends Input |
| ? GenericStringError |
| : ParseNodesHelper<Input, []> |
|
|
| type ParseNodesHelper<Input extends string, Nodes extends Ast.Node[]> = |
| ParseNode<Input> extends [infer Node, `${infer Remainder}`] |
| ? Node extends Ast.Node |
| ? EatWhitespace<Remainder> extends `,${infer Remainder}` |
| ? ParseNodesHelper<EatWhitespace<Remainder>, [...Nodes, Node]> |
| : [[...Nodes, Node], EatWhitespace<Remainder>] |
| : ParserError<'Invalid node type in nodes helper'> |
| : ParseNode<Input> |
| |
| |
| |
| |
| |
| |
| |
| |
| type ParseNode<Input extends string> = Input extends '' |
| ? ParserError<'Empty string'> |
| : |
| Input extends `*${infer Remainder}` |
| ? [Ast.StarNode, EatWhitespace<Remainder>] |
| : |
| Input extends `...${infer Remainder}` |
| ? ParseField<EatWhitespace<Remainder>> extends [infer TargetField, `${infer Remainder}`] |
| ? TargetField extends Ast.FieldNode |
| ? [{ type: 'spread'; target: TargetField }, EatWhitespace<Remainder>] |
| : ParserError<'Invalid target field type in spread'> |
| : ParserError<`Unable to parse spread resource at \`${Input}\``> |
| : ParseIdentifier<Input> extends [infer NameOrAlias, `${infer Remainder}`] |
| ? EatWhitespace<Remainder> extends `::${infer _}` |
| ? |
| ParseField<Input> |
| : EatWhitespace<Remainder> extends `:${infer Remainder}` |
| ? |
| ParseField<EatWhitespace<Remainder>> extends [infer Field, `${infer Remainder}`] |
| ? Field extends Ast.FieldNode |
| ? [Omit<Field, 'alias'> & { alias: NameOrAlias }, EatWhitespace<Remainder>] |
| : ParserError<'Invalid field type in alias parsing'> |
| : ParserError<`Unable to parse renamed field at \`${Input}\``> |
| : |
| ParseField<Input> |
| : ParserError<`Expected identifier at \`${Input}\``> |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| type ParseField<Input extends string> = Input extends '' |
| ? ParserError<'Empty string'> |
| : ParseIdentifier<Input> extends [infer Name, `${infer Remainder}`] |
| ? Name extends 'count' |
| ? ParseCountField<Input> |
| : Remainder extends `!inner${infer Remainder}` |
| ? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [ |
| infer Children, |
| `${infer Remainder}`, |
| ] |
| ? Children extends Ast.Node[] |
| ? |
| [{ type: 'field'; name: Name; innerJoin: true; children: Children }, Remainder] |
| : ParserError<'Invalid children array in inner join'> |
| : CreateParserErrorIfRequired< |
| ParseEmbeddedResource<EatWhitespace<Remainder>>, |
| `Expected embedded resource after "!inner" at \`${Remainder}\`` |
| > |
| : EatWhitespace<Remainder> extends `!left${infer Remainder}` |
| ? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [ |
| infer Children, |
| `${infer Remainder}`, |
| ] |
| ? Children extends Ast.Node[] |
| ? |
| |
| [{ type: 'field'; name: Name; children: Children }, EatWhitespace<Remainder>] |
| : ParserError<'Invalid children array in left join'> |
| : CreateParserErrorIfRequired< |
| ParseEmbeddedResource<EatWhitespace<Remainder>>, |
| `Expected embedded resource after "!left" at \`${EatWhitespace<Remainder>}\`` |
| > |
| : EatWhitespace<Remainder> extends `!${infer Remainder}` |
| ? ParseIdentifier<EatWhitespace<Remainder>> extends [infer Hint, `${infer Remainder}`] |
| ? EatWhitespace<Remainder> extends `!inner${infer Remainder}` |
| ? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [ |
| infer Children, |
| `${infer Remainder}`, |
| ] |
| ? Children extends Ast.Node[] |
| ? |
| [ |
| { |
| type: 'field' |
| name: Name |
| hint: Hint |
| innerJoin: true |
| children: Children |
| }, |
| EatWhitespace<Remainder>, |
| ] |
| : ParserError<'Invalid children array in hint inner join'> |
| : ParseEmbeddedResource<EatWhitespace<Remainder>> |
| : ParseEmbeddedResource<EatWhitespace<Remainder>> extends [ |
| infer Children, |
| `${infer Remainder}`, |
| ] |
| ? Children extends Ast.Node[] |
| ? |
| [ |
| { type: 'field'; name: Name; hint: Hint; children: Children }, |
| EatWhitespace<Remainder>, |
| ] |
| : ParserError<'Invalid children array in hint'> |
| : ParseEmbeddedResource<EatWhitespace<Remainder>> |
| : ParserError<`Expected identifier after "!" at \`${EatWhitespace<Remainder>}\``> |
| : EatWhitespace<Remainder> extends `(${infer _}` |
| ? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [ |
| infer Children, |
| `${infer Remainder}`, |
| ] |
| ? Children extends Ast.Node[] |
| ? |
| [{ type: 'field'; name: Name; children: Children }, EatWhitespace<Remainder>] |
| : ParserError<'Invalid children array in field'> |
| : |
| ParseEmbeddedResource<EatWhitespace<Remainder>> |
| : |
| ParseNonEmbeddedResourceField<Input> |
| : ParserError<`Expected identifier at \`${Input}\``> |
|
|
| type ParseCountField<Input extends string> = |
| ParseIdentifier<Input> extends ['count', `${infer Remainder}`] |
| ? ( |
| EatWhitespace<Remainder> extends `()${infer Remainder_}` |
| ? EatWhitespace<Remainder_> |
| : EatWhitespace<Remainder> |
| ) extends `${infer Remainder}` |
| ? Remainder extends `::${infer _}` |
| ? ParseFieldTypeCast<Remainder> extends [infer CastType, `${infer Remainder}`] |
| ? [ |
| { type: 'field'; name: 'count'; aggregateFunction: 'count'; castType: CastType }, |
| Remainder, |
| ] |
| : ParseFieldTypeCast<Remainder> |
| : [{ type: 'field'; name: 'count'; aggregateFunction: 'count' }, Remainder] |
| : never |
| : ParserError<`Expected "count" at \`${Input}\``> |
|
|
| |
| |
| |
| |
| |
| |
| |
| type ParseEmbeddedResource<Input extends string> = Input extends `(${infer Remainder}` |
| ? EatWhitespace<Remainder> extends `)${infer Remainder}` |
| ? [[], EatWhitespace<Remainder>] |
| : ParseNodes<EatWhitespace<Remainder>> extends [infer Nodes, `${infer Remainder}`] |
| ? Nodes extends Ast.Node[] |
| ? EatWhitespace<Remainder> extends `)${infer Remainder}` |
| ? [Nodes, EatWhitespace<Remainder>] |
| : ParserError<`Expected ")" at \`${EatWhitespace<Remainder>}\``> |
| : ParserError<'Invalid nodes array in embedded resource'> |
| : ParseNodes<EatWhitespace<Remainder>> |
| : ParserError<`Expected "(" at \`${Input}\``> |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| type ParseNonEmbeddedResourceField<Input extends string> = |
| ParseIdentifier<Input> extends [infer Name, `${infer Remainder}`] |
| ? |
| ( |
| Remainder extends `->${infer PathAndRest}` |
| ? ParseJsonAccessor<Remainder> extends [ |
| infer PropertyName, |
| infer PropertyType, |
| `${infer Remainder}`, |
| ] |
| ? [ |
| { |
| type: 'field' |
| name: Name |
| alias: PropertyName |
| castType: PropertyType |
| jsonPath: JsonPathToAccessor< |
| PathAndRest extends `${infer Path},${string}` ? Path : PathAndRest |
| > |
| }, |
| Remainder, |
| ] |
| : ParseJsonAccessor<Remainder> |
| : [{ type: 'field'; name: Name }, Remainder] |
| ) extends infer Parsed |
| ? Parsed extends [infer Field, `${infer Remainder}`] |
| ? |
| ( |
| Remainder extends `::${infer _}` |
| ? ParseFieldTypeCast<Remainder> extends [infer CastType, `${infer Remainder}`] |
| ? [Omit<Field, 'castType'> & { castType: CastType }, Remainder] |
| : ParseFieldTypeCast<Remainder> |
| : [Field, Remainder] |
| ) extends infer Parsed |
| ? Parsed extends [infer Field, `${infer Remainder}`] |
| ? |
| Remainder extends `.${infer _}` |
| ? ParseFieldAggregation<Remainder> extends [ |
| infer AggregateFunction, |
| `${infer Remainder}`, |
| ] |
| ? |
| Remainder extends `::${infer _}` |
| ? ParseFieldTypeCast<Remainder> extends [infer CastType, `${infer Remainder}`] |
| ? [ |
| Omit<Field, 'castType'> & { |
| aggregateFunction: AggregateFunction |
| castType: CastType |
| }, |
| Remainder, |
| ] |
| : ParseFieldTypeCast<Remainder> |
| : [Field & { aggregateFunction: AggregateFunction }, Remainder] |
| : ParseFieldAggregation<Remainder> |
| : [Field, Remainder] |
| : Parsed |
| : never |
| : Parsed |
| : never |
| : ParserError<`Expected identifier at \`${Input}\``> |
|
|
| |
| |
| |
| |
| |
| |
| type ParseJsonAccessor<Input extends string> = Input extends `->${infer Remainder}` |
| ? Remainder extends `>${infer Remainder}` |
| ? ParseIdentifier<Remainder> extends [infer Name, `${infer Remainder}`] |
| ? [Name, 'text', EatWhitespace<Remainder>] |
| : ParserError<'Expected property name after `->>`'> |
| : ParseIdentifier<Remainder> extends [infer Name, `${infer Remainder}`] |
| ? ParseJsonAccessor<Remainder> extends [ |
| infer PropertyName, |
| infer PropertyType, |
| `${infer Remainder}`, |
| ] |
| ? [PropertyName, PropertyType, EatWhitespace<Remainder>] |
| : [Name, 'json', EatWhitespace<Remainder>] |
| : ParserError<'Expected property name after `->`'> |
| : ParserError<'Expected ->'> |
|
|
| |
| |
| |
| type ParseFieldTypeCast<Input extends string> = |
| EatWhitespace<Input> extends `::${infer Remainder}` |
| ? ParseIdentifier<EatWhitespace<Remainder>> extends [`${infer CastType}`, `${infer Remainder}`] |
| ? [CastType, EatWhitespace<Remainder>] |
| : ParserError<`Invalid type for \`::\` operator at \`${Remainder}\``> |
| : ParserError<'Expected ::'> |
|
|
| |
| |
| |
| type ParseFieldAggregation<Input extends string> = |
| EatWhitespace<Input> extends `.${infer Remainder}` |
| ? ParseIdentifier<EatWhitespace<Remainder>> extends [ |
| `${infer FunctionName}`, |
| `${infer Remainder}`, |
| ] |
| ? |
| FunctionName extends Token.AggregateFunction |
| ? EatWhitespace<Remainder> extends `()${infer Remainder}` |
| ? [FunctionName, EatWhitespace<Remainder>] |
| : ParserError<`Expected \`()\` after \`.\` operator \`${FunctionName}\``> |
| : ParserError<`Invalid type for \`.\` operator \`${FunctionName}\``> |
| : ParserError<`Invalid type for \`.\` operator at \`${Remainder}\``> |
| : ParserError<'Expected .'> |
|
|
| |
| |
| |
| |
| type ParseIdentifier<Input extends string> = |
| ParseLetters<Input> extends [infer Name, `${infer Remainder}`] |
| ? [Name, EatWhitespace<Remainder>] |
| : ParseQuotedLetters<Input> extends [infer Name, `${infer Remainder}`] |
| ? [Name, EatWhitespace<Remainder>] |
| : ParserError<`No (possibly double-quoted) identifier at \`${Input}\``> |
|
|
| |
| |
| |
| type ParseLetters<Input extends string> = string extends Input |
| ? GenericStringError |
| : ParseLettersHelper<Input, ''> extends [`${infer Letters}`, `${infer Remainder}`] |
| ? Letters extends '' |
| ? ParserError<`Expected letter at \`${Input}\``> |
| : [Letters, Remainder] |
| : ParseLettersHelper<Input, ''> |
|
|
| type ParseLettersHelper<Input extends string, Acc extends string> = string extends Input |
| ? GenericStringError |
| : Input extends `${infer L}${infer Remainder}` |
| ? L extends Token.Letter |
| ? ParseLettersHelper<Remainder, `${Acc}${L}`> |
| : [Acc, Input] |
| : [Acc, ''] |
|
|
| |
| |
| |
| |
| type ParseQuotedLetters<Input extends string> = string extends Input |
| ? GenericStringError |
| : Input extends `"${infer Remainder}` |
| ? ParseQuotedLettersHelper<Remainder, ''> extends [`${infer Letters}`, `${infer Remainder}`] |
| ? Letters extends '' |
| ? ParserError<`Expected string at \`${Remainder}\``> |
| : [Letters, Remainder] |
| : ParseQuotedLettersHelper<Remainder, ''> |
| : ParserError<`Not a double-quoted string at \`${Input}\``> |
|
|
| type ParseQuotedLettersHelper<Input extends string, Acc extends string> = string extends Input |
| ? GenericStringError |
| : Input extends `${infer L}${infer Remainder}` |
| ? L extends '"' |
| ? [Acc, Remainder] |
| : ParseQuotedLettersHelper<Remainder, `${Acc}${L}`> |
| : ParserError<`Missing closing double-quote in \`"${Acc}${Input}\``> |
|
|
| |
| |
| |
| type EatWhitespace<Input extends string> = string extends Input |
| ? GenericStringError |
| : Input extends `${Token.Whitespace}${infer Remainder}` |
| ? EatWhitespace<Remainder> |
| : Input |
|
|
| |
| |
| |
| type CreateParserErrorIfRequired<Input, Message extends string> = |
| Input extends ParserError<string> ? Input : ParserError<Message> |
|
|
| |
| |
| |
| export type ParserError<Message extends string> = { error: true } & Message |
| type GenericStringError = ParserError<'Received a generic string'> |
|
|
| export namespace Ast { |
| export type Node = FieldNode | StarNode | SpreadNode |
|
|
| export type FieldNode = { |
| type: 'field' |
| name: string |
| alias?: string |
| hint?: string |
| innerJoin?: true |
| castType?: string |
| jsonPath?: string |
| aggregateFunction?: Token.AggregateFunction |
| children?: Node[] |
| } |
|
|
| export type StarNode = { |
| type: 'star' |
| } |
|
|
| export type SpreadNode = { |
| type: 'spread' |
| target: FieldNode & { children: Node[] } |
| } |
| } |
|
|
| namespace Token { |
| export type Whitespace = ' ' | '\n' | '\t' |
|
|
| type LowerAlphabet = |
| | 'a' |
| | 'b' |
| | 'c' |
| | 'd' |
| | 'e' |
| | 'f' |
| | 'g' |
| | 'h' |
| | 'i' |
| | 'j' |
| | 'k' |
| | 'l' |
| | 'm' |
| | 'n' |
| | 'o' |
| | 'p' |
| | 'q' |
| | 'r' |
| | 's' |
| | 't' |
| | 'u' |
| | 'v' |
| | 'w' |
| | 'x' |
| | 'y' |
| | 'z' |
|
|
| type Alphabet = LowerAlphabet | Uppercase<LowerAlphabet> |
|
|
| type Digit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0' |
|
|
| export type Letter = Alphabet | Digit | '_' |
|
|
| export type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max' |
| } |
|
|