From 9a70b19d1a627e942d42b413646fe9594a72a044 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 7 Apr 2022 15:22:07 +0000 Subject: [PATCH] Finish ast reflection interpreter --- packages/langium-cli/src/parser-validation.ts | 6 +- .../src/grammar/ast-reflection-interpreter.ts | 75 ++++++++++++++++--- packages/langium/src/index.ts | 1 + .../parser/langium-parser-builder.test.ts | 4 +- 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/packages/langium-cli/src/parser-validation.ts b/packages/langium-cli/src/parser-validation.ts index 68ed88a6c..edc83aff1 100644 --- a/packages/langium-cli/src/parser-validation.ts +++ b/packages/langium-cli/src/parser-validation.ts @@ -5,7 +5,7 @@ ******************************************************************************/ import { - createDefaultModule, createDefaultSharedModule, getDocument, Grammar, inject, IParserConfig, + createDefaultModule, createDefaultSharedModule, getDocument, Grammar, inject, interpreteAstReflection, IParserConfig, isGrammar, isParserRule, LangiumDocuments, LangiumGeneratedServices, LangiumGeneratedSharedServices, LangiumParser, LangiumServices, LangiumSharedServices, Module, ParserRule, prepareLangiumParser } from 'langium'; @@ -18,10 +18,8 @@ export function validateParser(grammar: Grammar, config: LangiumConfig, grammarC ...grammarConfigMap.get(grammar)?.chevrotainParserConfig, skipValidations: false }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const unavailable: () => any = () => ({}); const generatedSharedModule: Module = { - AstReflection: unavailable, + AstReflection: interpreteAstReflection(grammar, documents), }; const generatedModule: Module = { Grammar: () => grammar, diff --git a/packages/langium/src/grammar/ast-reflection-interpreter.ts b/packages/langium/src/grammar/ast-reflection-interpreter.ts index f59df7425..1072a593b 100644 --- a/packages/langium/src/grammar/ast-reflection-interpreter.ts +++ b/packages/langium/src/grammar/ast-reflection-interpreter.ts @@ -4,17 +4,26 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import { AstNode, AstReflection, TypeMetaData } from '../syntax-tree'; +import { AstNode, AstReflection, TypeMandatoryField, TypeMetaData } from '../syntax-tree'; import { isAstNode } from '../utils/ast-util'; +import { MultiMap } from '../utils/collections'; import { LangiumDocuments } from '../workspace/documents'; import { Grammar } from './generated/ast'; +import { createLangiumGrammarServices } from './langium-grammar-module'; import { collectAst } from './type-system/type-collector'; -import { AstTypes } from './type-system/types-util'; +import { AstTypes, Property } from './type-system/types-util'; -export function interpreteAstReflection(langiumDocuments: LangiumDocuments, grammar: Grammar): AstReflection { - const collectedTypes = collectAst(langiumDocuments, [grammar]); +let emptyDocuments: LangiumDocuments; + +export function interpreteAstReflection(grammar: Grammar, documents?: LangiumDocuments): AstReflection { + if (!emptyDocuments && !documents) { + emptyDocuments = createLangiumGrammarServices().shared.workspace.LangiumDocuments; + } + const collectedTypes = collectAst(documents ?? emptyDocuments, [grammar]); const allTypes = collectedTypes.interfaces.map(e => e.name).concat(collectedTypes.unions.map(e => e.name)); - const references = getReferenceTypes(collectedTypes); + const references = buildReferenceTypes(collectedTypes); + const metaData = buildTypeMetaData(collectedTypes); + const superTypeMap = buildSupertypeMap(collectedTypes); return { getAllTypes() { @@ -28,7 +37,7 @@ export function interpreteAstReflection(langiumDocuments: LangiumDocuments, gram throw new Error('Could not find reference type for ' + referenceId); }, getTypeMetaData(type: string): TypeMetaData { - return { + return metaData.get(type) ?? { name: type, mandatory: [] }; @@ -36,16 +45,22 @@ export function interpreteAstReflection(langiumDocuments: LangiumDocuments, gram isInstance(node: AstNode, type: string): boolean { return isAstNode(node) && this.isSubtype(node.$type, type); }, - isSubtype(subtype: string, supertype: string): boolean { - if (subtype === supertype) { + isSubtype(subtype: string, originalSuperType: string): boolean { + if (subtype === originalSuperType) { return true; } - return true; + const superTypes = superTypeMap.get(subtype); + for (const superType of superTypes) { + if (this.isSubtype(superType, originalSuperType)) { + return true; + } + } + return false; } }; } -function getReferenceTypes(astTypes: AstTypes): Map { +function buildReferenceTypes(astTypes: AstTypes): Map { const references = new Map(); for (const interfaceType of astTypes.interfaces) { for (const property of interfaceType.properties) { @@ -57,4 +72,44 @@ function getReferenceTypes(astTypes: AstTypes): Map { } } return references; +} + +function buildTypeMetaData(astTypes: AstTypes): Map { + const map = new Map(); + for (const interfaceType of astTypes.interfaces) { + const props = interfaceType.properties; + const arrayProps = props.filter(e => e.typeAlternatives.some(e => e.array)); + const booleanProps = props.filter(e => e.typeAlternatives.every(e => !e.array && e.types.includes('boolean'))); + if (arrayProps.length > 0 || booleanProps.length > 0) { + map.set(interfaceType.name, { + name: interfaceType.name, + mandatory: buildMandatoryMetaData(arrayProps, booleanProps) + }); + } + } + return map; +} + +function buildMandatoryMetaData(arrayProps: Property[], booleanProps: Property[]): TypeMandatoryField[] { + const array: TypeMandatoryField[] = []; + const all = arrayProps.concat(booleanProps).sort((a, b) => a.name.localeCompare(b.name)); + for (const property of all) { + const type = arrayProps.includes(property) ? 'array' : 'boolean'; + array.push({ + name: property.name, + type + }); + } + return array; +} + +function buildSupertypeMap(astTypes: AstTypes): MultiMap { + const map = new MultiMap(); + for (const interfaceType of astTypes.interfaces) { + map.addAll(interfaceType.name, interfaceType.superTypes); + } + for (const unionType of astTypes.unions) { + map.addAll(unionType.name, unionType.superTypes); + } + return map; } \ No newline at end of file diff --git a/packages/langium/src/index.ts b/packages/langium/src/index.ts index 3f8b52cb6..812c33df2 100644 --- a/packages/langium/src/index.ts +++ b/packages/langium/src/index.ts @@ -9,6 +9,7 @@ export * from './dependency-injection'; export * from './generator/generator-node'; export * from './generator/node-processor'; export * from './generator/template-string'; +export * from './grammar/ast-reflection-interpreter'; export * from './grammar/generated/ast'; export * from './grammar/generated/module'; export * from './grammar/grammar-util'; diff --git a/packages/langium/test/parser/langium-parser-builder.test.ts b/packages/langium/test/parser/langium-parser-builder.test.ts index dcb3d87c3..49d87d35e 100644 --- a/packages/langium/test/parser/langium-parser-builder.test.ts +++ b/packages/langium/test/parser/langium-parser-builder.test.ts @@ -4,7 +4,7 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import { createDefaultModule, createDefaultSharedModule, createLangiumGrammarServices, createLangiumParser, Grammar, inject, IParserConfig, LangiumGeneratedServices, LangiumGeneratedSharedServices, LangiumParser, LangiumServices, LangiumSharedServices, Module } from '../../src'; +import { createDefaultModule, createDefaultSharedModule, createLangiumGrammarServices, createLangiumParser, Grammar, inject, interpreteAstReflection, IParserConfig, LangiumGeneratedServices, LangiumGeneratedSharedServices, LangiumParser, LangiumServices, LangiumSharedServices, Module } from '../../src'; import { parseHelper } from '../../src/test'; const grammarServices = createLangiumGrammarServices().grammar; @@ -266,7 +266,7 @@ function parserFromGrammar(grammar: Grammar): LangiumParser { // eslint-disable-next-line @typescript-eslint/no-explicit-any const unavailable: () => any = () => ({}); const generatedSharedModule: Module = { - AstReflection: unavailable, + AstReflection: () => interpreteAstReflection(grammar), }; const generatedModule: Module = { Grammar: () => grammar,