Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .madgerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"detectiveOptions": {
"ts": {
"skipTypeImports": true
}
}
}
25 changes: 25 additions & 0 deletions packages/parjs-vitest-error/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.yarn
1 change: 1 addition & 0 deletions packages/parjs-vitest-error/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.9.0
1 change: 1 addition & 0 deletions packages/parjs-vitest-error/.yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
6 changes: 6 additions & 0 deletions packages/parjs-vitest-error/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
To recreate:

- `yarn` in the repo root directory to install packages
- `yarn test` to see test hang forever

The line causing the problem is line 13 in parse.test.ts. Comment it out and restart vitest to see the test pass.
20 changes: 20 additions & 0 deletions packages/parjs-vitest-error/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "perror",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "tsc && vite build",
"dev": "vite",
"preview": "vite preview",
"test": "vitest"
},
"devDependencies": {
"typescript": "5.5.4",
"vite": "5.4.0",
"vitest": "2.0.5"
},
"peerDependencies": {
"parjs": "*"
}
}
17 changes: 17 additions & 0 deletions packages/parjs-vitest-error/src/parse-hangs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expect, test } from "vitest";

import { float } from "parjs";
import { between } from "parjs/combinators";

const sum = (a: number, b: number) => a + b;

test("adds 1 + 2 to equal 3", () => {
const tupleElement = float();

// this line causes the infinite loop or whatever.
// comment out this line, abort vitest, then re-run vitest to watch the test pass
// const paddedElement = tupleElement.pipe(between(whitespace()));
const paddedElement = tupleElement.pipe(between(null as any));

expect(sum(1, 2)).toBe(3);
});
23 changes: 23 additions & 0 deletions packages/parjs-vitest-error/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
29 changes: 29 additions & 0 deletions packages/parjs/src/internal/ParjsFailure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ParjsParsingFailure } from "../errors";
import type { FailureInfo, Trace } from "./result";
import { visualizeTrace } from "./trace-visualizer";

/** A failure result from a Parjs parser. */

export class ParjsFailure implements FailureInfo {
constructor(public trace: Trace) {}

get value(): never {
throw new ParjsParsingFailure(this);
}

get kind() {
return this.trace.kind;
}

get reason() {
return this.trace.reason;
}

toString() {
return visualizeTrace(this.trace);
}
/** Whether this result is an OK. */
get isOk() {
return false;
}
}
2 changes: 1 addition & 1 deletion packages/parjs/src/internal/combinators/between.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ImplicitParjser, ParjsCombinator } from "../../index";
import { wrapImplicit } from "../wrap-implicit";
import { wrapImplicit } from "../parser";
import { defineCombinator } from "./combinator";
import { qthen, thenq } from "./then";

Expand Down
5 changes: 3 additions & 2 deletions packages/parjs/src/internal/combinators/combinator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ImplicitParjser, ParjsCombinator, Parjser } from "../../index";
import type { CombinatorInput } from "../combinated";
import { wrapImplicit } from "../wrap-implicit";
import type { ParjsCombinator, Parjser } from "../parjser";
import type { ImplicitParjser } from "../parser";
import { wrapImplicit } from "../parser";

/**
* Represents the given function as a Parjs combinator.
Expand Down
2 changes: 1 addition & 1 deletion packages/parjs/src/internal/combinators/exactly.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { ImplicitParjser } from "../../index";
import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import { wrapImplicit } from "../parser";
import { ResultKind } from "../result";
import type { ParsingState } from "../state";
import { wrapImplicit } from "../wrap-implicit";

class Exactly<T> extends Combinated<T, T[]> {
type = "exactly";
Expand Down
3 changes: 1 addition & 2 deletions packages/parjs/src/internal/combinators/many-sep-by.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import type { ImplicitParjser, ParjsCombinator } from "../../index";
import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import { Issues } from "../issues";
import type { ParjserBase } from "../parser";
import { wrapImplicit, type ParjserBase } from "../parser";
import { ResultKind } from "../result";
import type { ParsingState } from "../state";
import { wrapImplicit } from "../wrap-implicit";

export type ArrayWithSeparators<Normal, Separator> = Normal[] & {
separators: Separator[];
Expand Down
5 changes: 3 additions & 2 deletions packages/parjs/src/internal/combinators/many-till.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { ImplicitParjser, ParjsCombinator } from "../../index";
import type { ParjsCombinator } from "../../index";
import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import { Issues } from "../issues";
import type { ImplicitParjser } from "../parser";
import { wrapImplicit } from "../parser";
import { ResultKind } from "../result";
import type { ParsingState, UserState } from "../state";
import { wrapImplicit } from "../wrap-implicit";
import { pipe } from "./combinator";
import { qthen } from "./then";

Expand Down
2 changes: 1 addition & 1 deletion packages/parjs/src/internal/combinators/many.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { ParsingState } from "../state";

import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import { wrapImplicit } from "../wrap-implicit";
import { wrapImplicit } from "../parser";

class Many<T> extends Combinated<T, T[]> {
type = "many";
Expand Down
4 changes: 2 additions & 2 deletions packages/parjs/src/internal/combinators/or.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import type { ParjsCombinator } from "../parjser";
import type { ImplicitParjser } from "../parser";
import { wrapImplicit } from "../parser";
import { ResultKind } from "../result";
import type { ParsingState } from "../state";
import type { ImplicitParjser } from "../wrap-implicit";
import { wrapImplicit } from "../wrap-implicit";

import type { getParsedType } from "../util-types";

Expand Down
4 changes: 2 additions & 2 deletions packages/parjs/src/internal/combinators/reason.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import type { ParjsCombinator } from "../parjser";
import type { ImplicitParjser } from "../parser";
import { wrapImplicit } from "../parser";
import type { FailureInfo } from "../result";
import type { ParsingState } from "../state";
import type { ImplicitParjser } from "../wrap-implicit";
import { wrapImplicit } from "../wrap-implicit";

class Expects<T> extends Combinated<T, T> {
type = "expects";
Expand Down
3 changes: 1 addition & 2 deletions packages/parjs/src/internal/combinators/then-pick.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import type { ParjsCombinator, ParjsProjection } from "../parjser";
import { wrapImplicit, type ImplicitParjser } from "../parser";
import type { ParsingState } from "../state";
import type { ImplicitParjser } from "../wrap-implicit";
import { wrapImplicit } from "../wrap-implicit";

class ThenPick<A, B> extends Combinated<A, B> {
type = "then-pick";
Expand Down
2 changes: 1 addition & 1 deletion packages/parjs/src/internal/combinators/then.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { ParsingState } from "../state";

import type { CombinatorInput } from "../combinated";
import { Combinated } from "../combinated";
import { wrapImplicit } from "../wrap-implicit";
import { composeCombinator } from "./combinator";
import { map } from "./map";

import { wrapImplicit } from "../parser";
import type { getParsedType } from "../util-types";

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/parjs/src/internal/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export { ParjserBase, ParserUserState } from "./parser";
export type { ParsingState, UserState } from "./state";

export { ParjsFailure } from "./ParjsFailure";
export { composeCombinator, defineCombinator } from "./combinators";
export type { ParjsCombinator, ParjsProjection, ParjsValidator, Parjser } from "./parjser";
export { regexp, string } from "./parser";
export { regexp, string, wrapImplicit } from "./parser";
export {
anyChar,
anyCharOf,
Expand Down Expand Up @@ -38,7 +39,6 @@ export {
whitespace
} from "./parsers";
export type { FloatOptions, IntOptions } from "./parsers";
export { ParjsFailure, ParjsSuccess, ResultKind } from "./result";
export { ParjsSuccess, ResultKind } from "./result";
export type { ErrorLocation, FailureInfo, ParjsResult, SuccessInfo, Trace } from "./result";
export { BasicParsingState, FAIL_RESULT, UNINITIALIZED_RESULT } from "./state";
export { wrapImplicit } from "./wrap-implicit";
5 changes: 2 additions & 3 deletions packages/parjs/src/internal/parjser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ParjserDebugFunction } from "./parser";
import type { ImplicitParjser, ParjserDebugFunction } from "./parser";
import type { FailureInfo, ParjsResult } from "./result";
import type { ParsingState, UserState } from "./state";
import type { ImplicitParjser } from "./wrap-implicit";

/** A combinator or operator that takes a source parser that returns a new parser based on it. */
export interface ParjsCombinator<TFrom, TTo> {
Expand Down Expand Up @@ -30,7 +29,7 @@ export interface Parjser<out T> {

readonly expecting: string;
/**
* Exposes the display name of the parser. Userful when debugging.
* Exposes the display name of the parser. Useful when debugging.
*
* @group informational
*/
Expand Down
49 changes: 46 additions & 3 deletions packages/parjs/src/internal/parser.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { ParserDefinitionError } from "../errors";
import { clone, defaults } from "../utils";
import { ParjsFailure } from "./ParjsFailure";
import type { CombinatorInput } from "./combinated";
import type { ParjsCombinator, Parjser } from "./parjser";
import type { ErrorLocation, ParjsResult, Trace } from "./result";
import { ParjsFailure, ParjsSuccess, ResultKind } from "./result";
import { ParjsSuccess, ResultKind } from "./result";
import type { ParsingState, UserState } from "./state";
import { BasicParsingState, FAIL_RESULT, UNINITIALIZED_RESULT } from "./state";
import { wrapImplicit } from "./wrap-implicit";

function getErrorLocation(ps: ParsingState) {
const endln = /\r\n|\n|\r/g;
Expand Down Expand Up @@ -141,7 +142,7 @@ export abstract class ParjserBase<TValue> implements Parjser<TValue> {
}

/**
* The internal operation performed by the PARSER. This will be overriden by derived classes.
* The internal operation performed by the PARSER. This will be overridden by derived classes.
*
* @param ps
*/
Expand Down Expand Up @@ -276,3 +277,45 @@ class ParseString<T> extends ParjserBase<T> {
ps.kind = ResultKind.Ok;
}
}

/** A {@link Parjser} or a literal value convertible to a {@link Parjser}. */
/**
* @private Should Not be used from user code. Used to implement implicit parser literals.
* @type {symbol}
*/
export const convertibleSymbol: unique symbol = Symbol("ParjsConvertibleLiteral");

/**
* A literal type which is implicitly convertible to a parser. This normally includes the `string`
* and `RegExp` types.
*/
export interface ConvertibleScalar<T> {
[convertibleSymbol](): Parjser<T>;
}

declare global {
interface String {
[convertibleSymbol](): Parjser<string>;
}

interface RegExp {
[convertibleSymbol](): Parjser<string[]>;
}
}

/**
* Either a Parjser or a scalar value convertible to one.
*
* @module parjs
*/
export type ImplicitParjser<T> = Parjser<T> | ConvertibleScalar<T>;

export function wrapImplicit<V>(scalarOrParjser: ImplicitParjser<V>): CombinatorInput<V> {
if (typeof scalarOrParjser === "string") {
return string(scalarOrParjser) as unknown as CombinatorInput<V>;
} else if (scalarOrParjser instanceof RegExp) {
return regexp(scalarOrParjser) as CombinatorInput<V>;
} else {
return scalarOrParjser as CombinatorInput<V>;
}
}
28 changes: 1 addition & 27 deletions packages/parjs/src/internal/result.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ParjsParsingFailure } from "../errors";
import { ParjsFailure } from "./ParjsFailure";
import type { Parjser } from "./parjser";
import { visualizeTrace } from "./trace-visualizer";

/** Indicates a success reply and contains the value and other information. */
export class ParjsSuccess<T> implements SuccessInfo<T> {
Expand Down Expand Up @@ -46,31 +45,6 @@ export interface Trace extends FailureInfo {
input: string;
}

/** A failure result from a Parjs parser. */
export class ParjsFailure implements FailureInfo {
constructor(public trace: Trace) {}

get value(): never {
throw new ParjsParsingFailure(this);
}

get kind() {
return this.trace.kind;
}

get reason() {
return this.trace.reason;
}

toString() {
return visualizeTrace(this.trace);
}
/** Whether this result is an OK. */
get isOk() {
return false;
}
}

export function isParjsSuccess<T>(x: unknown): x is ParjsSuccess<T> {
return x instanceof ParjsSuccess;
}
Expand Down
Loading