diff --git a/.vscode/settings.json b/.vscode/settings.json index fd4665b..2f13220 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,4 +6,4 @@ }, "deno.enable": true, "deno.lint": false, -} \ No newline at end of file +} diff --git a/deno.jsonc b/deno.jsonc index f0db3c0..0af3cfb 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -17,7 +17,11 @@ // -------------------------------------------------------------- // ParseBox // -------------------------------------------------------------- - "@sinclair/parsebox": "./src/index.ts" + "@sinclair/parsebox": "./src/index.ts", + // -------------------------------------------------------------- + // Mitata + // -------------------------------------------------------------- + "mitata": "npm:mitata" }, "fmt": { "lineWidth": 240, @@ -27,4 +31,4 @@ "compilerOptions": { "strict": true } -} \ No newline at end of file +} diff --git a/deno.lock b/deno.lock index a77b2cd..fb4e16e 100644 --- a/deno.lock +++ b/deno.lock @@ -5,7 +5,8 @@ "jsr:@std/internal@^1.0.6": "1.0.6", "jsr:@std/path@1.0.8": "1.0.8", "npm:@sinclair/typebox@*": "0.34.33", - "npm:@types/node@*": "22.15.15" + "npm:@types/node@*": "22.15.15", + "npm:mitata@*": "1.0.34" }, "jsr": { "@std/assert@1.0.12": { @@ -31,6 +32,9 @@ "undici-types" ] }, + "mitata@1.0.34": { + "integrity": "sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA==" + }, "undici-types@6.21.0": { "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" } @@ -170,5 +174,10 @@ "https://raw.githubusercontent.com/sinclairzx81/tasksmith/0.8.5/src/test/test.ts": "894650a3af2f7c0c8e4e4f0930d6f3e7bfc5187c5c66ce869c0429b942a51c2d", "https://raw.githubusercontent.com/sinclairzx81/tasksmith/0.8.5/src/tsc/index.ts": "ef3fd7538b1f3d85e102f4b6317ad92e2f1dc007df6d7d7cceb283913e71117d", "https://raw.githubusercontent.com/sinclairzx81/tasksmith/0.8.5/src/tsc/tsc.ts": "2d84d6b8bf155811eec128a03095dbad9f15feb7602c7fc3c6a9e0c36dc38602" + }, + "workspace": { + "dependencies": [ + "npm:mitata@*" + ] } } diff --git a/src/token/bigint.ts b/src/token/bigint.ts index c397d3c..d4b3549 100644 --- a/src/token/bigint.ts +++ b/src/token/bigint.ts @@ -44,14 +44,13 @@ type TTakeBigInt = ( ) function TakeBigInt(input: Input): TTakeBigInt { const integer = Integer(input) - return ( - IsResult(integer) ? (() => { - const n = Take(['n'], integer[1]) - return IsResult(n) - ? [`${integer[0]}`, n[1]] - : [] // fail: did not match 'n' - })() : [] // fail: did not match Integer - ) as never + if (IsResult(integer)) { + const n = Take(['n'], integer[1]) + if (IsResult(n)) + return [integer[0], n[1]] as never + } + + return [] as never } // ------------------------------------------------------------------ // BigInt @@ -63,4 +62,4 @@ export type TBigInt = ( /** Matches if next is a Integer literal with trailing 'n'. Trailing 'n' is omitted in result. */ export function BigInt(input: Input): TBigInt { return TakeBigInt(input) as never -} \ No newline at end of file +} diff --git a/src/token/ident.ts b/src/token/ident.ts index eedc472..f0b98ae 100644 --- a/src/token/ident.ts +++ b/src/token/ident.ts @@ -46,9 +46,6 @@ const Initial = [...Alpha, UnderScore, DollarSign] type TTakeInitial = ( TTake ) -function TakeInitial(input: Input): TTakeInitial { - return Take(Initial, input) as never -} // ------------------------------------------------------------------ // TakeRemaining // ------------------------------------------------------------------ @@ -61,7 +58,7 @@ type TTakeRemaining = ( : [Result, Input] ) function TakeRemaining(input: Input, result: string = ''): TTakeRemaining { - const remaining = Take(Remaining, input) as string[] + const remaining = Take(Remaining, input) return ( IsResult(remaining) ? TakeRemaining(remaining[1], `${result}${remaining[0]}`) @@ -79,15 +76,17 @@ type TTakeIdent = ( : [] // fail: did not match Initial ) function TakeIdent(input: Input): TTakeIdent { - const initial = TakeInitial(input) as string[] - return ( - IsResult(initial) ? (() => { - const remaining = TakeRemaining(initial[1]) - return IsResult(remaining) - ? [`${initial[0]}${remaining[0]}`, remaining[1]] - : [] // fail: did not match Remaining - })() : [] // fail: did not match Initial - ) as never + const initial = Take(Initial, input) as [string, string] | [] + if (IsResult(initial)) { + const remaining = TakeRemaining(initial[1]) + if (IsResult(remaining)) + return [initial[0] + remaining[0], remaining[1]] as never + + // fail: did not match Remaining + } + + // fail: did not match Initial + return [] as never; } // ------------------------------------------------------------------ // Ident @@ -99,4 +98,4 @@ export type TIdent = ( /** Matches if next is an Ident */ export function Ident(input: Input): TIdent { return TakeIdent(Trim(input)) as never -} \ No newline at end of file +} diff --git a/src/token/integer.ts b/src/token/integer.ts index 008c4dd..565c2f2 100644 --- a/src/token/integer.ts +++ b/src/token/integer.ts @@ -65,7 +65,7 @@ type TAllowedDigits = [...TDigit, TUnderScore] const AllowedDigits = [...Digit, UnderScore] as TAllowedDigits // ... type TTakeDigits = ( - TMany + TMany ) function TakeDigits(input: Input): TTakeDigits { return Many(AllowedDigits, [UnderScore], input) as never @@ -86,22 +86,25 @@ type TTakeInteger = ( ) function TakeInteger(input: Input): TTakeInteger { const sign = TakeSign(input) - return ( - IsResult(sign) ? (() => { - const zero = Take([Zero], sign[1]) - return IsResult(zero) - ? [`${sign[0]}${zero[0]}`, zero[1]] - : (() => { - const nonZero = TakeNonZero(sign[1]) - return IsResult(nonZero) ? (() => { - const digits = TakeDigits(nonZero[1]) - return IsResult(digits) - ? [`${sign[0]}${nonZero[0]}${digits[0]}`, digits[1]] - : [] // fail: did not match Digits - })() : [] // fail: did not match NonZero - })() - })() : [] // fail: did not match Sign - ) as never + if (IsResult(sign)) { + const zero = Take([Zero], sign[1]) + if (IsResult(zero)) + return [sign[0] + zero[0], zero[1]] as never + + const nonZero = TakeNonZero(sign[1]) + if (IsResult(nonZero)) { + const digits = TakeDigits(nonZero[1]) + if (IsResult(digits)) + return [sign[0] + nonZero[0] + digits[0], digits[1]] as never + + // fail: did not match Digits + } + + // fail: did not match NonZero + } + + // fail: did not match Sign + return [] as never } // ------------------------------------------------------------------ // Integer @@ -114,4 +117,3 @@ export type TInteger = ( export function Integer(input: Input): TInteger { return TakeInteger(Trim(input)) as never } - diff --git a/src/token/internal/char.ts b/src/token/internal/char.ts index 791d96c..46d6898 100644 --- a/src/token/internal/char.ts +++ b/src/token/internal/char.ts @@ -32,7 +32,9 @@ THE SOFTWARE. // Range // ------------------------------------------------------------------ function Range(start: number, end: number): string[] { - return Array.from({ length: end - start + 1 }, (_, i) => String.fromCharCode(start + i)) + return new Array(end - start + 1) + .fill(start) + .map((start, i) => String.fromCharCode(start + i)) } // ------------------------------------------------------------------ // Alphas @@ -49,7 +51,7 @@ export type TAlpha = [ export const Alpha = [ ...Range(97, 122), // Lowercase ...Range(65, 90) // Uppercase -] as TAlpha +] as TAlpha // ------------------------------------------------------------------ // Digits diff --git a/src/token/internal/optional.ts b/src/token/internal/optional.ts index 2f019c0..2756f64 100644 --- a/src/token/internal/optional.ts +++ b/src/token/internal/optional.ts @@ -42,8 +42,8 @@ export function Optional (value: Value, input: Input): TOptional { const result = Take([value], input) return ( - IsResult(result) - ? result + IsResult(result) + ? result : ['', input] ) as never -} \ No newline at end of file +} diff --git a/src/token/internal/result.ts b/src/token/internal/result.ts index 4b9a3b8..777e1be 100644 --- a/src/token/internal/result.ts +++ b/src/token/internal/result.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsEqual } from './guard.ts' +import { IsEqual } from './guard.ts' /** Checks the value is a Tuple-2 [string, string] result */ -export function IsResult(value: unknown): value is [string, string] { - return IsArray(value) && IsEqual(value.length, 2) +export function IsResult(value: [] | [string, string]): value is [string, string] { + return IsEqual(value.length, 2) } diff --git a/src/token/internal/take.ts b/src/token/internal/take.ts index f55d389..d72c029 100644 --- a/src/token/internal/take.ts +++ b/src/token/internal/take.ts @@ -28,20 +28,21 @@ THE SOFTWARE. // deno-fmt-ignore-file -import { IsEqual, IsString } from './guard.ts' +import { IsEqual } from './guard.ts' +import { IsResult } from './result.ts' // ------------------------------------------------------------------ // TakeString // ------------------------------------------------------------------ type TTakeVariant = ( - Input extends `${Variant}${infer Rest extends string}` + Input extends `${Variant}${infer Rest extends string}` ? [Variant, Rest] : [] ) function TakeVariant(variant: Variant, input: Input): TTakeVariant { return ( - IsEqual(input.indexOf(variant), 0) - ? [variant, input.slice(variant.length)] + IsEqual(input.indexOf(variant), 0) + ? [variant, input.slice(variant.length)] : [] ) as never } @@ -51,20 +52,19 @@ function TakeVariant(variant: Vari /** Takes one of the given variants or fail */ export type TTake = ( Variants extends [infer ValueLeft extends string, ...infer ValueRight extends string[]] - ? TTakeVariant extends [infer Take extends string, infer Rest extends string] + ? TTakeVariant extends [infer Take extends string, infer Rest extends string] ? [Take, Rest] : TTake : [] ) /** Takes one of the given variants or fail */ export function Take(variants: [...Variants], input: Input): TTake { - const [left, ...right] = variants - return ( - IsString(left) - ? (() => { - const result = TakeVariant(left, input) - return IsEqual(result.length, 2) ? result : Take(right, input) - })() - : [] - ) as never + for (let i = 0; i < variants.length; i++) { + const result = TakeVariant(variants[i], input) as [] | [string, string] + if (IsResult(result)) { + return result as never + } + } + + return [] as never } diff --git a/src/token/internal/trim.ts b/src/token/internal/trim.ts index cf278f9..e4d7d25 100644 --- a/src/token/internal/trim.ts +++ b/src/token/internal/trim.ts @@ -58,8 +58,8 @@ function DiscardMultilineComment(input: Input): TDiscardMu // DiscardLineComment // ------------------------------------------------------------------ type TDiscardLineComment = ( - Input extends `${string}${Char.TNewLine}${infer Rest extends string}` - ? TTrimWhitespace<`${Char.TNewLine}${Rest}`> + Input extends `${string}${Char.TNewLine}${infer Rest extends string}` + ? TTrimWhitespace<`${Char.TNewLine}${Rest}`> : '' ) function DiscardLineComment(input: Input): TDiscardLineComment { @@ -121,4 +121,3 @@ export function Trim(input: Input): TTrim { trimmed ) as never } - diff --git a/src/token/number.ts b/src/token/number.ts index 2de08df..6bc22a9 100644 --- a/src/token/number.ts +++ b/src/token/number.ts @@ -77,11 +77,9 @@ type TTakeFractional = ( function TakeFractional(input: Input): TTakeFractional { const digits = Many(AllowedDigits, [UnderScore], input) return ( - IsResult(digits) - ? IsEqual(digits[0], '') - ? [] // fail: no Digits - : [digits[0], digits[1]] - : [] // fail: did not match Digits + IsResult(digits) && !IsEqual(digits[0], '') + ? [digits[0], digits[1]] + : [] // fail: did not match Digits or no Digits ) as never } // ------------------------------------------------------------------ @@ -95,17 +93,19 @@ type TLeadingDot = ( : [] // fail: did not match Dot ) function LeadingDot - (sign: Sign, input: Input): + (sign: Sign, input: Input): TLeadingDot { const dot = Take([Dot], input) - return ( - IsResult(dot) ? (() => { - const fractional = TakeFractional(dot[1]) - return IsResult(fractional) - ? [`${sign}0${dot[0]}${fractional[0]}`, fractional[1]] - : [] // fail: did not match Fractional - })() : [] // fail: did not match Dot - ) as never + if (IsResult(dot)) { + const fractional = TakeFractional(dot[1]) + if (IsResult(fractional)) + return [sign + '0' + dot[0] + fractional[0], fractional[1]] as never + + // fail: did not match Fractional + } + + // fail: did not match Dot + return [] as never } // ------------------------------------------------------------------ // TakeLeadingInteger @@ -120,19 +120,21 @@ type TLeadingInteger = ( : [] // fail: did not match Integer ) function LeadingInteger - (sign: Sign, input: Input): + (sign: Sign, input: Input): TLeadingInteger { const integer = Integer(input) + if (!IsResult(integer)) + return [] as never // fail: did not match Integer + + const dot = Take([Dot], integer[1]) + if (!IsResult(dot)) + return [sign + integer[0], integer[1]] as never // fail: did not match Dot, use Integer + + const fractional = TakeFractional(dot[1]) return ( - IsResult(integer) ? (() => { - const dot = Take([Dot], integer[1]) - return IsResult(dot) ? (() => { - const fractional = TakeFractional(dot[1]) - return IsResult(fractional) - ? [`${sign}${integer[0]}${dot[0]}${fractional[0]}`, fractional[1]] - : [`${sign}${integer[0]}`, dot[1]] // fail: did not match Fractional, use Integer - })() : [`${sign}${integer[0]}`, integer[1]] // fail: did not match Dot, use Integer - })() : [] // fail: did not match Integer + IsResult(fractional) + ? [sign + integer[0] + dot[0] + fractional[0], fractional[1]] + : [sign + integer[0], dot[1]] // fail: did not match Fractional, use Integer ) as never } // ------------------------------------------------------------------ @@ -165,4 +167,4 @@ export type TNumber = ( /** Matches if next is a literal Number */ export function Number(input: Input): TNumber { return TakeNumber(Trim(input)) as never -} \ No newline at end of file +} diff --git a/src/token/span.ts b/src/token/span.ts index 6dc7470..cc88508 100644 --- a/src/token/span.ts +++ b/src/token/span.ts @@ -46,18 +46,18 @@ type TMultiLine : [] // fail: did not match Start ) function MultiLine - (start: Start, end: End, input: Input): + (start: Start, end: End, input: Input): TMultiLine { - return ( - input.startsWith(start) ? (() => { - const until = Until([end], input.slice(start.length)) - return IsResult(until) ? (() => { - return until[1].startsWith(end) - ? [`${until[0]}`, until[1].slice(end.length)] - : [] // fail: did not match End - })() : [] // fail: did not match Until - })() : [] // fail: did not match Start - ) as never + if (input.startsWith(start)) { + const until = Until([end], input.slice(start.length)) + if (IsResult(until) && until[1].startsWith(end)) + return [until[0], until[1].slice(end.length)] as never + + // fail: did not match End or Until + } + + // fail: did not match Start + return [] as never } // ------------------------------------------------------------------ // SingleLine @@ -72,18 +72,18 @@ type TSingleLine : [] // fail: not match Start ) function SingleLine - (start: Start, end: End, input: Input): + (start: Start, end: End, input: Input): TSingleLine { - return ( - input.startsWith(start) ? (() => { - const until = Until([NewLine, end], input.slice(start.length)) - return IsResult(until) ? (() => { - return until[1].startsWith(end) - ? [`${until[0]}`, until[1].slice(end.length)] - : [] // fail: did not match End - })() : [] // fail: did not match Until - })() : [] // fail: not match Start - ) as never + if (input.startsWith(start)) { + const until = Until([NewLine, end], input.slice(start.length)) + if (IsResult(until) && until[1].startsWith(end)) + return [until[0], until[1].slice(end.length)] as never + + // fail: did not match End or Until + } + + // fail: not match Start + return [] as never } // ------------------------------------------------------------------ // Span @@ -96,11 +96,11 @@ export type TSpan - (start: Start, end: End, multiLine: MultiLine, input: Input): + (start: Start, end: End, multiLine: MultiLine, input: Input): TSpan { return ( multiLine ? MultiLine(start, end, Trim(input)) : SingleLine(start, end, Trim(input)) ) as never -} \ No newline at end of file +} diff --git a/src/token/string.ts b/src/token/string.ts index 848d513..6a2fcd8 100644 --- a/src/token/string.ts +++ b/src/token/string.ts @@ -60,10 +60,10 @@ type TTakeString = ( : [] // fail: did not match Initial ) function TakeString(quotes: [...Quotes], input: Input): TTakeString { - const initial = TakeInitial(quotes, input) as [string, string] + const initial = TakeInitial(quotes, input) return ( IsResult(initial) - ? TakeSpan(initial[0], `${initial[0]}${initial[1]}`) + ? TakeSpan(initial[0], initial[0] + initial[1]) : [] // fail: did not match Initial ) as never } @@ -74,4 +74,4 @@ export type TString = ( /** Matches a literal String with the given quotes */ export function String(quotes: [...Quotes], input: Input): TString { return TakeString(quotes, Trim(input)) as never -} \ No newline at end of file +} diff --git a/src/token/until.ts b/src/token/until.ts index f3431c0..fda200c 100644 --- a/src/token/until.ts +++ b/src/token/until.ts @@ -28,29 +28,27 @@ THE SOFTWARE. // deno-fmt-ignore-file -import { IsEqual, IsString } from './internal/guard.ts' +import { IsEqual } from './internal/guard.ts' // ------------------------------------------------------------------ // IsEnd // ------------------------------------------------------------------ type TIsEnd = ( - End extends [infer Left extends string, ...infer Right extends string[]] - ? Input extends `${Left}${string}` + End extends [infer Left extends string, ...infer Right extends string[]] + ? Input extends `${Left}${string}` ? true : TIsEnd : false ) function IsEnd - (end: [...End], input: Input): + (end: [...End], input: Input): TIsEnd { - const [left, ...right] = end - return ( - IsString(left) - ? input.startsWith(left) - ? true - : IsEnd(right, input) - : false - ) as never + for (let i = 0; i < end.length; i++) { + if (input.startsWith(end[i])) + return true as never; + } + + return false as never; } // ------------------------------------------------------------------ // Until @@ -59,21 +57,22 @@ function IsEnd export type TUntil = ( Input extends `` ? [] // fail: Input is empty - : TIsEnd extends true + : TIsEnd extends true ? [Result, Input] - : Input extends `${infer Left extends string}${infer Right extends string}` + : Input extends `${infer Left extends string}${infer Right extends string}` ? TUntil : [] ) /** Match Input until but not including End. No match if End not found. */ export function Until (end: [...End], input: Input, result: string = ''): TUntil { - return ( - IsEqual(input, '') - ? [] // fail: Input is empty - : IsEnd(end, input) ? [result, input] : (() => { - const [left, right] = [input.slice(0, 1), input.slice(1)] - return Until(end, right, `${result}${left}`) - })() - ) as never + if (IsEqual(input, '')) + return [] as never // fail: Input is empty + + if (IsEnd(end, input)) + return [result, input] as never + + const left = input.slice(0, 1), + right = input.slice(1) + return Until(end, right, result + left) as never } diff --git a/src/token/until_1.ts b/src/token/until_1.ts index e95514c..d9bf9e3 100644 --- a/src/token/until_1.ts +++ b/src/token/until_1.ts @@ -37,22 +37,20 @@ import { type TUntil, Until } from './until.ts' // ------------------------------------------------------------------ /** Match Input until but not including End. No match if End not found or match is zero-length. */ export type TUntil_1 = ( - TUntil extends [infer Until extends string, infer UntilRest extends string] - ? Until extends '' + TUntil extends [infer Until extends string, infer UntilRest extends string] + ? Until extends '' ? [] // fail: match has no characters : [Until, UntilRest] : [] // fail: did not match Until ) /** Match Input until but not including End. No match if End not found or match is zero-length. */ export function Until_1 - (end: [...End], input: Input): + (end: [...End], input: Input): TUntil_1 { const until = Until(end, input) return ( - IsResult(until) - ? IsEqual(until[0], '') - ? [] // fail: match has no characters - : until - : [] // fail: did not match Until + IsResult(until) && !IsEqual(until[0], '') + ? until + : [] // fail: match has no characters or did not match Until ) as never }