From 0144c8622c514525754d49ac2f531ae6f39e434c Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 7 Nov 2024 14:14:06 +0900 Subject: [PATCH] Revision 0.33.20 (#1062) * Add Parse Infrastructure * Version --- changelog/0.33.0.md | 2 + example/index.ts | 10 +- package-lock.json | 4 +- package.json | 2 +- readme.md | 9 +- src/index.ts | 6 +- src/parse/index.ts | 29 + src/parse/parse.ts | 54 ++ src/parse/parsebox/index.ts | 30 + src/parse/parsebox/runtime/guard.ts | 96 +++ src/parse/parsebox/runtime/index.ts | 33 + src/parse/parsebox/runtime/module.ts | 51 ++ src/parse/parsebox/runtime/parse.ts | 141 ++++ src/parse/parsebox/runtime/token.ts | 247 ++++++ src/parse/parsebox/runtime/types.ts | 169 ++++ src/parse/parsebox/static/index.ts | 31 + src/parse/parsebox/static/parse.ts | 119 +++ src/parse/parsebox/static/token.ts | 213 +++++ src/parse/parsebox/static/types.ts | 100 +++ src/parse/runtime.ts | 678 ++++++++++++++++ src/parse/static.ts | 764 ++++++++++++++++++ .../compression/module/typebox-parse.ts | 3 + task/build/package/build.ts | 2 +- test/runtime/index.ts | 1 + test/runtime/parse/index.ts | 1 + test/runtime/parse/parse.ts | 389 +++++++++ tsconfig.json | 1 + 27 files changed, 3175 insertions(+), 10 deletions(-) create mode 100644 src/parse/index.ts create mode 100644 src/parse/parse.ts create mode 100644 src/parse/parsebox/index.ts create mode 100644 src/parse/parsebox/runtime/guard.ts create mode 100644 src/parse/parsebox/runtime/index.ts create mode 100644 src/parse/parsebox/runtime/module.ts create mode 100644 src/parse/parsebox/runtime/parse.ts create mode 100644 src/parse/parsebox/runtime/token.ts create mode 100644 src/parse/parsebox/runtime/types.ts create mode 100644 src/parse/parsebox/static/index.ts create mode 100644 src/parse/parsebox/static/parse.ts create mode 100644 src/parse/parsebox/static/token.ts create mode 100644 src/parse/parsebox/static/types.ts create mode 100644 src/parse/runtime.ts create mode 100644 src/parse/static.ts create mode 100644 task/benchmark/compression/module/typebox-parse.ts create mode 100644 test/runtime/parse/index.ts create mode 100644 test/runtime/parse/parse.ts diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index ce6af4c..f349e74 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.20](https://github.com/sinclairzx81/typebox/pull/1062) + - Add TypeScript Parsing Infrastructure. Add Parse API to top level import. - [Revision 0.33.19](https://github.com/sinclairzx81/typebox/pull/1061) - Preemptive fix for TypeScript 5.8.0-dev (Type Fix for Immutable Function) - [Revision 0.33.18](https://github.com/sinclairzx81/typebox/pull/1060) diff --git a/example/index.ts b/example/index.ts index ed52156..711d7d0 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,7 +1,7 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' +import { Type, Parse, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' // ----------------------------------------------------------- // Create: Type @@ -17,6 +17,14 @@ type T = Static console.log(T) +// ----------------------------------------------------------- +// Parse: Type +// ----------------------------------------------------------- + +const S = Parse({ T }, `Partial`) + +type S = Static + // ----------------------------------------------------------- // Create: Value // ----------------------------------------------------------- diff --git a/package-lock.json b/package-lock.json index 8f97ebc..fae9cd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.19", + "version": "0.33.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.19", + "version": "0.33.20", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e614d82..e31bce4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.19", + "version": "0.33.20", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 9590eea..e62b157 100644 --- a/readme.md +++ b/readme.md @@ -1760,11 +1760,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '119.6 kb' │ ' 52.6 kb' │ '2.27 x' │ -│ typebox/errors │ ' 48.6 kb' │ ' 21.9 kb' │ '2.22 x' │ +│ typebox/compiler │ '119.8 kb' │ ' 52.6 kb' │ '2.28 x' │ +│ typebox/errors │ ' 74.4 kb' │ ' 33.1 kb' │ '2.25 x' │ +│ typebox/parse │ '115.3 kb' │ ' 48.3 kb' │ '2.39 x' │ │ typebox/system │ ' 7.4 kb' │ ' 3.2 kb' │ '2.33 x' │ -│ typebox/value │ '157.8 kb' │ ' 66.6 kb' │ '2.37 x' │ -│ typebox │ ' 98.3 kb' │ ' 40.9 kb' │ '2.40 x' │ +│ typebox/value │ '157.2 kb' │ ' 66.1 kb' │ '2.38 x' │ +│ typebox │ '127.3 kb' │ ' 53.3 kb' │ '2.39 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/index.ts b/src/index.ts index b88ad07..9f0429d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,10 @@ export * from './type/registry/index' export * from './type/sets/index' export * from './type/symbols/index' // ------------------------------------------------------------------ +// Parse +// ------------------------------------------------------------------ +export * from './parse/index' +// ------------------------------------------------------------------ // Types // ------------------------------------------------------------------ export * from './type/any/index' @@ -102,6 +106,6 @@ export * from './type/unknown/index' export * from './type/unsafe/index' export * from './type/void/index' // ------------------------------------------------------------------ -// Namespace +// Type.* // ------------------------------------------------------------------ export * from './type/type/index' diff --git a/src/parse/index.ts b/src/parse/index.ts new file mode 100644 index 0000000..d0a1a96 --- /dev/null +++ b/src/parse/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './parse' diff --git a/src/parse/parse.ts b/src/parse/parse.ts new file mode 100644 index 0000000..ef9928f --- /dev/null +++ b/src/parse/parse.ts @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Static } from './parsebox/index' +import { CreateType } from '../type/create/type' +import { TSchema, SchemaOptions } from '../type/schema/index' +import { Module } from './runtime' +import { Type } from './static' + +/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ +export function Parse = {}>(context: Context, code: Code, options?: SchemaOptions): Static.Parse[0] +/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ +export function Parse(code: Code, options?: SchemaOptions): Static.Parse[0] +/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ +export function Parse(...args: any[]): never { + return ParseOnly.apply(null, args as never) as never +} + +/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +export function ParseOnly = {}>(context: Context, code: Code, options?: SchemaOptions): TSchema | undefined +/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +export function ParseOnly(code: Code, options?: SchemaOptions): TSchema | undefined +/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +export function ParseOnly(...args: any[]): TSchema | undefined { + const withContext = typeof args[0] === 'string' ? false : true + const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] + const type = Module.Parse('Type', code, context)[0] as TSchema | undefined + return (type !== undefined ? CreateType(type, options) : undefined) as never +} diff --git a/src/parse/parsebox/index.ts b/src/parse/parsebox/index.ts new file mode 100644 index 0000000..ec3432d --- /dev/null +++ b/src/parse/parsebox/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Runtime from './runtime/index' +export * as Static from './static/index' diff --git a/src/parse/parsebox/runtime/guard.ts b/src/parse/parsebox/runtime/guard.ts new file mode 100644 index 0000000..fa002e4 --- /dev/null +++ b/src/parse/parsebox/runtime/guard.ts @@ -0,0 +1,96 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IIdent, INumber, IRef, IString, IConst, ITuple, IUnion } from './types' + +// ------------------------------------------------------------------ +// Value Guard +// ------------------------------------------------------------------ +// prettier-ignore +function HasPropertyKey(value: Record, key: Key): value is Record & { [_ in Key]: unknown } { + return key in value +} +// prettier-ignore +function IsObjectValue(value: unknown): value is Record { + return typeof value === 'object' && value !== null +} +// prettier-ignore +function IsArrayValue(value: unknown): value is unknown[] { + return globalThis.Array.isArray(value) +} +// ------------------------------------------------------------------ +// Parser Guard +// ------------------------------------------------------------------ +/** Returns true if the value is a Tuple Parser */ +// prettier-ignore +export function IsTuple(value: unknown): value is ITuple { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Tuple' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) +} +/** Returns true if the value is a Union Parser */ +// prettier-ignore +export function IsUnion(value: unknown): value is IUnion { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Union' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) +} +/** Returns true if the value is a Const Parser */ +// prettier-ignore +export function IsConst(value: unknown): value is IConst { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Const' && HasPropertyKey(value, 'value') && typeof value.value === 'string' +} +/** Returns true if the value is a Ident Parser */ +// prettier-ignore +export function IsIdent(value: unknown): value is IIdent { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ident' +} +/** Returns true if the value is a Number Parser */ +// prettier-ignore +export function IsNumber(value: unknown): value is INumber { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Number' +} +/** Returns true if the value is a Ref Parser */ +// prettier-ignore +export function IsRef(value: unknown): value is IRef { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ref' && HasPropertyKey(value, 'ref') && typeof value.ref === 'string' +} +/** Returns true if the value is a String Parser */ +// prettier-ignore +export function IsString(value: unknown): value is IString { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'String' && HasPropertyKey(value, 'options') && IsArrayValue(value.options) +} +/** Returns true if the value is a Parser */ +// prettier-ignore +export function IsParser(value: unknown) { + return ( + IsTuple(value) || + IsUnion(value) || + IsConst(value) || + IsIdent(value) || + IsNumber(value) || + IsRef(value) || + IsString(value) + ) +} diff --git a/src/parse/parsebox/runtime/index.ts b/src/parse/parsebox/runtime/index.ts new file mode 100644 index 0000000..3c15d46 --- /dev/null +++ b/src/parse/parsebox/runtime/index.ts @@ -0,0 +1,33 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Guard from './guard' +export * as Token from './token' +export * from './types' +export * from './module' +export * from './parse' diff --git a/src/parse/parsebox/runtime/module.ts b/src/parse/parsebox/runtime/module.ts new file mode 100644 index 0000000..4384ce4 --- /dev/null +++ b/src/parse/parsebox/runtime/module.ts @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from './types' +import { Parse } from './parse' + +// ------------------------------------------------------------------ +// Module +// ------------------------------------------------------------------ +// prettier-ignore +export class Module { + constructor(private readonly properties: Properties) { } + + /** Parses using one of the parsers defined on this instance */ + public Parse(key: Key, code: string, context: unknown): [] | [Types.StaticParser, string] + /** Parses using one of the parsers defined on this instance */ + public Parse(key: Key, code: string): [] | [Types.StaticParser, string] + /** Parses using one of the parsers defined on this instance */ + public Parse(...args: any[]): never { + const [key, code, context] = + args.length === 3 ? [args[0], args[1], args[2]] : + args.length === 2 ? [args[0], args[1], undefined] : + (() => { throw Error('Invalid parse arguments') })() + return Parse(this.properties[key], this.properties, code, context) as never + } +} diff --git a/src/parse/parsebox/runtime/parse.ts b/src/parse/parsebox/runtime/parse.ts new file mode 100644 index 0000000..73ecf2f --- /dev/null +++ b/src/parse/parsebox/runtime/parse.ts @@ -0,0 +1,141 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Guard from './guard' +import * as Token from './token' +import * as Types from './types' + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +function ParseTuple(parsers: [...Parsers], properties: Properties, code: string, context: unknown): [] | [unknown[], string] { + const buffer = [] as unknown[] + let rest = code + for(const parser of parsers) { + const result = ParseParser(parser, properties, rest, context) + if(result.length === 0) return [] + buffer.push(result[0]) + rest = result[1] + } + return [buffer, rest] +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +function ParseUnion(parsers: [...Parsers], properties: Properties, code: string, context: unknown): [] | [unknown, string] { + for(const parser of parsers) { + const result = ParseParser(parser, properties, code, context) + if(result.length === 0) continue + return result + } + return [] +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +function ParseConst(value: Value, code: string, context: unknown): [] | [Value, string] { + return Token.Const(value, code) as never +} +// ------------------------------------------------------------------ +// Ref +// ------------------------------------------------------------------ +// prettier-ignore +function ParseRef(ref: Ref, properties: Properties, code: string, context: unknown): [] | [string, string] { + const parser = properties[ref] + if(!Guard.IsParser(parser)) throw Error(`Cannot dereference parser '${ref}'`) + return ParseParser(parser, properties, code, context) as never +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +function ParseString(options: string[], code: string, _context: unknown): [] | [string, string] { + return Token.String(options, code) +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +function ParseNumber(code: string, _context: unknown): [] | [string, string] { + return Token.Number(code) +} +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +// prettier-ignore +function ParseIdent(code: string, _context: unknown): [] | [string, string] { + return Token.Ident(code) +} +// ------------------------------------------------------------------ +// Parser +// ------------------------------------------------------------------ +// prettier-ignore +function ParseParser(parser: Parser, properties: Types.IModuleProperties, code: string, context: unknown): [] | [Types.StaticParser, string] { + const result = ( + Guard.IsTuple(parser) ? ParseTuple(parser.parsers, properties, code, context) : + Guard.IsUnion(parser) ? ParseUnion(parser.parsers, properties, code, context) : + Guard.IsConst(parser) ? ParseConst(parser.value, code, context) : + Guard.IsRef(parser) ? ParseRef(parser.ref, properties, code, context) : + Guard.IsString(parser) ? ParseString(parser.options, code, context) : + Guard.IsIdent(parser) ? ParseIdent(code, context) : + Guard.IsNumber(parser) ? ParseNumber(code, context) : + [] + ) + return ( + result.length === 2 + ? [parser.mapping(result[0], context), result[1]] + : result + ) as never +} +// ------------------------------------------------------------------ +// Parse +// ------------------------------------------------------------------ +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, properties: Types.IModuleProperties, code: string, context: unknown): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, properties: Types.IModuleProperties, code: string): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, code: string, context: unknown): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, code: string): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(...args: any[]): never { + const withProperties = typeof args[1] === 'string' ? false : true + const [parser, properties, code, context] = withProperties + ? [args[0], args[1], args[2], args[3]] + : [args[0], {}, args[1], args[2]] + return ParseParser(parser, properties, code, context) as never +} diff --git a/src/parse/parsebox/runtime/token.ts b/src/parse/parsebox/runtime/token.ts new file mode 100644 index 0000000..6ad9f49 --- /dev/null +++ b/src/parse/parsebox/runtime/token.ts @@ -0,0 +1,247 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Chars +// ------------------------------------------------------------------ +// prettier-ignore +namespace Chars { + /** Returns true if the char code is a whitespace */ + export function IsWhitespace(value: number): boolean { + return value === 32 + } + /** Returns true if the char code is a newline */ + export function IsNewline(value: number): boolean { + return value === 10 + } + /** Returns true if the char code is a alpha */ + export function IsAlpha(value: number): boolean { + return ( + (value >= 65 && value <= 90) || // A-Z + (value >= 97 && value <= 122) // a-z + ) + } + /** Returns true if the char code is zero */ + export function IsZero(value: number): boolean { + return value === 48 + } + /** Returns true if the char code is non-zero */ + export function IsNonZero(value: number): boolean { + return value >= 49 && value <= 57 + } + /** Returns true if the char code is a digit */ + export function IsDigit(value: number): boolean { + return ( + IsNonZero(value) || + IsZero(value) + ) + } + /** Returns true if the char code is a dot */ + export function IsDot(value: number): boolean { + return value === 46 + } + /** Returns true if this char code is a underscore */ + export function IsUnderscore(value: unknown): boolean { + return value === 95 + } + /** Returns true if this char code is a dollar sign */ + export function IsDollarSign(value: unknown): boolean { + return value === 36 + } +} + +// ------------------------------------------------------------------ +// Trim +// ------------------------------------------------------------------ +// prettier-ignore +namespace Trim { + /** Trims Whitespace and retains Newline, Tabspaces, etc. */ + export function TrimWhitespaceOnly(code: string): string { + for (let i = 0; i < code.length; i++) { + if (Chars.IsWhitespace(code.charCodeAt(i))) continue + return code.slice(i) + } + return code + } + /** Trims Whitespace including Newline, Tabspaces, etc. */ + export function TrimAll(code: string): string { + return code.trimStart() + } +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +/** Checks the value matches the next string */ +// prettier-ignore +function NextTokenCheck(value: string, code: string): boolean { + if (value.length > code.length) return false + for (let i = 0; i < value.length; i++) { + if (value.charCodeAt(i) !== code.charCodeAt(i)) return false + } + return true +} +/** Gets the next constant string value or empty if no match */ +// prettier-ignore +function NextConst(value: string, code: string, ): [] | [string, string] { + return NextTokenCheck(value, code) + ? [code.slice(0, value.length), code.slice(value.length)] + : [] +} +/** Takes the next constant string value skipping any whitespace */ +// prettier-ignore +export function Const(value: string, code: string): [] | [string, string] { + if(value.length === 0) return ['', code] + const char_0 = value.charCodeAt(0) + return ( + Chars.IsNewline(char_0) ? NextConst(value, Trim.TrimWhitespaceOnly(code)) : + Chars.IsWhitespace(char_0) ? NextConst(value, code) : + NextConst(value, Trim.TrimAll(code)) + ) +} +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +// prettier-ignore +function IdentIsFirst(char: number) { + return ( + Chars.IsAlpha(char) || + Chars.IsDollarSign(char) || + Chars.IsUnderscore(char) + ) +} +// prettier-ignore +function IdentIsRest(char: number) { + return ( + Chars.IsAlpha(char) || + Chars.IsDigit(char) || + Chars.IsDollarSign(char) || + Chars.IsUnderscore(char) + ) +} +// prettier-ignore +function NextIdent(code: string): [] | [string, string] { + if (!IdentIsFirst(code.charCodeAt(0))) return [] + for (let i = 1; i < code.length; i++) { + const char = code.charCodeAt(i) + if (IdentIsRest(char)) continue + const slice = code.slice(0, i) + const rest = code.slice(i) + return [slice, rest] + } + return [code, ''] +} +/** Scans for the next Ident token */ +// prettier-ignore +export function Ident(code: string): [] | [string, string] { + return NextIdent(Trim.TrimAll(code)) +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +/** Checks that the next number is not a leading zero */ +// prettier-ignore +function NumberLeadingZeroCheck(code: string, index: number) { + const char_0 = code.charCodeAt(index + 0) + const char_1 = code.charCodeAt(index + 1) + return ( + ( + // 1-9 + Chars.IsNonZero(char_0) + ) || ( + // 0 + Chars.IsZero(char_0) && + !Chars.IsDigit(char_1) + ) || ( + // 0. + Chars.IsZero(char_0) && + Chars.IsDot(char_1) + ) || ( + // .0 + Chars.IsDot(char_0) && + Chars.IsDigit(char_1) + ) + ) +} +/** Gets the next number token */ +// prettier-ignore +function NextNumber(code: string): [] | [string, string] { + const negated = code.charAt(0) === '-' + const index = negated ? 1 : 0 + if (!NumberLeadingZeroCheck(code, index)) { + return [] + } + const dash = negated ? '-' : '' + let hasDot = false + for (let i = index; i < code.length; i++) { + const char_i = code.charCodeAt(i) + if (Chars.IsDigit(char_i)) { + continue + } + if (Chars.IsDot(char_i)) { + if (hasDot) { + const slice = code.slice(index, i) + const rest = code.slice(i) + return [`${dash}${slice}`, rest] + } + hasDot = true + continue + } + const slice = code.slice(index, i) + const rest = code.slice(i) + return [`${dash}${slice}`, rest] + } + return [code, ''] +} +/** Scans for the next number token */ +// prettier-ignore +export function Number(code: string) { + return NextNumber(Trim.TrimAll(code)) +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +function NextString(options: string[], code: string): [] | [string, string] { + const first = code.charAt(0) + if(!options.includes(first)) return [] + const quote = first + for(let i = 1; i < code.length; i++) { + const char = code.charAt(i) + if(char === quote) { + const slice = code.slice(1, i) + const rest = code.slice(i + 1) + return [slice, rest] + } + } + return [] +} +/** Scans the next Literal String value */ +// prettier-ignore +export function String(options: string[], code: string) { + return NextString(options, Trim.TrimAll(code)) +} diff --git a/src/parse/parsebox/runtime/types.ts b/src/parse/parsebox/runtime/types.ts new file mode 100644 index 0000000..53dd047 --- /dev/null +++ b/src/parse/parsebox/runtime/types.ts @@ -0,0 +1,169 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type IModuleProperties = Record + +// ------------------------------------------------------------------ +// Static +// ------------------------------------------------------------------ + +/** Infers the Output Parameter for a Parser */ +export type StaticParser = Parser extends IParser ? Output : unknown + +// ------------------------------------------------------------------ +// Mapping +// ------------------------------------------------------------------ +export type IMapping = (input: Input, context: any) => Output + +/** Maps input to output. This is the default Mapping */ +export const Identity = (value: unknown) => value + +/** Maps the output as the given parameter T */ +export const As = + (mapping: T): ((value: unknown) => T) => + (_: unknown) => + mapping + +// ------------------------------------------------------------------ +// Parser +// ------------------------------------------------------------------ +export interface IParser { + type: string + mapping: IMapping +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +export type TupleParameter = Parsers extends [infer L extends IParser, ...infer R extends IParser[]] ? TupleParameter]> : Result +export interface ITuple extends IParser { + type: 'Tuple' + parsers: IParser[] +} +/** Creates a Tuple parser */ +export function Tuple>>(parsers: [...Parsers], mapping: Mapping): ITuple> +/** Creates a Tuple parser */ +export function Tuple(parsers: [...Parsers]): ITuple> +export function Tuple(...args: unknown[]): never { + const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Tuple', parsers, mapping } as never +} + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +export type UnionParameter = Parsers extends [infer L extends IParser, ...infer R extends IParser[]] ? UnionParameter> : Result +export interface IUnion extends IParser { + type: 'Union' + parsers: IParser[] +} +/** Creates a Union parser */ +export function Union>>(parsers: [...Parsers], mapping: Mapping): IUnion> +/** Creates a Union parser */ +export function Union(parsers: [...Parsers]): IUnion> +export function Union(...args: unknown[]): never { + const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Union', parsers, mapping } as never +} + +// ------------------------------------------------------------------ +// Token +// ------------------------------------------------------------------ +export interface IConst extends IParser { + type: 'Const' + value: string +} +/** Creates a Const parser */ +export function Const>(value: Value, mapping: Mapping): IConst> +/** Creates a Const parser */ +export function Const(value: Value): IConst +export function Const(...args: unknown[]): never { + const [value, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Const', value, mapping } as never +} + +// ------------------------------------------------------------------ +// Ref +// ------------------------------------------------------------------ +export interface IRef extends IParser { + type: 'Ref' + ref: string +} +/** Creates a Ref parser */ +export function Ref>(ref: string, mapping: Mapping): IRef> +/** Creates a Ref parser */ +export function Ref(ref: string): IRef +export function Ref(...args: unknown[]): never { + const [ref, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Ref', ref, mapping } as never +} + +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +export interface IString extends IParser { + type: 'String' + options: string[] +} +/** Creates a String Parser. Options are an array of permissable quote characters */ +export function String>(options: string[], mapping: Mapping): IString> +/** Creates a String Parser. Options are an array of permissable quote characters */ +export function String(options: string[]): IString +export function String(...params: unknown[]): never { + const [options, mapping] = params.length === 2 ? [params[0], params[1]] : [params[0], Identity] + return { type: 'String', options, mapping } as never +} + +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +export interface IIdent extends IParser { + type: 'Ident' +} +/** Creates an Ident parser */ +export function Ident>(mapping: Mapping): IIdent> +/** Creates an Ident parser */ +export function Ident(): IIdent +export function Ident(...params: unknown[]): never { + const mapping = params.length === 1 ? params[0] : Identity + return { type: 'Ident', mapping } as never +} + +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +export interface INumber extends IParser { + type: 'Number' +} +/** Creates a Number parser */ +export function Number>(mapping: Mapping): INumber> +/** Creates a Number parser */ +export function Number(): INumber +export function Number(...params: unknown[]): never { + const mapping = params.length === 1 ? params[0] : Identity + return { type: 'Number', mapping } as never +} diff --git a/src/parse/parsebox/static/index.ts b/src/parse/parsebox/static/index.ts new file mode 100644 index 0000000..3eb58ee --- /dev/null +++ b/src/parse/parsebox/static/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Token from './token' +export * from './parse' +export * from './types' diff --git a/src/parse/parsebox/static/parse.ts b/src/parse/parsebox/static/parse.ts new file mode 100644 index 0000000..f4fc12e --- /dev/null +++ b/src/parse/parsebox/static/parse.ts @@ -0,0 +1,119 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Tokens from './token' +import * as Types from './types' + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TupleParser = ( + Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] + ? Parse extends [infer Value extends unknown, infer Rest extends string] + ? TupleParser + : [] + : [Result, Code] +) + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type UnionParser = ( + Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] + ? Parse extends [infer Value extends unknown, infer Rest extends string] + ? [Value, Rest] + : UnionParser + : [] +) + +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +type ConstParser = ( + Tokens.Const extends [infer Match extends Value, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +// prettier-ignore +type IdentParser = ( + Tokens.Ident extends [infer Match extends string, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +type NumberParser = ( + Tokens.Number extends [infer Match extends string, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +type StringParser = ( + Tokens.String extends [infer Match extends string, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// Parse +// ------------------------------------------------------------------ +// prettier-ignore +type ParseCode = ( + Type extends Types.Union ? UnionParser : + Type extends Types.Tuple ? TupleParser : + Type extends Types.Const ? ConstParser : + Type extends Types.String ? StringParser : + Type extends Types.Ident ? IdentParser : + Type extends Types.Number ? NumberParser : + [] +) +// prettier-ignore +type ParseMapping = ( + (Parser['mapping'] & { input: Result, context: Context })['output'] +) +/** Parses code with the given parser */ +// prettier-ignore +export type Parse = ( + ParseCode extends [infer L extends unknown, infer R extends string] + ? [ParseMapping, R] + : [] +) diff --git a/src/parse/parsebox/static/token.ts b/src/parse/parsebox/static/token.ts new file mode 100644 index 0000000..8bfdf19 --- /dev/null +++ b/src/parse/parsebox/static/token.ts @@ -0,0 +1,213 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Chars +// ------------------------------------------------------------------ +// prettier-ignore +namespace Chars { + export type Empty = '' + export type Space = ' ' + export type Newline = '\n' + export type Dot = '.' + export type Hyphen = '-' + export type Digit = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + ] + export type Alpha = [ + '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', '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' + ] +} + +// ------------------------------------------------------------------ +// Trim +// ------------------------------------------------------------------ +// prettier-ignore +namespace Trim { + // ------------------------------------------------------------------ + // Whitespace Filters + // ------------------------------------------------------------------ + type W9 = `${W8}${W8}` // 512 + type W8 = `${W7}${W7}` // 256 + type W7 = `${W6}${W6}` // 128 + type W6 = `${W5}${W5}` // 64 + type W5 = `${W4}${W4}` // 32 + type W4 = `${W3}${W3}` // 16 + type W3 = `${W2}${W2}` // 8 + type W2 = `${W1}${W1}` // 4 + type W1 = `${W0}${W0}` // 2 + type W0 = ` ` // 1 + // ------------------------------------------------------------------ + // TrimWhitespace + // ------------------------------------------------------------------ + /** Trims whitespace only */ + export type TrimWhitespace = ( + Code extends `${W4}${infer Rest extends string}` ? TrimWhitespace : + Code extends `${W3}${infer Rest extends string}` ? TrimWhitespace : + Code extends `${W1}${infer Rest extends string}` ? TrimWhitespace : + Code extends `${W0}${infer Rest extends string}` ? TrimWhitespace : + Code + ) + // ------------------------------------------------------------------ + // Trim + // ------------------------------------------------------------------ + /** Trims Whitespace and Newline */ + export type TrimAll = ( + Code extends `${W4}${infer Rest extends string}` ? TrimAll : + Code extends `${W3}${infer Rest extends string}` ? TrimAll : + Code extends `${W1}${infer Rest extends string}` ? TrimAll : + Code extends `${W0}${infer Rest extends string}` ? TrimAll : + Code extends `${Chars.Newline}${infer Rest extends string}` ? TrimAll : + Code + ) +} + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +/** Scans for the next match union */ +// prettier-ignore +type NextUnion = ( + Variants extends [infer Variant extends string, ...infer Rest1 extends string[]] + ? NextConst extends [infer Match extends string, infer Rest2 extends string] + ? [Match, Rest2] + : NextUnion + : [] +) + +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +type NextConst = ( + Code extends `${Value}${infer Rest extends string}` + ? [Value, Rest] + : [] +) +/** Scans for the next constant value */ +// prettier-ignore +export type Const = ( + Value extends '' ? ['', Code] : + Value extends `${infer First extends string}${string}` + ? ( + First extends Chars.Newline ? NextConst> : + First extends Chars.Space ? NextConst : + NextConst> + ) : never +) +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +type NextNumberNegate = ( + Code extends `${Chars.Hyphen}${infer Rest extends string}` + ? [Chars.Hyphen, Rest] + : [Chars.Empty, Code] +) +// prettier-ignore +type NextNumberZeroCheck = ( + Code extends `0${infer Rest}` + ? NextUnion extends [string, string] ? false : true + : true +) +// prettier-ignore +type NextNumberScan = ( + NextUnion<[...Chars.Digit, Chars.Dot], Code> extends [infer Char extends string, infer Rest extends string] + ? Char extends Chars.Dot + ? HasDecimal extends false + ? NextNumberScan + : [Result, `.${Rest}`] + : NextNumberScan + : [Result, Code] +) +// prettier-ignore +export type NextNumber = ( + NextNumberNegate extends [infer Negate extends string, infer Rest extends string] + ? NextNumberZeroCheck extends true + ? NextNumberScan extends [infer Number extends string, infer Rest2 extends string] + ? Number extends Chars.Empty + ? [] + : [`${Negate}${Number}`, Rest2] + : [] + : [] + : [] +) +/** Scans for the next literal number */ +export type Number = NextNumber> + +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +type NextStringQuote = NextUnion +// prettier-ignore +type NextStringBody = ( + Code extends `${infer Char extends string}${infer Rest extends string}` + ? Char extends Quote + ? [Result, Rest] + : NextStringBody + : [] +) +// prettier-ignore +type NextString = ( + NextStringQuote extends [infer Quote extends string, infer Rest extends string] + ? NextStringBody extends [infer String extends string, infer Rest extends string] + ? [String, Rest] + : [] + : [] +) +/** Scans for the next literal string */ +export type String = NextString> + +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +type IdentLeft = [...Chars.Alpha, '_', '$'] // permissable first characters +type IdentRight = [...Chars.Digit, ...IdentLeft] // permissible subsequent characters + +// prettier-ignore +type NextIdentScan = ( + NextUnion extends [infer Char extends string, infer Rest extends string] + ? NextIdentScan + : [Result, Code] +) +// prettier-ignore +type NextIdent = ( + NextUnion extends [infer Left extends string, infer Rest1 extends string] + ? NextIdentScan extends [infer Right extends string, infer Rest2 extends string] + ? [`${Left}${Right}`, Rest2] + : [] + : [] +) + +/** Scans for the next Ident */ +export type Ident = NextIdent> diff --git a/src/parse/parsebox/static/types.ts b/src/parse/parsebox/static/types.ts new file mode 100644 index 0000000..5341255 --- /dev/null +++ b/src/parse/parsebox/static/types.ts @@ -0,0 +1,100 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/parse + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Mapping +// ------------------------------------------------------------------ +export interface IMapping { + context: unknown + input: unknown + output: unknown +} +/** Maps input to output. This is the default Mapping */ +export interface Identity extends IMapping { + output: this['input'] +} +/** Maps the output as the given parameter T */ +export interface As extends IMapping { + output: T +} +// ------------------------------------------------------------------ +// Parser +// ------------------------------------------------------------------ +/** Base type Parser implemented by all other parsers */ +export interface IParser { + type: string + mapping: Mapping +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +/** Creates a Tuple Parser */ +export interface Tuple extends IParser { + type: 'Tuple' + parsers: [...Parsers] +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +/** Creates a Union Parser */ +export interface Union extends IParser { + type: 'Union' + parsers: [...Parsers] +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +/** Creates a Const Parser */ +export interface Const extends IParser { + type: 'Const' + value: Value +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +/** Creates a String Parser. Options are an array of permissable quote characters */ +export interface String extends IParser { + type: 'String' + quote: Options +} +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +/** Creates an Ident Parser. */ +// prettier-ignore +export interface Ident extends IParser { + type: 'Ident' +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +/** Creates a Number Parser. */ +// prettier-ignore +export interface Number extends IParser { + type: 'Number' +} diff --git a/src/parse/runtime.ts b/src/parse/runtime.ts new file mode 100644 index 0000000..de018ed --- /dev/null +++ b/src/parse/runtime.ts @@ -0,0 +1,678 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Runtime } from './parsebox/index' +import * as Types from '../type/index' + +// ------------------------------------------------------------------ +// Tokens +// ------------------------------------------------------------------ +const Newline = '\n' +const LBracket = '[' +const RBracket = ']' +const LParen = '(' +const RParen = ')' +const LBrace = '{' +const RBrace = '}' +const LAngle = '<' +const RAngle = '>' +const Question = '?' +const Colon = ':' +const Comma = ',' +const SemiColon = ';' +const SingleQuote = "'" +const DoubleQuote = '"' +const Tilde = '`' + +// ------------------------------------------------------------------ +// DestructureRight +// ------------------------------------------------------------------ +// prettier-ignore +function DestructureRight(values: T[]): [T[], T | undefined] { + return (values.length > 0) + ? [values.slice(0, values.length - 1), values[values.length - 1]] + : [values, undefined] +} +// ------------------------------------------------------------------ +// Reference +// ------------------------------------------------------------------ +// prettier-ignore +const Reference = Runtime.Ident((value, context: Record) => { + return value in context ? context[value] : Types.Ref(value) +}) + +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +// prettier-ignore +const Literal = Runtime.Union([ + Runtime.Union([Runtime.Const('true'), Runtime.Const('false')], value => Types.Literal(value === 'true')), + Runtime.Number(value => Types.Literal(parseFloat(value))), + Runtime.String([SingleQuote, DoubleQuote, Tilde], value => Types.Literal(value)) +]) + +// ------------------------------------------------------------------ +// Keyword +// ------------------------------------------------------------------ +// prettier-ignore +const Keyword = Runtime.Union([ + Runtime.Const('any', Runtime.As(Types.Any())), + Runtime.Const('bigint', Runtime.As(Types.BigInt())), + Runtime.Const('boolean', Runtime.As(Types.Boolean())), + Runtime.Const('integer', Runtime.As(Types.Integer())), + Runtime.Const('never', Runtime.As(Types.Never())), + Runtime.Const('null', Runtime.As(Types.Null())), + Runtime.Const('number', Runtime.As(Types.Number())), + Runtime.Const('string', Runtime.As(Types.String())), + Runtime.Const('symbol', Runtime.As(Types.Symbol())), + Runtime.Const('undefined', Runtime.As(Types.Undefined())), + Runtime.Const('unknown', Runtime.As(Types.Unknown())), + Runtime.Const('void', Runtime.As(Types.Void())), +]) + +// ------------------------------------------------------------------ +// KeyOf +// ------------------------------------------------------------------ +// prettier-ignore +const KeyOfMapping = (values: unknown[]) => ( + values.length > 0 +) +// prettier-ignore +const KeyOf = Runtime.Union([ + Runtime.Tuple([Runtime.Const('keyof')]), Runtime.Tuple([]) +], KeyOfMapping) + +// ------------------------------------------------------------------ +// IndexArray +// ------------------------------------------------------------------ +// prettier-ignore +const IndexArrayMapping = (values: unknown[]) => ( + values.length === 4 ? [[values[1]], ...values[3] as unknown[]] : + values.length === 3 ? [[], ...values[2] as unknown[]] : + [] +) +// prettier-ignore +const IndexArray = Runtime.Union([ + Runtime.Tuple([Runtime.Const(LBracket), Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Ref('IndexArray')]), + Runtime.Tuple([Runtime.Const(LBracket), Runtime.Const(RBracket), Runtime.Ref('IndexArray')]), + Runtime.Tuple([]) +], value => IndexArrayMapping(value)) + +// ------------------------------------------------------------------ +// Extends +// ------------------------------------------------------------------ +// prettier-ignore +const ExtendsMapping = (values: unknown[]) => { + return values.length === 6 + ? [values[1], values[3], values[5]] + : [] +} +// prettier-ignore +const Extends = Runtime.Union([ + Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('Type'), Runtime.Const(Question), Runtime.Ref('Type'), Runtime.Const(Colon), Runtime.Ref('Type')]), + Runtime.Tuple([]) +], ExtendsMapping) + +// ------------------------------------------------------------------ +// Base +// ------------------------------------------------------------------ +// prettier-ignore +const BaseMapping = (values: unknown[]) => { + return values.length === 3 ? values[1] : values[0] +} +// prettier-ignore +const Base = Runtime.Union([ + Runtime.Tuple([ + Runtime.Const(LParen), + Runtime.Ref('Type'), + Runtime.Const(RParen) + ]), + Runtime.Tuple([Runtime.Union([ + Runtime.Ref('Literal'), + Runtime.Ref('Keyword'), + Runtime.Ref('Object'), + Runtime.Ref('Tuple'), + Runtime.Ref('Constructor'), + Runtime.Ref('Function'), + Runtime.Ref('Mapped'), + Runtime.Ref('AsyncIterator'), + Runtime.Ref('Iterator'), + Runtime.Ref('ConstructorParameters'), + Runtime.Ref('FunctionParameters'), + Runtime.Ref('InstanceType'), + Runtime.Ref('ReturnType'), + Runtime.Ref('Awaited'), + Runtime.Ref('Array'), + Runtime.Ref('Record'), + Runtime.Ref('Promise'), + Runtime.Ref('Partial'), + Runtime.Ref('Required'), + Runtime.Ref('Pick'), + Runtime.Ref('Omit'), + Runtime.Ref('Exclude'), + Runtime.Ref('Extract'), + Runtime.Ref('Uppercase'), + Runtime.Ref('Lowercase'), + Runtime.Ref('Capitalize'), + Runtime.Ref('Uncapitalize'), + Runtime.Ref('Date'), + Runtime.Ref('Uint8Array'), + Runtime.Ref('Reference') + ])]) +], BaseMapping) + +// ------------------------------------------------------------------ +// Factor +// ------------------------------------------------------------------ +// prettier-ignore +const FactorExtends = (Type: Types.TSchema, Extends: Types.TSchema[]) => { + return Extends.length === 3 + ? Types.Extends(Type, Extends[0], Extends[1], Extends[2]) + : Type +} +// prettier-ignore +const FactorIndexArray = (Type: Types.TSchema, IndexArray: unknown[]): Types.TSchema => { + const [Left, Right] = DestructureRight(IndexArray) as [unknown[], Types.TSchema[]] + return ( + !Types.ValueGuard.IsUndefined(Right) ? ( + Right.length === 1 ? Types.Index(FactorIndexArray(Type, Left), Right[0]) : + Right.length === 0 ? Types.Array(FactorIndexArray(Type, Left)) : + Types.Never() + ) : Type + ) +} +// prettier-ignore +const FactorMapping = (KeyOf: boolean, Type: Types.TSchema, IndexArray: unknown[], Extends: Types.TSchema[]) => { + return KeyOf + ? FactorExtends(Types.KeyOf(FactorIndexArray(Type, IndexArray)), Extends) + : FactorExtends(FactorIndexArray(Type, IndexArray), Extends) +} +// prettier-ignore +const Factor = Runtime.Tuple([ + Runtime.Ref('KeyOf'), + Runtime.Ref('Base'), + Runtime.Ref('IndexArray'), + Runtime.Ref('Extends') +], values => FactorMapping(...values)) + +// ------------------------------------------------------------------ +// Expr +// ------------------------------------------------------------------ +// prettier-ignore +function ExprBinaryMapping(Left: Types.TSchema, Rest: unknown[]): Types.TSchema { + return ( + Rest.length === 3 ? (() => { + const [Operator, Right, Next] = Rest as [string, Types.TSchema, unknown[]] + const Schema = ExprBinaryMapping(Right, Next) + if (Operator === '&') { + return Types.TypeGuard.IsIntersect(Schema) + ? Types.Intersect([Left, ...Schema.allOf]) + : Types.Intersect([Left, Schema]) + } + if (Operator === '|') { + return Types.TypeGuard.IsUnion(Schema) + ? Types.Union([Left, ...Schema.anyOf]) + : Types.Union([Left, Schema]) + } + throw 1 + })() : Left + ) +} +// prettier-ignore +const ExprTermTail = Runtime.Union([ + Runtime.Tuple([Runtime.Const('&'), Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail')]), + Runtime.Tuple([]) +]) +// prettier-ignore +const ExprTerm = Runtime.Tuple([ + Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail') +], value => ExprBinaryMapping(...value)) +// prettier-ignore +const ExprTail = Runtime.Union([ + Runtime.Tuple([Runtime.Const('|'), Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail')]), + Runtime.Tuple([]) +]) +// prettier-ignore +const Expr = Runtime.Tuple([ + Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail') +], value => ExprBinaryMapping(...value)) + +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +const Type = Runtime.Ref('Expr') + +// ------------------------------------------------------------------ +// Properties +// ------------------------------------------------------------------ +// prettier-ignore +const PropertyKey = Runtime.Union([Runtime.Ident(), Runtime.String([SingleQuote, DoubleQuote])]) +// prettier-ignore +const PropertyReadonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], value => value.length > 0) +// prettier-ignore +const PropertyOptional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], value => value.length > 0) +// prettier-ignore +const PropertyMapping = (Readonly: boolean, Key: string, Optional: boolean, _: typeof Colon, Type: Types.TSchema) => ({ + [Key]: ( + Readonly && Optional ? Types.ReadonlyOptional(Type) : + Readonly && !Optional ? Types.Readonly(Type) : + !Readonly && Optional ? Types.Optional(Type) : + Type + ) +}) +// prettier-ignore +const Property = Runtime.Tuple([ + Runtime.Ref('PropertyReadonly'), + Runtime.Ref('PropertyKey'), + Runtime.Ref('PropertyOptional'), + Runtime.Const(Colon), + Runtime.Ref('Type'), +], value => PropertyMapping(...value)) +// prettier-ignore +const PropertyDelimiter = Runtime.Union([ + Runtime.Tuple([Runtime.Const(Comma), Runtime.Const(Newline)]), + Runtime.Tuple([Runtime.Const(SemiColon), Runtime.Const(Newline)]), + Runtime.Tuple([Runtime.Const(Comma)]), + Runtime.Tuple([Runtime.Const(SemiColon)]), + Runtime.Tuple([Runtime.Const(Newline)]), +]) +// prettier-ignore +const Properties = Runtime.Union([ + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]), + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]), + Runtime.Tuple([Runtime.Ref('Property')]), + Runtime.Tuple([]) +], values => ( + values.length === 3 ? [values[0], ...values[2] as unknown[]] : + values.length === 2 ? [values[0]] : + values.length === 1 ? [values[0]] : + [] +)) + +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +const ObjectMapping = (values: Record[]) => Types.Object(values.reduce((properties, record) => { + return { ...properties, ...record } +}, {} as Types.TProperties)) +// prettier-ignore +const Object = Runtime.Tuple([ + Runtime.Const(LBrace), + Runtime.Ref[]>('Properties'), + Runtime.Const(RBrace) +], values => ObjectMapping(values[1])) + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +const Elements = Runtime.Union([ + Runtime.Tuple([Runtime.Ref('Type'), Runtime.Const(Comma), Runtime.Ref('Elements')]), + Runtime.Tuple([Runtime.Ref('Type'), Runtime.Const(Comma)]), + Runtime.Tuple([Runtime.Ref('Type')]), + Runtime.Tuple([]), +], value => ( + value.length === 3 ? [value[0], ...value[2]] : + value.length === 2 ? [value[0]] : + value.length === 1 ? [value[0]] : + [] +)) +// prettier-ignore +const Tuple = Runtime.Tuple([ + Runtime.Const(LBracket), + Runtime.Ref('Elements'), + Runtime.Const(RBracket) +], value => Types.Tuple(value[1])) + +// ------------------------------------------------------------------ +// Parameters +// ------------------------------------------------------------------ +// prettier-ignore +const Parameter = Runtime.Tuple([ + Runtime.Ident(), Runtime.Const(Colon), Runtime.Ref('Type') +], value => value[2]) +// prettier-ignore +const Parameters = Runtime.Union([ + Runtime.Tuple([Runtime.Ref('Parameter'), Runtime.Const(Comma), Runtime.Ref('Parameters')]), + Runtime.Tuple([Runtime.Ref('Parameter'), Runtime.Const(Comma)]), + Runtime.Tuple([Runtime.Ref('Parameter')]), + Runtime.Tuple([]), +], value => ( + value.length === 3 ? [value[0], ...value[2]] : + value.length === 2 ? [value[0]] : + value.length === 1 ? [value[0]] : + [] +)) +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +const Constructor = Runtime.Tuple([ + Runtime.Const('new'), + Runtime.Const(LParen), + Runtime.Ref('Parameters'), + Runtime.Const(RParen), + Runtime.Const('=>'), + Runtime.Ref('Type') +], value => Types.Constructor(value[2], value[5])) +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +const Function = Runtime.Tuple([ + Runtime.Const(LParen), + Runtime.Ref('Parameters'), + Runtime.Const(RParen), + Runtime.Const('=>'), + Runtime.Ref('Type') +], value => Types.Function(value[1], value[4])) +// ------------------------------------------------------------------ +// Mapped (requires deferred types) +// ------------------------------------------------------------------ +// prettier-ignore +const MappedMapping = (values: unknown[]) => { + return Types.Literal('Mapped types not supported') +} +// prettier-ignore +const Mapped = Runtime.Tuple([ + Runtime.Const(LBrace), Runtime.Const(LBracket), Runtime.Ident(), Runtime.Const('in'), Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Const(Colon), Runtime.Ref('Type'), Runtime.Const(RBrace) +], MappedMapping) +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +const AsyncIterator = Runtime.Tuple([ + Runtime.Const('AsyncIterator'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.AsyncIterator(value[2])) +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +const Iterator = Runtime.Tuple([ + Runtime.Const('Iterator'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Iterator(value[2])) +// ------------------------------------------------------------------ +// ConstructorParameters +// ------------------------------------------------------------------ +// prettier-ignore +const ConstructorParameters = Runtime.Tuple([ + Runtime.Const('ConstructorParameters'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.ConstructorParameters(value[2])) +// ------------------------------------------------------------------ +// Parameters +// ------------------------------------------------------------------ +// prettier-ignore +const FunctionParameters = Runtime.Tuple([ + Runtime.Const('Parameters'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Parameters(value[2])) +// ------------------------------------------------------------------ +// InstanceType +// ------------------------------------------------------------------ +// prettier-ignore +const InstanceType = Runtime.Tuple([ + Runtime.Const('InstanceType'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.InstanceType(value[2])) +// ------------------------------------------------------------------ +// ReturnType +// ------------------------------------------------------------------ +// prettier-ignore +const ReturnType = Runtime.Tuple([ + Runtime.Const('ReturnType'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.ReturnType(value[2])) +// ------------------------------------------------------------------ +// Awaited +// ------------------------------------------------------------------ +// prettier-ignore +const Awaited = Runtime.Tuple([ + Runtime.Const('Awaited'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Awaited(value[2])) +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +const Array = Runtime.Tuple([ + Runtime.Const('Array'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Array(value[2])) +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +// prettier-ignore +const Record = Runtime.Tuple([ + Runtime.Const('Record'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Record(value[2], value[4])) +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +// prettier-ignore +const Promise = Runtime.Tuple([ + Runtime.Const('Promise'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Promise(value[2])) +// ------------------------------------------------------------------ +// Partial +// ------------------------------------------------------------------ +// prettier-ignore +const Partial = Runtime.Tuple([ + Runtime.Const('Partial'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Partial(value[2])) +// ------------------------------------------------------------------ +// Required +// ------------------------------------------------------------------ +// prettier-ignore +const Required = Runtime.Tuple([ + Runtime.Const('Required'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Required(value[2])) +// ------------------------------------------------------------------ +// Pick +// ------------------------------------------------------------------ +// prettier-ignore +const Pick = Runtime.Tuple([ + Runtime.Const('Pick'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Pick(value[2], value[4])) +// ------------------------------------------------------------------ +// Omit +// ------------------------------------------------------------------ +// prettier-ignore +const Omit = Runtime.Tuple([ + Runtime.Const('Omit'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Omit(value[2], value[4])) +// ------------------------------------------------------------------ +// Exclude +// ------------------------------------------------------------------ +// prettier-ignore +const Exclude = Runtime.Tuple([ + Runtime.Const('Exclude'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Exclude(value[2], value[4])) +// ------------------------------------------------------------------ +// Extract +// ------------------------------------------------------------------ +// prettier-ignore +const Extract = Runtime.Tuple([ + Runtime.Const('Extract'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Extract(value[2], value[4])) +// ------------------------------------------------------------------ +// Uppercase +// ------------------------------------------------------------------ +// prettier-ignore +const Uppercase = Runtime.Tuple([ + Runtime.Const('Uppercase'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Uppercase(value[2])) +// ------------------------------------------------------------------ +// Lowercase +// ------------------------------------------------------------------ +// prettier-ignore +const Lowercase = Runtime.Tuple([ + Runtime.Const('Lowercase'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Lowercase(value[2])) +// ------------------------------------------------------------------ +// Capitalize +// ------------------------------------------------------------------ +// prettier-ignore +const Capitalize = Runtime.Tuple([ + Runtime.Const('Capitalize'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Capitalize(value[2])) +// ------------------------------------------------------------------ +// Uncapitalize +// ------------------------------------------------------------------ +// prettier-ignore +const Uncapitalize = Runtime.Tuple([ + Runtime.Const('Uncapitalize'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Uncapitalize(value[2])) +// ------------------------------------------------------------------ +// Date +// ------------------------------------------------------------------ +const Date = Runtime.Const('Date', Runtime.As(Types.Date())) +// ------------------------------------------------------------------ +// Uint8Array +// ------------------------------------------------------------------ +const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(Types.Uint8Array())) +// ------------------------------------------------------------------ +// Module +// ------------------------------------------------------------------ +// prettier-ignore +export const Module = new Runtime.Module({ + Literal, + Keyword, + KeyOf, + IndexArray, + Extends, + Base, + Factor, + ExprTermTail, + ExprTerm, + ExprTail, + Expr, + Type, + PropertyKey, + PropertyReadonly, + PropertyOptional, + Property, + PropertyDelimiter, + Properties, + Object, + Elements, + Tuple, + Parameter, + Function, + Parameters, + Constructor, + Mapped, + AsyncIterator, + Iterator, + Awaited, + Array, + Record, + Promise, + ConstructorParameters, + FunctionParameters, + InstanceType, + ReturnType, + Partial, + Required, + Pick, + Omit, + Exclude, + Extract, + Uppercase, + Lowercase, + Capitalize, + Uncapitalize, + Date, + Uint8Array, + Reference +}) diff --git a/src/parse/static.ts b/src/parse/static.ts new file mode 100644 index 0000000..90bec8e --- /dev/null +++ b/src/parse/static.ts @@ -0,0 +1,764 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Static } from './parsebox/index' +import * as Types from '../type/index' + +// ------------------------------------------------------------------ +// Tokens +// ------------------------------------------------------------------ +type Newline = '\n' +type LBracket = '[' +type RBracket = ']' +type LParen = '(' +type RParen = ')' +type LBrace = '{' +type RBrace = '}' +type LAngle = '<' +type RAngle = '>' +type Question = '?' +type Colon = ':' +type Comma = ',' +type SemiColon = ';' +type SingleQuote = "'" +type DoubleQuote = '"' +type Tilde = '`' + +// ------------------------------------------------------------------ +// Delimit +// +// This type is used to perform a partial breadth match for repeated +// elements in a sequence. It used to mitigate depth+1 traversal for +// each element which helps prevent reaching instantiation limits. The +// technique works by infering as wide as possible on the sequence +// enabling TS to hoist interior matches, but does come at slight +// inference performance cost. The current (infer=9) is configured +// to match 64 terminal tuple elements. +// +// for the given sequence +// +// [a, b, c, d, e, f, g] +// +// ------------------------------------------------------------------ +// +// without breadth mapping (infer=1, depth=6) +// +// [infer a, +// [infer b, +// [infer c, +// [infer d, +// [infer e, +// [infer f, +// [infer g, +// []]]]]]]] +// +// ------------------------------------------------------------------ +// +// with breadth mapping (infer=4, depth=2) +// +// [infer a, infer b, infer c, infer d, +// [infer e, infer f, infer g, +// []]] +// +// +// ------------------------------------------------------------------ +// prettier-ignore +interface DelimitTailMapping<_ = unknown> extends Static.IMapping { + output: ( + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer H, _, infer I, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, H, I, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer H, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, H, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer Rest extends unknown[]] ? [A, B, C, D, E, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer Rest extends unknown[]] ? [A, B, C, D, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer Rest extends unknown[]] ? [A, B, C, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer Rest extends unknown[]] ? [A, B, ...Rest] : + this['input'] extends [_, infer A, _, infer Rest extends unknown[]] ? [A, ...Rest] : + this['input'] extends [_, infer Rest extends unknown[]] ? [...Rest] : + this['input'] extends [_] ? [] : + [] + ) +} +// prettier-ignore +type DelimitTail = Static.Union<[ + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _,Delimit]>, + Static.Tuple<[_, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, Delimit]>, + Static.Tuple<[_, Delimit]>, + Static.Tuple<[_]>, + Static.Tuple<[]> +], DelimitTailMapping> +// prettier-ignore +interface DelimitMapping extends Static.IMapping { + output: ( + this['input'] extends [infer Element extends unknown, infer Rest extends unknown[]] + ? [Element, ...Rest] + : [] + ) +} +// prettier-ignore +type Delimit = ( + Static.Union<[ + Static.Tuple<[Parser, DelimitTail]>, + Static.Tuple<[]> + ], DelimitMapping> +) +// ------------------------------------------------------------------ +// Reference +// ------------------------------------------------------------------ +// prettier-ignore +interface ReferenceMapping extends Static.IMapping { + output: this['input'] extends [infer Key extends string] + ? Key extends keyof this['context'] + ? this['context'][Key] + : Types.TRef + : never +} +type Reference = Static.Tuple<[Static.Ident], ReferenceMapping> + +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +// prettier-ignore +interface LiteralBooleanMapping extends Static.IMapping { + output: this['input'] extends `${infer S extends boolean}` ? Types.TLiteral : never +} +// prettier-ignore +interface LiteralNumberMapping extends Static.IMapping { + output: this['input'] extends `${infer S extends number}` ? Types.TLiteral : never +} +// prettier-ignore +interface LiteralStringMapping extends Static.IMapping { + output: this['input'] extends `${infer S extends string}` ? Types.TLiteral : never +} +// prettier-ignore +type Literal = Static.Union<[ + Static.Union<[Static.Const<'true'>, Static.Const<'false'>], LiteralBooleanMapping>, + Static.Number, + Static.String<[DoubleQuote, SingleQuote, Tilde], LiteralStringMapping>, +]> +// ------------------------------------------------------------------ +// Keyword +// ------------------------------------------------------------------ +// prettier-ignore +type Keyword = Static.Union<[ + Static.Const<'any', Static.As>, + Static.Const<'bigint', Static.As>, + Static.Const<'boolean', Static.As>, + Static.Const<'integer', Static.As>, + Static.Const<'never', Static.As>, + Static.Const<'null', Static.As>, + Static.Const<'number', Static.As>, + Static.Const<'string', Static.As>, + Static.Const<'symbol', Static.As>, + Static.Const<'undefined', Static.As>, + Static.Const<'unknown', Static.As>, + Static.Const<'void', Static.As>, +]> +// ------------------------------------------------------------------ +// KeyOf +// ------------------------------------------------------------------ +// prettier-ignore +interface KeyOfMapping extends Static.IMapping { + output: this['input'] extends [] ? false : true +} +// prettier-ignore +type KeyOf = Static.Union<[ + Static.Tuple<[Static.Const<'keyof'>]>, + Static.Tuple<[]> +], KeyOfMapping> + +// ------------------------------------------------------------------ +// IndexArray +// ------------------------------------------------------------------ +// prettier-ignore +interface IndexArrayMapping extends Static.IMapping { + output: ( + this['input'] extends [LBracket, infer Type extends Types.TSchema, RBracket, infer Rest extends unknown[]] ? [[Type], ...Rest] : + this['input'] extends [LBracket, RBracket, infer Rest extends unknown[]] ? [[], ...Rest] : + [] + ) +} +// prettier-ignore +type IndexArray = Static.Union<[ + Static.Tuple<[Static.Const, Type, Static.Const, IndexArray]>, + Static.Tuple<[Static.Const, Static.Const, IndexArray]>, + Static.Tuple<[]> +], IndexArrayMapping> + +// ------------------------------------------------------------------ +// Extends +// ------------------------------------------------------------------ +// prettier-ignore +interface ExtendsMapping extends Static.IMapping { + output: this['input'] extends ['extends', infer Type extends Types.TSchema, Question, infer True extends Types.TSchema, Colon, infer False extends Types.TSchema] + ? [Type, True, False] + : [] +} +// prettier-ignore +type Extends = Static.Union<[ + Static.Tuple<[Static.Const<'extends'>, Type, Static.Const, Type, Static.Const, Type]>, + Static.Tuple<[]> +], ExtendsMapping> + +// ------------------------------------------------------------------ +// Base +// ------------------------------------------------------------------ +// prettier-ignore +interface BaseMapping extends Static.IMapping { + output: ( + this['input'] extends [LParen, infer Type extends Types.TSchema, RParen] ? Type : + this['input'] extends [infer Type extends Types.TSchema] ? Type : + never + ) +} +// prettier-ignore +type Base = Static.Union<[ + Static.Tuple<[ + Static.Const, + Type, + Static.Const + ]>, + Static.Tuple<[Static.Union<[ + Literal, + Keyword, + Object, + Tuple, + Function, + Constructor, + Mapped, + AsyncIterator, + Iterator, + ConstructorParameters, + FunctionParameters, + InstanceType, + ReturnType, + Awaited, + Array, + Record, + Promise, + Partial, + Required, + Pick, + Omit, + Exclude, + Extract, + Lowercase, + Uppercase, + Capitalize, + Uncapitalize, + Date, + Uint8Array, + Reference + ]>]> +], BaseMapping> +// ------------------------------------------------------------------ +// Factor +// ------------------------------------------------------------------ +// prettier-ignore +type FactorExtends = ( + Extends extends [infer Right extends Types.TSchema, infer True extends Types.TSchema, infer False extends Types.TSchema] + ? Types.TExtends + : Type +) +// prettier-ignore +type FactorIndexArray = ( + IndexArray extends [...infer Left extends unknown[], infer Right extends Types.TSchema[]] ? ( + Right extends [infer Indexer extends Types.TSchema] ? Types.TIndex, Types.TIndexPropertyKeys> : + Right extends [] ? Types.TArray> : + Types.TNever + ) : Type +) +// prettier-ignore +interface FactorMapping extends Static.IMapping { + output: this['input'] extends [infer KeyOf extends boolean, infer Type extends Types.TSchema, infer IndexArray extends unknown[], infer Extends extends unknown[]] + ? KeyOf extends true + ? FactorExtends>, Extends> + : FactorExtends, Extends> + : never +} +// prettier-ignore +type Factor = Static.Tuple<[ + KeyOf, Base, IndexArray, Extends +], FactorMapping> +// ------------------------------------------------------------------ +// Expr +// ------------------------------------------------------------------ +// prettier-ignore +type ExprBinaryReduce = ( + Rest extends [infer Operator extends unknown, infer Right extends Types.TSchema, infer Next extends unknown[]] ? ( + ExprBinaryReduce extends infer Schema extends Types.TSchema ? ( + Operator extends '&' ? ( + Schema extends Types.TIntersect + ? Types.TIntersect<[Left, ...Types]> + : Types.TIntersect<[Left, Schema]> + ) : + Operator extends '|' ? ( + Schema extends Types.TUnion + ? Types.TUnion<[Left, ...Types]> + : Types.TUnion<[Left, Schema]> + ) : never + ) : never + ) : Left +) +// prettier-ignore +interface ExprBinaryMapping extends Static.IMapping { + output: ( + this['input'] extends [infer Left extends Types.TSchema, infer Rest extends unknown[]] + ? ExprBinaryReduce + : [] + ) +} +// prettier-ignore +type ExprTermTail = Static.Union<[ + Static.Tuple<[Static.Const<'&'>, Factor, ExprTermTail]>, + Static.Tuple<[]> +]> +// prettier-ignore +type ExprTerm = Static.Tuple<[ + Factor, ExprTermTail +], ExprBinaryMapping> +// prettier-ignore +type ExprTail = Static.Union<[ + Static.Tuple<[Static.Const<'|'>, ExprTerm, ExprTail]>, + Static.Tuple<[]> +]> +// prettier-ignore +type Expr = Static.Tuple<[ + ExprTerm, ExprTail +], ExprBinaryMapping> +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +export type Type = Expr +// ------------------------------------------------------------------ +// Properties +// ------------------------------------------------------------------ +// prettier-ignore +interface PropertyKeyStringMapping extends Static.IMapping { + output: this['input'] +} +type PropertyKeyString = Static.String<[SingleQuote, DoubleQuote], PropertyKeyStringMapping> + +type PropertyKey = Static.Union<[Static.Ident, PropertyKeyString]> +// prettier-ignore +interface PropertyReadonlyMapping extends Static.IMapping { + output: this['input'] extends ['readonly'] ? true : false +} +type PropertyReadonly = Static.Union<[Static.Tuple<[Static.Const<'readonly'>]>, Static.Tuple<[]>], PropertyReadonlyMapping> +// prettier-ignore +interface PropertyOptionalMapping extends Static.IMapping { + output: this['input'] extends [Question] ? true : false +} +type PropertyOptional = Static.Union<[Static.Tuple<[Static.Const]>, Static.Tuple<[]>], PropertyOptionalMapping> +// prettier-ignore +interface PropertyMapping extends Static.IMapping { + output: this['input'] extends [infer Readonly extends boolean, infer Key extends string, infer Optional extends boolean, string, infer Type extends Types.TSchema] + ? { + [_ in Key]: ( + [Readonly, Optional] extends [true, true] ? Types.TReadonlyOptional : + [Readonly, Optional] extends [true, false] ? Types.TReadonly : + [Readonly, Optional] extends [false, true] ? Types.TOptional : + Type + ) + } : never +} + +type Property = Static.Tuple<[PropertyReadonly, PropertyKey, PropertyOptional, Static.Const, Type], PropertyMapping> + +type PropertiesEvaluate = { [K in keyof T]: T[K] } & {} +// prettier-ignore +type PropertyDelimiter = Static.Union<[ + Static.Tuple<[Static.Const, Static.Const]>, + Static.Tuple<[Static.Const, Static.Const]>, + Static.Tuple<[Static.Const]>, + Static.Tuple<[Static.Const]>, + Static.Tuple<[Static.Const]>, +]> +// prettier-ignore +type PropertiesReduce = ( + PropertiesArray extends [infer Left extends Types.TProperties, ...infer Right extends Types.TProperties[]] + ? PropertiesReduce> + : Result +) +// prettier-ignore +interface PropertiesMapping extends Static.IMapping { + output: this['input'] extends Types.TProperties[] ? PropertiesReduce : never +} +type Properties = Static.Union<[Delimit], PropertiesMapping> +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +interface ObjectMapping extends Static.IMapping { + output: this['input'] extends [unknown, infer Properties extends Types.TProperties, unknown] + ? Types.TObject + : never +} +// prettier-ignore +type Object = Static.Tuple<[ + Static.Const, Properties, Static.Const +], ObjectMapping> +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +type Elements = Delimit> +// prettier-ignore +interface TupleMapping extends Static.IMapping { + output: this['input'] extends [unknown, infer Elements extends Types.TSchema[], unknown] ? Types.TTuple : never +} +// prettier-ignore +type Tuple = Static.Tuple<[ + Static.Const, Elements, Static.Const +], TupleMapping> +// ------------------------------------------------------------------ +// Parameters +// ------------------------------------------------------------------ +interface ParameterMapping extends Static.IMapping { + output: this['input'] extends [string, Colon, infer Type extends Types.TSchema] ? Type : never +} +// prettier-ignore +type Parameter = Static.Tuple<[ + Static.Ident, Static.Const, Type +], ParameterMapping> + +type Parameters = Delimit> +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +interface FunctionMapping extends Static.IMapping { + output: this['input'] extends [LParen, infer Parameters extends Types.TSchema[], RParen, '=>', infer ReturnType extends Types.TSchema] + ? Types.TFunction + : never +} +// prettier-ignore +type Function = Static.Tuple<[ + Static.Const, Parameters, Static.Const, Static.Const<'=>'>, Type +], FunctionMapping> +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +interface ConstructorMapping extends Static.IMapping { + output: this['input'] extends ['new', LParen, infer Parameters extends Types.TSchema[], RParen, '=>', infer InstanceType extends Types.TSchema] + ? Types.TConstructor + : never +} +// prettier-ignore +type Constructor = Static.Tuple<[ + Static.Const<'new'>, Static.Const, Parameters, Static.Const, Static.Const<'=>'>, Type +], ConstructorMapping> +// ------------------------------------------------------------------ +// Mapped (requires deferred types) +// ------------------------------------------------------------------ +// prettier-ignore +interface MappedMapping extends Static.IMapping { + output: this['input'] extends [LBrace, LBracket, infer Key extends string, 'in', infer Right extends Types.TSchema, RBracket, Colon, infer Type extends Types.TSchema, RBrace] + ? Types.TLiteral<'Mapped types not supported'> + : this['input'] +} +// prettier-ignore +type Mapped = Static.Tuple<[ + Static.Const, Static.Const, Static.Ident, Static.Const<'in'>, Type, Static.Const, Static.Const, Type, Static.Const +], MappedMapping> +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +interface ArrayMapping extends Static.IMapping { + output: this['input'] extends ['Array', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TArray + : never +} +// prettier-ignore +type Array = Static.Tuple<[ + Static.Const<'Array'>, Static.Const, Type, Static.Const, +], ArrayMapping> +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +interface AsyncIteratorMapping extends Static.IMapping { + output: this['input'] extends ['AsyncIterator', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TAsyncIterator + : never +} +// prettier-ignore +type AsyncIterator = Static.Tuple<[ + Static.Const<'AsyncIterator'>, Static.Const, Type, Static.Const, +], AsyncIteratorMapping> +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +interface IteratorMapping extends Static.IMapping { + output: this['input'] extends ['Iterator', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TIterator + : never +} +// prettier-ignore +type Iterator = Static.Tuple<[ + Static.Const<'Iterator'>, Static.Const, Type, Static.Const, +], IteratorMapping> + +// ------------------------------------------------------------------ +// ConstructorParameters +// ------------------------------------------------------------------ +// prettier-ignore +interface ConstructorParametersMapping extends Static.IMapping { + output: this['input'] extends ['ConstructorParameters', LAngle, infer Type extends Types.TConstructor, RAngle] + ? Types.TConstructorParameters + : never +} +// prettier-ignore +type ConstructorParameters = Static.Tuple<[ + Static.Const<'ConstructorParameters'>, Static.Const, Type, Static.Const, +], ConstructorParametersMapping> +// ------------------------------------------------------------------ +// FunctionParameters +// ------------------------------------------------------------------ +// prettier-ignore +interface FunctionParametersMapping extends Static.IMapping { + output: this['input'] extends ['Parameters', LAngle, infer Type extends Types.TFunction, RAngle] + ? Types.TParameters + : never +} +// prettier-ignore +type FunctionParameters = Static.Tuple<[ + Static.Const<'Parameters'>, Static.Const, Type, Static.Const, +], FunctionParametersMapping> +// ------------------------------------------------------------------ +// InstanceType +// ------------------------------------------------------------------ +// prettier-ignore +interface InstanceTypeMapping extends Static.IMapping { + output: this['input'] extends ['InstanceType', LAngle, infer Type extends Types.TConstructor, RAngle] + ? Types.TInstanceType + : never +} +// prettier-ignore +type InstanceType = Static.Tuple<[ + Static.Const<'InstanceType'>, Static.Const, Type, Static.Const, +], InstanceTypeMapping> +// ------------------------------------------------------------------ +// ReturnType +// ------------------------------------------------------------------ +// prettier-ignore +interface ReturnTypeMapping extends Static.IMapping { + output: this['input'] extends ['ReturnType', LAngle, infer Type extends Types.TFunction, RAngle] + ? Types.TReturnType + : never +} +// prettier-ignore +type ReturnType = Static.Tuple<[ + Static.Const<'ReturnType'>, Static.Const, Type, Static.Const, +], ReturnTypeMapping> +// ------------------------------------------------------------------ +// Awaited +// ------------------------------------------------------------------ +// prettier-ignore +interface AwaitedMapping extends Static.IMapping { + output: this['input'] extends ['Awaited', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TAwaited + : never +} +// prettier-ignore +type Awaited = Static.Tuple<[ + Static.Const<'Awaited'>, Static.Const, Type, Static.Const, +], AwaitedMapping> +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +// prettier-ignore +interface PromiseMapping extends Static.IMapping { + output: this['input'] extends ['Promise', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TPromise + : never +} +// prettier-ignore +type Promise = Static.Tuple<[ + Static.Const<'Promise'>, Static.Const, Type, Static.Const, +], PromiseMapping> +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +// prettier-ignore +interface RecordMapping extends Static.IMapping { + output: this['input'] extends ['Record', LAngle, infer Key extends Types.TSchema, Comma, infer Type extends Types.TSchema, RAngle] + ? Types.TRecord + : never +} +// prettier-ignore +type Record = Static.Tuple<[ + Static.Const<'Record'>, Static.Const, Type, Static.Const, Type, Static.Const, +], RecordMapping> +// ------------------------------------------------------------------ +// Partial +// ------------------------------------------------------------------ +// prettier-ignore +interface PartialMapping extends Static.IMapping { + output: this['input'] extends ['Partial', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TPartial + : never +} +// prettier-ignore +type Partial = Static.Tuple<[ + Static.Const<'Partial'>, Static.Const, Type, Static.Const, +], PartialMapping> +// ------------------------------------------------------------------ +// Required +// ------------------------------------------------------------------ +// prettier-ignore +interface RequiredMapping extends Static.IMapping { + output: this['input'] extends ['Required', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TPartial + : never +} +// prettier-ignore +type Required = Static.Tuple<[ + Static.Const<'Required'>, Static.Const, Type, Static.Const, +], RequiredMapping> +// ------------------------------------------------------------------ +// Pick +// ------------------------------------------------------------------ +// prettier-ignore +interface PickMapping extends Static.IMapping { + output: this['input'] extends ['Pick', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TPick> + : never +} +// prettier-ignore +type Pick = Static.Tuple<[ + Static.Const<'Pick'>, Static.Const, Type, Static.Const, Type, Static.Const, +], PickMapping> +// ------------------------------------------------------------------ +// Omit +// ------------------------------------------------------------------ +// prettier-ignore +interface OmitMapping extends Static.IMapping { + output: this['input'] extends ['Omit', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TOmit> + : never +} +// prettier-ignore +type Omit = Static.Tuple<[ + Static.Const<'Omit'>, Static.Const, Type, Static.Const, Type, Static.Const +], OmitMapping> +// ------------------------------------------------------------------ +// Exclude +// ------------------------------------------------------------------ +// prettier-ignore +interface ExcludeMapping extends Static.IMapping { + output: this['input'] extends ['Exclude', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TExclude + : never +} +// prettier-ignore +type Exclude = Static.Tuple<[ + Static.Const<'Exclude'>, Static.Const, Type, Static.Const, Type, Static.Const +], ExcludeMapping> +// ------------------------------------------------------------------ +// Extract +// ------------------------------------------------------------------ +// prettier-ignore +interface ExtractMapping extends Static.IMapping { + output: this['input'] extends ['Extract', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TExtract + : never +} +// prettier-ignore +type Extract = Static.Tuple<[ + Static.Const<'Extract'>, Static.Const, Type, Static.Const, Type, Static.Const +], ExtractMapping> +// ------------------------------------------------------------------ +// Uppercase +// ------------------------------------------------------------------ +// prettier-ignore +interface UppercaseMapping extends Static.IMapping { + output: this['input'] extends ['Uppercase', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TUppercase + : never +} +// prettier-ignore +type Uppercase = Static.Tuple<[ + Static.Const<'Uppercase'>, Static.Const, Type, Static.Const, +], UppercaseMapping> +// ------------------------------------------------------------------ +// Lowercase +// ------------------------------------------------------------------ +// prettier-ignore +interface LowercaseMapping extends Static.IMapping { + output: this['input'] extends ['Lowercase', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TLowercase + : never +} +// prettier-ignore +type Lowercase = Static.Tuple<[ + Static.Const<'Lowercase'>, Static.Const, Type, Static.Const, +], LowercaseMapping> +// ------------------------------------------------------------------ +// Capitalize +// ------------------------------------------------------------------ +// prettier-ignore +interface CapitalizeMapping extends Static.IMapping { + output: this['input'] extends ['Capitalize', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TCapitalize + : never +} +// prettier-ignore +type Capitalize = Static.Tuple<[ + Static.Const<'Capitalize'>, Static.Const, Type, Static.Const, +], CapitalizeMapping> +// ------------------------------------------------------------------ +// Uncapitalize +// ------------------------------------------------------------------ +// prettier-ignore +interface UncapitalizeMapping extends Static.IMapping { + output: this['input'] extends ['Uncapitalize', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TUncapitalize + : never +} +// prettier-ignore +type Uncapitalize = Static.Tuple<[ + Static.Const<'Uncapitalize'>, Static.Const, Type, Static.Const, +], UncapitalizeMapping> +// ------------------------------------------------------------------ +// Date +// ------------------------------------------------------------------ +type Date = Static.Const<'Date', Static.As> +// ------------------------------------------------------------------ +// Uint8Array +// ------------------------------------------------------------------ +type Uint8Array = Static.Const<'Uint8Array', Static.As> diff --git a/task/benchmark/compression/module/typebox-parse.ts b/task/benchmark/compression/module/typebox-parse.ts new file mode 100644 index 0000000..7ba2bf3 --- /dev/null +++ b/task/benchmark/compression/module/typebox-parse.ts @@ -0,0 +1,3 @@ +import * as Parse from '@sinclair/typebox/parse' + +console.log(Parse) diff --git a/task/build/package/build.ts b/task/build/package/build.ts index e7975ed..a7fc92f 100644 --- a/task/build/package/build.ts +++ b/task/build/package/build.ts @@ -32,7 +32,7 @@ import { createPackageJson } from './create-package-json' /** Builds package.json and redirect directories */ export async function build(target: string) { console.log('building...package.json') - const submodules = ['compiler', 'errors', 'system', 'type', 'value'] + const submodules = ['compiler', 'errors', 'parse', 'system', 'type', 'value'] await createPackageJsonRedirect(target, submodules) await createPackageJson(target, submodules) } diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 15ca57a..56b6a34 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -8,6 +8,7 @@ TypeSystemPolicy.InstanceMode = 'freeze' import './compiler/index' import './compiler-ajv/index' import './errors/index' +import './parse/index' import './system/index' import './type/index' import './value/index' diff --git a/test/runtime/parse/index.ts b/test/runtime/parse/index.ts new file mode 100644 index 0000000..09cc871 --- /dev/null +++ b/test/runtime/parse/index.ts @@ -0,0 +1 @@ +import './parse' diff --git a/test/runtime/parse/parse.ts b/test/runtime/parse/parse.ts new file mode 100644 index 0000000..1ac85df --- /dev/null +++ b/test/runtime/parse/parse.ts @@ -0,0 +1,389 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type, Parse } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +// prettier-ignore +describe('parse/Parse', () => { + it('Should parse Any', () => { + const T = Parse(`any`) + Assert.IsTrue(TypeGuard.IsAny(T)) + }) + it('Should parse Array 1', () => { + const T = Parse(`number[]`) + Assert.IsTrue(TypeGuard.IsArray(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse Array 2', () => { + const T = Parse(`Array`) + Assert.IsTrue(TypeGuard.IsArray(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse AsyncIterator', () => { + const T = Parse(`AsyncIterator`) + Assert.IsTrue(TypeGuard.IsAsyncIterator(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse Awaited', () => { + const T = Parse(`Awaited>`) + Assert.IsTrue(TypeGuard.IsNumber(T)) + }) + it('Should parse BigInt', () => { + const T = Parse(`bigint`) + Assert.IsTrue(TypeGuard.IsBigInt(T)) + }) + it('Should parse Boolean', () => { + const T = Parse(`boolean`) + Assert.IsTrue(TypeGuard.IsBoolean(T)) + }) + it('Should parse ConstructorParameters', () => { + const T = Parse(`ConstructorParameters boolean>`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + it('Should parse Constructor', () => { + const T = Parse(`new (a: number, b: string) => boolean`) + Assert.IsTrue(TypeGuard.IsConstructor(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.parameters[0])) + Assert.IsTrue(TypeGuard.IsString(T.parameters[1])) + Assert.IsTrue(TypeGuard.IsBoolean(T.returns)) + }) + it('Should parse Date', () => { + const T = Parse(`Date`) + Assert.IsTrue(TypeGuard.IsDate(T)) + }) + it('Should parse Exclude', () => { + const T = Parse(`Exclude<1 | 2 | 3, 1>`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 2) + Assert.IsTrue(T.anyOf[1].const === 3) + }) + it('Should parse Extract', () => { + const T = Parse(`Extract<1 | 2 | 3, 1 | 2>`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + // @ts-ignore fix: incorrect union order (result of UnionToTuple, replace with Tuple destructuring) + Assert.IsTrue(T.anyOf[0].const === 1) + // @ts-ignore fix: incorrect union order (result of UnionToTuple, replace with Tuple destructuring) + Assert.IsTrue(T.anyOf[1].const === 2) + }) + it('Should parse Function', () => { + const T = Parse(`(a: number, b: string) => boolean`) + Assert.IsTrue(TypeGuard.IsFunction(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.parameters[0])) + Assert.IsTrue(TypeGuard.IsString(T.parameters[1])) + Assert.IsTrue(TypeGuard.IsBoolean(T.returns)) + }) + it('Should parse Indexed 1', () => { + const T = Parse(`{ x: 1, y: 2, z: 3 }['x']`) + Assert.IsTrue(T.const === 1) + }) + it('Should parse Indexed 2', () => { + const T = Parse(`{ x: 1, y: 2, z: 3 }['x' | 'y' | 'z']`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 1) + Assert.IsTrue(T.anyOf[1].const === 2) + Assert.IsTrue(T.anyOf[2].const === 3) + }) + it('Should parse Indexed 3', () => { + const T = Parse(`{ x: 1, y: 2, z: 3 }`) + const S = Parse({ T }, `T[keyof T]`) + Assert.IsTrue(TypeGuard.IsUnion(S)) + Assert.IsTrue(S.anyOf[0].const === 1) + Assert.IsTrue(S.anyOf[1].const === 2) + Assert.IsTrue(S.anyOf[2].const === 3) + }) + it('Should parse Indexed 4', () => { + const T = Parse(`['A', 'B', 'C']`) + const S = Parse({ T }, `T[number]`) + Assert.IsTrue(TypeGuard.IsUnion(S)) + Assert.IsTrue(S.anyOf[0].const === 'A') + Assert.IsTrue(S.anyOf[1].const === 'B') + Assert.IsTrue(S.anyOf[2].const === 'C') + }) + it('Should parse Integer', () => { + const T = Parse(`integer`) + Assert.IsTrue(TypeGuard.IsInteger(T)) + }) + it('Should parse Intersect 1', () => { + const T = Parse(`1 & 2`) + Assert.IsTrue(TypeGuard.IsIntersect(T)) + Assert.IsTrue(T.allOf[0].const === 1) + Assert.IsTrue(T.allOf[1].const === 2) + }) + it('Should parse Intersect 2', () => { + const T = Parse(`1 & (2 & 3)`) // expect flatten + Assert.IsTrue(TypeGuard.IsIntersect(T)) + Assert.IsTrue(T.allOf[0].const === 1) + Assert.IsTrue(T.allOf[1].const === 2) + Assert.IsTrue(T.allOf[2].const === 3) + }) + it('Should parse Intersect 3', () => { + const T = Parse(`(1 | 2) & 3`) // operator precedence + Assert.IsTrue(TypeGuard.IsIntersect(T)) + Assert.IsTrue(TypeGuard.IsUnion(T.allOf[0])) + Assert.IsTrue(T.allOf[1].const === 3) + }) + it('Should parse InstanceType 1', () => { + const T = Parse(`InstanceType { x: 1, y: 2 }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(T.properties.x.const === 1) + Assert.IsTrue(T.properties.y.const === 2) + }) + it('Should parse InstanceType 2', () => { + const T = Parse(`InstanceType`) // generalization issue + Assert.IsTrue(T === undefined) + }) + it('Should parse Iterator', () => { + const T = Parse(`Iterator`) + Assert.IsTrue(TypeGuard.IsIterator(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse KeyOf 1', () => { + const T = Parse(`keyof { x: 1, y: 2 }`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 'x') + Assert.IsTrue(T.anyOf[1].const === 'y') + }) + it('Should parse KeyOf 2', () => { + const T = Parse(`keyof [0, 1]`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === '0') + Assert.IsTrue(T.anyOf[1].const === '1') + }) + it('Should parse KeyOf 3', () => { + const T = Parse(`{ x: 1, y: 2 }`) + const S = Parse({ T }, `keyof T`) + Assert.IsTrue(TypeGuard.IsUnion(S)) + Assert.IsTrue(S.anyOf[0].const === 'x') + Assert.IsTrue(S.anyOf[1].const === 'y') + }) + it('Should parse Literal Boolean 1', () => { + const T = Parse(`true`) + Assert.IsTrue(T.const === true) + }) + it('Should parse Literal Boolean 2', () => { + const T = Parse(`false`) + Assert.IsTrue(T.const === false) + }) + it('Should parse Literal Number', () => { + const T = Parse(`1`) + Assert.IsTrue(T.const === 1) + }) + it('Should parse Literal String', () => { + const T = Parse(`'1'`) + Assert.IsTrue(T.const === '1') + }) + it('Should parse Mapped (Pending)', () => { + const T = Parse(`{ [K in 1 | 2 | 3]: K }`) + Assert.IsTrue(T.const === 'Mapped types not supported') + }) + it('Should parse Never', () => { + const T = Parse(`never`) + Assert.IsTrue(TypeGuard.IsNever(T)) + }) + it('Should parse Null', () => { + const T = Parse(`null`) + Assert.IsTrue(TypeGuard.IsNull(T)) + }) + it('Should parse Number', () => { + const T = Parse(`number`) + Assert.IsTrue(TypeGuard.IsNumber(T)) + }) + it('Should parse Object 1', () => { + const T = Parse(`{x: boolean, y: number, z: string, }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 2', () => { + const T = Parse(`{x: boolean; y: number; z: string; }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 3', () => { + const T = Parse(`{ + x: boolean + y: number + z: string + }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 4', () => { + const T = Parse(`{ + x: boolean; + y: number; + z: string; + }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 5', () => { + const T = Parse(`{ + x: boolean, + y: number, + z: string, + }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Omit 1', () => { + const T = Parse(`Omit<{ x: boolean, y: number, z: string }, 'z'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Omit 2', () => { + const T = Parse(`Omit<{ x: boolean, y: number, z: string }, 'z' | 'y'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(('y' in T.properties) === false) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Parameters', () => { + const T = Parse(`Parameters<(a: number, b: string) => boolean>`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + it('Should parse Partial', () => { + const T = Parse(`Partial<{ x: boolean, y: number, z: string }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(('required' in T) === false) + }) + it('Should parse Pick 1', () => { + const T = Parse(`Pick<{ x: boolean, y: number, z: string }, 'x' | 'y'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Pick 2', () => { + const T = Parse(`Pick<{ x: boolean, y: number, z: string }, 'x'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(('y' in T.properties) === false) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Promise', () => { + const T = Parse(`Promise`) + Assert.IsTrue(TypeGuard.IsPromise(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.item)) + }) + it('Should parse ReadonlyOptional', () => { + const T = Parse(`{ readonly x?: boolean, readonly y?: number }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) + }) + it('Should parse Readonly', () => { + const T = Parse(`{ readonly x: boolean, readonly y: number }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) + }) + it('Should parse Record 1', () => { + const T = Parse(`Record`) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties['^(.*)$'])) + }) + it('Should parse Record 2', () => { + const T = Parse(`Record`) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties['^(0|[1-9][0-9]*)$'])) + }) + it('Should parse Record 3', () => { + const T = Parse(`Record<'x' | 'y', number>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + }) + it('Should parse Recursive', () => { + const T = Type.Recursive(This => Parse({ This }, `{ id: string, nodes: This[] }`)) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.id)) + Assert.IsTrue(TypeGuard.IsArray(T.properties.nodes)) + Assert.IsTrue(TypeGuard.IsThis(T.properties.nodes.items)) + }) + it('Should parse Ref', () => { + const T = Parse('foo') + Assert.IsTrue(TypeGuard.IsRef(T)) + Assert.IsTrue(T.$ref === 'foo') + }) + it('Should parse Required', () => { + const T = Parse(`Required<{ x?: boolean, y?: number, z?: string }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsEqual(T.required, ['x', 'y', 'z']) + }) + it('Should parse ReturnType 1', () => { + const T = Parse(`ReturnType<() => { x: 1, y: 2 }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(T.properties.x.const === 1) + Assert.IsTrue(T.properties.y.const === 2) + }) + it('Should parse ReturnType 2', () => { + const T = Parse(`ReturnType`) // generalization issue + Assert.IsTrue(T === undefined) + }) + it('Should parse String', () => { + const T = Parse(`string`) + Assert.IsTrue(TypeGuard.IsString(T)) + }) + it('Should parse Symbol', () => { + const T = Parse(`symbol`) + Assert.IsTrue(TypeGuard.IsSymbol(T)) + }) + it('Should parse Tuple', () => { + const T = Parse(`[0, 1, 2, 3]`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(T.items![0].const === 0) + Assert.IsTrue(T.items![1].const === 1) + Assert.IsTrue(T.items![2].const === 2) + Assert.IsTrue(T.items![3].const === 3) + }) + it('Should parse Uint8Array', () => { + const T = Parse(`Uint8Array`) + Assert.IsTrue(TypeGuard.IsUint8Array(T)) + }) + it('Should parse Undefined', () => { + const T = Parse(`undefined`) + Assert.IsTrue(TypeGuard.IsUndefined(T)) + }) + it('Should parse Union 1', () => { + const T = Parse(`1 | 2`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 1) + Assert.IsTrue(T.anyOf[1].const === 2) + }) + it('Should parse Union 2', () => { + const T = Parse(`1 | (2 | 3)`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 1) + Assert.IsTrue(T.anyOf[1].const === 2) + Assert.IsTrue(T.anyOf[2].const === 3) + }) + it('Should parse Unknown', () => { + const T = Parse(`unknown`) + Assert.IsTrue(TypeGuard.IsUnknown(T)) + }) + it('Should parse Void', () => { + const T = Parse(`void`) + Assert.IsTrue(TypeGuard.IsVoid(T)) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index 7ba7f03..811eedc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "paths": { "@sinclair/typebox/compiler": ["src/compiler/index.ts"], "@sinclair/typebox/errors": ["src/errors/index.ts"], + "@sinclair/typebox/parse": ["src/parse/index.ts"], "@sinclair/typebox/system": ["src/system/index.ts"], "@sinclair/typebox/type": ["src/type/index.ts"], "@sinclair/typebox/value": ["src/value/index.ts"],