From cb5c1440e97741bdc2c4650c55ff0cc363f79562 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Fri, 8 Dec 2023 19:07:38 +0200 Subject: [PATCH 1/5] fix: manyTill combinator infinite loop guard reported error in "many" --- src/lib/internal/combinators/many-sep-by.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/internal/combinators/many-sep-by.ts b/src/lib/internal/combinators/many-sep-by.ts index 78e7ef83..44450a65 100644 --- a/src/lib/internal/combinators/many-sep-by.ts +++ b/src/lib/internal/combinators/many-sep-by.ts @@ -76,7 +76,7 @@ export function manySepBy(implDelimeter: ImplicitParjser, max = Inf return; } if (max >= Infinity && ps.position === position) { - Issues.guardAgainstInfiniteLoop("many"); + Issues.guardAgainstInfiniteLoop("manySepBy"); } results.push(ps.value as E); position = ps.position; From 2e59637883ac6508badd2d9e3a6350ba283c659c Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Fri, 8 Dec 2023 19:09:49 +0200 Subject: [PATCH 2/5] refactor: remove extra type cast from manySepBy --- src/lib/internal/combinators/many-sep-by.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/internal/combinators/many-sep-by.ts b/src/lib/internal/combinators/many-sep-by.ts index 44450a65..6d96a080 100644 --- a/src/lib/internal/combinators/many-sep-by.ts +++ b/src/lib/internal/combinators/many-sep-by.ts @@ -3,12 +3,12 @@ */ /** */ +import { ImplicitParjser, ParjsCombinator } from "../../index"; import { Issues } from "../issues"; +import { ParjserBase } from "../parser"; import { ResultKind } from "../result"; -import { ParsingState } from "../state"; -import { ImplicitParjser, ParjsCombinator, Parjser } from "../../index"; import { ScalarConverter } from "../scalar-converter"; -import { ParjserBase } from "../parser"; +import { ParsingState } from "../state"; import { defineCombinator } from "./combinator"; export type ArrayWithSeparators = Normal[] & { @@ -40,7 +40,7 @@ export function manySepBy( ): ParjsCombinator>; export function manySepBy(implDelimeter: ImplicitParjser, max = Infinity) { - const delimeter = ScalarConverter.convert(implDelimeter) as ParjserBase & Parjser; + const delimeter = ScalarConverter.convert(implDelimeter) as ParjserBase; return defineCombinator(source => { return new (class extends ParjserBase { type = "manySepBy"; From 9c29e2468ba8fc72c9cbad7361f7dfa6db90c528 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Sat, 16 Dec 2023 09:23:39 +0200 Subject: [PATCH 3/5] refactor: scalar converter is not a namespace but an object --- src/lib/internal/scalar-converter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/internal/scalar-converter.ts b/src/lib/internal/scalar-converter.ts index 633c235f..a81fbcef 100644 --- a/src/lib/internal/scalar-converter.ts +++ b/src/lib/internal/scalar-converter.ts @@ -45,12 +45,12 @@ export type ImplicitParjser = Parjser | ConvertibleScalar; /** * A helper for working with implicit parsers. */ -export namespace ScalarConverter { +export const ScalarConverter = { /** * Normalizes scalars and Parjsers into Parjsers. * @param scalarOrParjser The literal or parjser. */ - export function convert(scalarOrParjser: ImplicitParjser): Parjser { + convert(scalarOrParjser: ImplicitParjser): Parjser { if (typeof scalarOrParjser === "string") { return string(scalarOrParjser) as Parjser; } else if (scalarOrParjser instanceof RegExp) { @@ -59,4 +59,4 @@ export namespace ScalarConverter { return scalarOrParjser as Parjser; } } -} +}; From 782897fa196f7feb8595d83d70bf42c4d5495c18 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Sun, 17 Dec 2023 14:52:31 +0200 Subject: [PATCH 4/5] chore: eslint disallows namespace usage --- .eslintrc.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b9c1a164..9c8c8c9d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,7 @@ -const path = require("path"); +// @ts-check -module.exports = { +/** @type {import("@typescript-eslint/utils/dist/ts-eslint/Linter").Linter.Config} c */ +const config = { root: true, extends: [ "@gregros/eslint-config", @@ -10,10 +11,8 @@ module.exports = { parser: "@typescript-eslint/parser", plugins: ["@typescript-eslint", "jest"], rules: { - // 'any' is used in many places to simplify the type system - "@typescript-eslint/no-explicit-any": "warn", - - "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-namespace": "error", // eslint's rule needs to be switched off in favour of the typescript one "@typescript-eslint/no-shadow": "error", @@ -24,3 +23,4 @@ module.exports = { project: "**/tsconfig.json" } }; +module.exports = config; From 3da6f62ada0725dc553454577f2e581dbcfa9cf7 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Sun, 17 Dec 2023 16:30:07 +0200 Subject: [PATCH 5/5] refactor!: remove all ts namespaces This is technically a breaking change because the types are no longer accessed through the namespace qualifiers. It should be very easy to resolve, though. --- src/lib/internal/combinators/recover.ts | 6 +-- src/lib/internal/functions/helpers.ts | 25 +++++----- src/lib/internal/issues.ts | 14 +++--- src/lib/internal/result.ts | 61 +++++++++++++------------ src/test/helpers/jest-setup.ts | 1 + 5 files changed, 55 insertions(+), 52 deletions(-) diff --git a/src/lib/internal/combinators/recover.ts b/src/lib/internal/combinators/recover.ts index 92315793..025d8433 100644 --- a/src/lib/internal/combinators/recover.ts +++ b/src/lib/internal/combinators/recover.ts @@ -4,7 +4,7 @@ /** */ import { ParjsCombinator } from "../../"; -import { FailureInfo, ResultKind, SuccessInfo } from "../result"; +import { FailureInfo, ResultKindFail, SuccessInfo } from "../result"; import { ParsingState, UserState } from "../state"; import { ParjserBase } from "../parser"; @@ -25,7 +25,7 @@ export interface ParserFailureState { /** * The severity of the failure. */ - readonly kind: ResultKind.Fail; + readonly kind: ResultKindFail; } /** @@ -50,7 +50,7 @@ export function recover(recoverFunction: RecoveryFunction): ParjsCombinato if (ps.isOk || ps.isFatal) return; const result = recoverFunction({ userState: ps.userState, - kind: ps.kind as ResultKind.Fail, + kind: ps.kind as ResultKindFail, reason: ps.reason! // the error is guaranteed to be non-null } satisfies ParserFailureState); if (!result) return; diff --git a/src/lib/internal/functions/helpers.ts b/src/lib/internal/functions/helpers.ts index 8213b6a7..a8141120 100644 --- a/src/lib/internal/functions/helpers.ts +++ b/src/lib/internal/functions/helpers.ts @@ -4,37 +4,38 @@ import repeat from "lodash/repeat"; +/** + * Recursively applies join to an array of arrays. + * @param arr + */ +type StringOrArray = string | StringOrArray[]; + /** * Some simple helpers for working with strings. */ -export namespace StringHelpers { - /** - * Recursively applies join to an array of arrays. - * @param arr - */ - type StringOrArray = string | StringOrArray[]; - export function recJoin(arr: StringOrArray): string { +export const StringHelpers = { + recJoin(arr: StringOrArray): string { if (arr instanceof Array) { - return arr.map(x => recJoin(x)).join(""); + return arr.map(x => this.recJoin(x)).join(""); } else { return String(arr); } } -} +}; /** * Helpers for working with numbers. */ -export namespace NumHelpers { +export const NumHelpers = { /** * Pads an integer with characters. * @param n * @param digits * @param char */ - export function padInt(n: number, digits: number, char: string) { + padInt(n: number, digits: number, char: string) { const str = n.toString(); if (str.length >= digits) return str; return repeat(char, digits - str.length) + str; } -} +}; diff --git a/src/lib/internal/issues.ts b/src/lib/internal/issues.ts index f5c2a2bd..f6e46c6c 100644 --- a/src/lib/internal/issues.ts +++ b/src/lib/internal/issues.ts @@ -7,24 +7,24 @@ import { ParserDefinitionError } from "../errors"; /** * Some canned error throwers. */ -export namespace Issues { +export const Issues = { /** * Throws an error saying that the parser is about to enter an infinite * loop. * @param name */ - export function guardAgainstInfiniteLoop(name: string): never { + guardAgainstInfiniteLoop(name: string): never { throw new ParserDefinitionError( name, `The combinator '${name}' expected one of its arguments to change the parser state.` ); - } + }, - export function delayedParserNotInit(name: string): never { + delayedParserNotInit(name: string): never { throw new ParserDefinitionError(name, `Delayed parser not initalized.`); - } + }, - export function delayedParserAlreadyInit(): never { + delayedParserAlreadyInit(): never { throw new ParserDefinitionError("", `Delayed parser has already been initialized`); } -} +}; diff --git a/src/lib/internal/result.ts b/src/lib/internal/result.ts index 220f57f3..218a9192 100644 --- a/src/lib/internal/result.ts +++ b/src/lib/internal/result.ts @@ -33,7 +33,7 @@ export class ParjsSuccess implements SuccessInfo { * Info about a success. */ export interface SuccessInfo { - kind: ResultKind.Ok; + kind: ResultKindOk; value: T; } @@ -41,7 +41,7 @@ export interface SuccessInfo { * Info about a potential failure. */ export interface FailureInfo { - kind: ResultKind.Fail; + kind: ResultKindFail; reason: string; } @@ -113,47 +113,48 @@ export type ParjsResult = ParjsSuccess | ParjsFailure; /** * Namespace that contains the different reply kinds/error levels. */ -export namespace ResultKind { - /** - * The OK reply type. - */ - export type Ok = "OK"; - /** - * The soft failure type. - */ - export type SoftFail = "Soft"; - /** - * The hard failure type. - */ - export type HardFail = "Hard"; - /** - * The fatal failure type. - */ - export type FatalFail = "Fatal"; - +export const ResultKind = { /** * An OK reply. */ - export const Ok: Ok = "OK"; + Ok: "OK" as const, /** * A soft failure reply. */ - export const SoftFail: SoftFail = "Soft"; + SoftFail: "Soft" as const, /** * A hard failure reply. */ - export const HardFail: HardFail = "Hard"; + HardFail: "Hard" as const, /** * A fatal failure reply. */ - export const FatalFail: FatalFail = "Fatal"; + FatalFail: "Fatal" as const +}; + +/** + * The OK reply type. + */ +export type ResultKindOk = typeof ResultKind.Ok; +/** + * The soft failure type. + */ +export type ResultKindSoftFail = typeof ResultKind.SoftFail; +/** + * The hard failure type. + */ +export type ResultKindHardFail = typeof ResultKind.HardFail; +/** + * The fatal failure type. + */ +export type ResultKindFatalFail = typeof ResultKind.FatalFail; + +/** + * Specifies any kind of failure. + */ +export type ResultKindFail = ResultKindHardFail | ResultKindFatalFail | ResultKindSoftFail; - /** - * Specifies any kind of failure. - */ - export type Fail = HardFail | FatalFail | SoftFail; -} /** * Specifies a reply kind, indicating success or failure, and the severity of the failure. */ -export type ResultKind = ResultKind.Ok | ResultKind.Fail; +export type ResultKind = ResultKindOk | ResultKindFail; diff --git a/src/test/helpers/jest-setup.ts b/src/test/helpers/jest-setup.ts index 46537245..d8d6539f 100644 --- a/src/test/helpers/jest-setup.ts +++ b/src/test/helpers/jest-setup.ts @@ -87,6 +87,7 @@ expect.extend({ }); declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace namespace jest { interface Matchers { toBeSuccessful(value?: T): R;