Skip to content

Commit

Permalink
fix lints and kill any (#142)
Browse files Browse the repository at this point in the history
* lint: fix lint warning that is easy to fix

* lint: typesafe signature of seqOrText

* lint: typesafe createLanguage and language

* lint: typesafe seq

* lint: typesafe Parser.option

* fix: node can be string

* lint: typesafe alt

* fix: invalid url in link element will cause error

* chore: get rid of any

* fix: unnecessary import

* lint: kill any but still with loose type checking

* Revert "lint: kill any but still with loose type checking"

This reverts commit 8c7462f.

* lint: kill any again

* test: write type test

* ci: upgrade node version for lint
  • Loading branch information
anatawa12 authored Jun 13, 2024
1 parent 6aaf680 commit 2c7e152
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 175 deletions.
11 changes: 9 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,21 @@ module.exports = {
'prefer-arrow-callback': ['error'],
'no-throw-literal': ['error'],
'no-param-reassign': ['warn'],
'no-constant-condition': ['warn'],
'no-constant-condition': ['warn', {
checkLoops: false,
}],
'no-empty-pattern': ['warn'],
'@typescript-eslint/no-unnecessary-condition': ['warn'],
'@typescript-eslint/no-unnecessary-condition': ['warn', {
allowConstantLoopConditions: true,
}],
'@typescript-eslint/no-inferrable-types': ['warn'],
'@typescript-eslint/no-non-null-assertion': ['warn'],
'@typescript-eslint/explicit-function-return-type': ['warn'],
'@typescript-eslint/no-misused-promises': ['error', {
'checksVoidReturn': false,
}],
'@typescript-eslint/no-unused-vars': ['error', {
"argsIgnorePattern": "^_",
}]
},
};
2 changes: 1 addition & 1 deletion .github/workflows/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 16.5.0
node-version: 16.10.0

- name: Cache dependencies
uses: actions/cache@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 16.5.0
node-version: 16.10.0

- name: Cache dependencies
uses: actions/cache@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [16.5.0]
node-version: [16.10.0]

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion src/cli/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { performance } from 'perf_hooks';
import inputLine, { InputCanceledError } from './misc/inputLine';
import { parse } from '..';

async function entryPoint() {
async function entryPoint(): Promise<void> {
console.log('intaractive parser');

while (true) {
Expand Down
2 changes: 1 addition & 1 deletion src/cli/parseSimple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { performance } from 'perf_hooks';
import inputLine, { InputCanceledError } from './misc/inputLine';
import { parseSimple } from '..';

async function entryPoint() {
async function entryPoint(): Promise<void> {
console.log('intaractive simple parser');

while (true) {
Expand Down
66 changes: 45 additions & 21 deletions src/internal/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ export type Failure = { success: false };

export type Result<T> = Success<T> | Failure;

export type ParserHandler<T> = (input: string, index: number, state: any) => Result<T>
interface State {
trace?: boolean,
linkLabel?: boolean,
nestLimit: number,
depth: number,
}

export type ParserHandler<T> = (input: string, index: number, state: State) => Result<T>

export function success<T>(index: number, value: T): Success<T> {
return {
Expand All @@ -31,7 +38,7 @@ export class Parser<T> {
public handler: ParserHandler<T>;

constructor(handler: ParserHandler<T>, name?: string) {
this.handler = (input, index, state) => {
this.handler = (input, index, state) : Failure | Success<T> => {
if (state.trace && this.name != null) {
const pos = `${index}`;
console.log(`${pos.padEnd(6, ' ')}enter ${this.name}`);
Expand Down Expand Up @@ -91,20 +98,24 @@ export class Parser<T> {
});
}

sep(separator: Parser<any>, min: number): Parser<T[]> {
sep(separator: Parser<unknown>, min: number): Parser<T[]> {
if (min < 1) {
throw new Error('"min" must be a value greater than or equal to 1.');
}
return seq([
return seq(
this,
seq([
seq(
separator,
this,
], 1).many(min - 1),
]).map(result => [result[0], ...result[1]]);
).select(1).many(min - 1),
).map(result => [result[0], ...result[1]]);
}

option<T>(): Parser<T | null> {
select<K extends keyof T>(key: K): Parser<T[K]> {
return this.map(v => v[key]);
}

option(): Parser<T | null> {
return alt([
this,
succeeded(null),
Expand Down Expand Up @@ -136,7 +147,17 @@ export function regexp<T extends RegExp>(pattern: T): Parser<string> {
});
}

export function seq(parsers: Parser<any>[], select?: number): Parser<any> {
type ParsedType<T extends Parser<unknown>> = T extends Parser<infer U> ? U : never;

export type SeqParseResult<T extends unknown[]> =
T extends [] ? []
: T extends [infer F, ...infer R]
? (
F extends Parser<unknown> ? [ParsedType<F>, ...SeqParseResult<R>] : [unknown, ...SeqParseResult<R>]
)
: unknown[];

export function seq<Parsers extends Parser<unknown>[]>(...parsers: Parsers): Parser<SeqParseResult<Parsers>> {
return new Parser((input, index, state) => {
let result;
let latestIndex = index;
Expand All @@ -149,17 +170,17 @@ export function seq(parsers: Parser<any>[], select?: number): Parser<any> {
latestIndex = result.index;
accum.push(result.value);
}
return success(latestIndex, (select != null ? accum[select] : accum));
return success(latestIndex, accum as SeqParseResult<Parsers>);
});
}

export function alt(parsers: Parser<any>[]): Parser<any> {
return new Parser((input, index, state) => {
let result;
export function alt<Parsers extends Parser<unknown>[]>(parsers: Parsers): Parser<ParsedType<Parsers[number]>> {
return new Parser<ParsedType<Parsers[number]>>((input, index, state): Result<ParsedType<Parsers[number]>> => {
for (let i = 0; i < parsers.length; i++) {
result = parsers[i].handler(input, index, state);
const parser: Parsers[number] = parsers[i];
const result = parser.handler(input, index, state);
if (result.success) {
return result;
return result as Result<ParsedType<Parsers[number]>>;
}
}
return failure();
Expand All @@ -172,7 +193,7 @@ function succeeded<T>(value: T): Parser<T> {
});
}

export function notMatch(parser: Parser<any>): Parser<null> {
export function notMatch(parser: Parser<unknown>): Parser<null> {
return new Parser((input, index, state) => {
const result = parser.handler(input, index, state);
return !result.success
Expand Down Expand Up @@ -232,18 +253,21 @@ export function lazy<T>(fn: () => Parser<T>): Parser<T> {
//type SyntaxReturn<T> = T extends (rules: Record<string, Parser<any>>) => infer R ? R : never;
//export function createLanguage2<T extends Record<string, Syntax<any>>>(syntaxes: T): { [K in keyof T]: SyntaxReturn<T[K]> } {

type ParserTable<T> = { [K in keyof T]: Parser<T[K]> };

// TODO: 関数の型宣言をいい感じにしたい
export function createLanguage<T>(syntaxes: { [K in keyof T]: (r: Record<string, Parser<any>>) => T[K] }): T {
const rules: Record<string, Parser<any>> = {};
for (const key of Object.keys(syntaxes)) {
export function createLanguage<T>(syntaxes: { [K in keyof T]: (r: ParserTable<T>) => Parser<T[K]> }): ParserTable<T> {
// @ts-expect-error initializing object so type error here
const rules: ParserTable<T> = {};
for (const key of Object.keys(syntaxes) as (keyof T & string)[]) {
rules[key] = lazy(() => {
const parser = (syntaxes as any)[key](rules);
const parser = syntaxes[key](rules);
if (parser == null) {

Check warning on line 265 in src/internal/core/index.ts

View workflow job for this annotation

GitHub Actions / lint

Unnecessary conditional, the types have no overlap
throw new Error('syntax must return a parser.');
}
parser.name = key;
return parser;
});
}
return rules as any;
return rules;
}
10 changes: 7 additions & 3 deletions src/internal/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as M from '..';
import { language } from './parser';
import { mergeText } from './util';
import * as P from './core';

export type FullParserOpts = {
nestLimit?: number;
Expand All @@ -13,11 +12,16 @@ export function fullParser(input: string, opts: FullParserOpts): M.MfmNode[] {
depth: 0,
linkLabel: false,
trace: false,
}) as P.Success<any>;
});
if (!result.success) throw new Error('Unexpected parse error');
return mergeText(result.value);
}

export function simpleParser(input: string): M.MfmSimpleNode[] {
const result = language.simpleParser.handler(input, 0, { }) as P.Success<any>;
const result = language.simpleParser.handler(input, 0, {
depth: 0,
nestLimit: 1 / 0, // reliable infinite
});
if (!result.success) throw new Error('Unexpected parse error');
return mergeText(result.value);
}
Loading

0 comments on commit 2c7e152

Please sign in to comment.