Skip to content

Commit

Permalink
Finish ast reflection interpreter
Browse files Browse the repository at this point in the history
  • Loading branch information
msujew committed Apr 7, 2022
1 parent 07181ac commit 9a70b19
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 16 deletions.
6 changes: 2 additions & 4 deletions packages/langium-cli/src/parser-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<LangiumSharedServices, LangiumGeneratedSharedServices> = {
AstReflection: unavailable,
AstReflection: interpreteAstReflection(grammar, documents),
};
const generatedModule: Module<LangiumServices, LangiumGeneratedServices> = {
Grammar: () => grammar,
Expand Down
75 changes: 65 additions & 10 deletions packages/langium/src/grammar/ast-reflection-interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -28,24 +37,30 @@ 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: []
};
},
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<string, string> {
function buildReferenceTypes(astTypes: AstTypes): Map<string, string> {
const references = new Map<string, string>();
for (const interfaceType of astTypes.interfaces) {
for (const property of interfaceType.properties) {
Expand All @@ -57,4 +72,44 @@ function getReferenceTypes(astTypes: AstTypes): Map<string, string> {
}
}
return references;
}

function buildTypeMetaData(astTypes: AstTypes): Map<string, TypeMetaData> {
const map = new Map<string, TypeMetaData>();
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<string, string> {
const map = new MultiMap<string, string>();
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;
}
1 change: 1 addition & 0 deletions packages/langium/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
4 changes: 2 additions & 2 deletions packages/langium/test/parser/langium-parser-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -266,7 +266,7 @@ function parserFromGrammar(grammar: Grammar): LangiumParser {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const unavailable: () => any = () => ({});
const generatedSharedModule: Module<LangiumSharedServices, LangiumGeneratedSharedServices> = {
AstReflection: unavailable,
AstReflection: () => interpreteAstReflection(grammar),
};
const generatedModule: Module<LangiumServices, LangiumGeneratedServices> = {
Grammar: () => grammar,
Expand Down

0 comments on commit 9a70b19

Please sign in to comment.