I would like to write in (current) Typescript a general parser parameterized over symbols particular to language grammars.
Below is the simplest of several tried approaches of parameterizing Sym
, the type for the symbols that varies per language grammar, but the first line in Parser.js fails type checking:
Type
Sym
does not satisfy the constraintstring | number | symbol
.
Every way I tried to adjust the definition of GrammarRules<Sym>
that resolved the issue unfortunately resulted in further downstream problems, only some of portion of which were themselves resolvable – couldn't find a full solution.
How can I define the symbol type, Sym
, differently for each parser language and handle generically in Parser.js? I'm open to substantially re-working some or all of the declarations and code to get the cleanest overall solution.
Parser.ts
export type GrammarRules<Sym> = Record<Sym, GrammarRule<Sym>>
export type GrammarRule<Sym> = { lhs: Sym /* further uses of Sym type */ }
export class Parser<Sym> {
rules: GrammarRules<Sym>
private str: string
/* Various other datastructure declarations with types dependent on Sym */
constructor(rules: GrammarRules<Sym>) {
this.rules = rules
}
parse(startSym: Sym, str: string) {
this.str = str
console.log(this.rules[startSym].lhs)
// ...
}
}
ParserLangABC.ts
import { Parser, GrammarRules } from "./Parser"
type Sym = 'A' | 'B' | 'C'
const rulesABC: GrammarRules<Sym> = {
A: { lhs: 'A' /* rhs with further data of type Sym */ },
B: { lhs: 'B' /* rhs with further data of type Sym */ },
C: { lhs: 'C' /* rhs with further data of type Sym */ }
}
class ParserLangABC<Sym> extends Parser<Sym> {
static parse(str: string) {
const parser = new Parser(rulesABC)
parser.parse('A', str)
}
// Other supporting methods parameterized to Sym
}
ParserLangDEF.ts
import { Parser, GrammarRules } from "./Parser"
type Sym = 'D' | 'E' | 'F'
const rulesDEF: GrammarRules<Sym> = {
D: { lhs: 'D' /* rhs with further data of type Sym */ },
E: { lhs: 'E' /* rhs with further data of type Sym */ },
F: { lhs: 'F' /* rhs with further data of type Sym */ }
}
class ParserLangDEF<Sym> extends Parser<Sym> {
static parse(str: string) {
const parser = new Parser(rulesDEF)
parser.parse('D', str)
}
// Other supporting methods parameterized to Sym
}
CodePudding user response:
Not sure what you are trying to do with parsers but I think this is what you want regarding templates. The key is to define export type SymBase = string | number | symbol;
and replace Sym
with Sym extends SymBase
where needed.
diff b/Parser.ts a/Parser.ts
1c1
< export type GrammarRules<Sym> = Record<Sym, GrammarRule<Sym>>
---
> export type SymBase = string | number | symbol;
> export type GrammarRules<Sym extends SymBase> = Record<Sym, GrammarRule<Sym>>
4c4
< export class Parser<Sym> {
---
> export class Parser<Sym extends SymBase> {
diff b/ParserLangABC.ts a/ParserLangABC.ts
1c1
< import { Parser, GrammarRules } from "./Parser"
---
> import { SymBase, Parser, GrammarRules } from "./Parser"
9c9
< class ParserLangABC<Sym> extends Parser<Sym> {
---
> class ParserLangABC<Sym extends SymBase> extends Parser<Sym> {
diff b/ParserLangDEF.ts a/ParserLangDEF.ts
1c1
< import { Parser, GrammarRules } from "./Parser"
---
> import { SymBase, Parser, GrammarRules } from "./Parser"
9c9
< class ParserLangABC<Sym> extends Parser<Sym> {
---
> class ParserLangABC<Sym extends SymBase> extends Parser<Sym> {