Aktueller Stand

This commit is contained in:
2026-01-23 01:33:35 +01:00
parent 082dc5e110
commit 2766dd12c5
10109 changed files with 1578841 additions and 77685 deletions

View File

@@ -0,0 +1,22 @@
import { Rule, GenerateDtsOptions } from "@chevrotain/types"
import { buildModel } from "./model"
import { genDts } from "./generate"
const defaultOptions: Required<GenerateDtsOptions> = {
includeVisitorInterface: true,
visitorInterfaceName: "ICstNodeVisitor"
}
export function generateCstDts(
productions: Record<string, Rule>,
options?: GenerateDtsOptions
): string {
const effectiveOptions = {
...defaultOptions,
...options
}
const model = buildModel(productions)
return genDts(model, effectiveOptions)
}

View File

@@ -0,0 +1,103 @@
import flatten from "lodash/flatten"
import isArray from "lodash/isArray"
import map from "lodash/map"
import reduce from "lodash/reduce"
import uniq from "lodash/uniq"
import upperFirst from "lodash/upperFirst"
import { GenerateDtsOptions } from "@chevrotain/types"
import {
CstNodeTypeDefinition,
PropertyTypeDefinition,
PropertyArrayType,
TokenArrayType,
RuleArrayType
} from "./model"
export function genDts(
model: CstNodeTypeDefinition[],
options: Required<GenerateDtsOptions>
): string {
let contentParts: string[] = []
contentParts = contentParts.concat(
`import type { CstNode, ICstVisitor, IToken } from "chevrotain";`
)
contentParts = contentParts.concat(
flatten(map(model, (node) => genCstNodeTypes(node)))
)
if (options.includeVisitorInterface) {
contentParts = contentParts.concat(
genVisitor(options.visitorInterfaceName, model)
)
}
return contentParts.join("\n\n") + "\n"
}
function genCstNodeTypes(node: CstNodeTypeDefinition) {
const nodeCstInterface = genNodeInterface(node)
const nodeChildrenInterface = genNodeChildrenType(node)
return [nodeCstInterface, nodeChildrenInterface]
}
function genNodeInterface(node: CstNodeTypeDefinition) {
const nodeInterfaceName = getNodeInterfaceName(node.name)
const childrenTypeName = getNodeChildrenTypeName(node.name)
return `export interface ${nodeInterfaceName} extends CstNode {
name: "${node.name}";
children: ${childrenTypeName};
}`
}
function genNodeChildrenType(node: CstNodeTypeDefinition) {
const typeName = getNodeChildrenTypeName(node.name)
return `export type ${typeName} = {
${map(node.properties, (property) => genChildProperty(property)).join("\n ")}
};`
}
function genChildProperty(prop: PropertyTypeDefinition) {
const typeName = buildTypeString(prop.type)
return `${prop.name}${prop.optional ? "?" : ""}: ${typeName}[];`
}
function genVisitor(name: string, nodes: CstNodeTypeDefinition[]) {
return `export interface ${name}<IN, OUT> extends ICstVisitor<IN, OUT> {
${map(nodes, (node) => genVisitorFunction(node)).join("\n ")}
}`
}
function genVisitorFunction(node: CstNodeTypeDefinition) {
const childrenTypeName = getNodeChildrenTypeName(node.name)
return `${node.name}(children: ${childrenTypeName}, param?: IN): OUT;`
}
function buildTypeString(type: PropertyArrayType) {
if (isArray(type)) {
const typeNames = uniq(map(type, (t) => getTypeString(t)))
const typeString = reduce(typeNames, (sum, t) => sum + " | " + t)
return "(" + typeString + ")"
} else {
return getTypeString(type)
}
}
function getTypeString(type: TokenArrayType | RuleArrayType) {
if (type.kind === "token") {
return "IToken"
}
return getNodeInterfaceName(type.name)
}
function getNodeInterfaceName(ruleName: string) {
return upperFirst(ruleName) + "CstNode"
}
function getNodeChildrenTypeName(ruleName: string) {
return upperFirst(ruleName) + "CstChildren"
}

View File

@@ -0,0 +1,177 @@
import type {
Alternation,
Alternative,
IProduction,
Option,
Repetition,
RepetitionMandatory,
RepetitionMandatoryWithSeparator,
RepetitionWithSeparator,
Rule,
Terminal,
TokenType
} from "@chevrotain/types"
import { NonTerminal, GAstVisitor } from "@chevrotain/gast"
import map from "lodash/map"
import flatten from "lodash/flatten"
import values from "lodash/values"
import some from "lodash/some"
import groupBy from "lodash/groupBy"
import assign from "lodash/assign"
export function buildModel(
productions: Record<string, Rule>
): CstNodeTypeDefinition[] {
const generator = new CstNodeDefinitionGenerator()
const allRules = values(productions)
return map(allRules, (rule) => generator.visitRule(rule))
}
export type CstNodeTypeDefinition = {
name: string
properties: PropertyTypeDefinition[]
}
export type PropertyTypeDefinition = {
name: string
type: PropertyArrayType
optional: boolean
}
export type PropertyArrayType =
| TokenArrayType
| RuleArrayType
| (TokenArrayType | RuleArrayType)[]
export type TokenArrayType = { kind: "token" }
export type RuleArrayType = {
kind: "rule"
name: string
}
class CstNodeDefinitionGenerator extends GAstVisitor {
visitRule(node: Rule): CstNodeTypeDefinition {
const rawElements = this.visitEach(node.definition)
const grouped = groupBy(rawElements, (el) => el.propertyName)
const properties = map(grouped, (group, propertyName) => {
const allNullable = !some(group, (el) => !el.canBeNull)
// In an alternation with a label a property name can have
// multiple types.
let propertyType: PropertyArrayType = group[0].type
if (group.length > 1) {
propertyType = map(group, (g) => g.type)
}
return {
name: propertyName,
type: propertyType,
optional: allNullable
} as PropertyTypeDefinition
})
return {
name: node.name,
properties: properties
}
}
visitAlternative(node: Alternative) {
return this.visitEachAndOverrideWith(node.definition, { canBeNull: true })
}
visitOption(node: Option) {
return this.visitEachAndOverrideWith(node.definition, { canBeNull: true })
}
visitRepetition(node: Repetition) {
return this.visitEachAndOverrideWith(node.definition, { canBeNull: true })
}
visitRepetitionMandatory(node: RepetitionMandatory) {
return this.visitEach(node.definition)
}
visitRepetitionMandatoryWithSeparator(
node: RepetitionMandatoryWithSeparator
) {
return this.visitEach(node.definition).concat({
propertyName: node.separator.name,
canBeNull: true,
type: getType(node.separator)
})
}
visitRepetitionWithSeparator(node: RepetitionWithSeparator) {
return this.visitEachAndOverrideWith(node.definition, {
canBeNull: true
}).concat({
propertyName: node.separator.name,
canBeNull: true,
type: getType(node.separator)
})
}
visitAlternation(node: Alternation) {
return this.visitEachAndOverrideWith(node.definition, { canBeNull: true })
}
visitTerminal(node: Terminal): PropertyTupleElement[] {
return [
{
propertyName: node.label || node.terminalType.name,
canBeNull: false,
type: getType(node)
}
]
}
visitNonTerminal(node: NonTerminal): PropertyTupleElement[] {
return [
{
propertyName: node.label || node.nonTerminalName,
canBeNull: false,
type: getType(node)
}
]
}
private visitEachAndOverrideWith(
definition: IProduction[],
override: Partial<PropertyTupleElement>
) {
return map(
this.visitEach(definition),
(definition) => assign({}, definition, override) as PropertyTupleElement
)
}
private visitEach(definition: IProduction[]) {
return flatten<PropertyTupleElement>(
map(
definition,
(definition) => this.visit(definition) as PropertyTupleElement[]
)
)
}
}
type PropertyTupleElement = {
propertyName: string
canBeNull: boolean
type: TokenArrayType | RuleArrayType
}
function getType(
production: Terminal | NonTerminal | TokenType
): TokenArrayType | RuleArrayType {
if (production instanceof NonTerminal) {
return {
kind: "rule",
name: production.referencedRule.name
}
}
return { kind: "token" }
}