UNPKG

typir

Version:

General purpose type checking library

175 lines 7.9 kB
/****************************************************************************** * Copyright 2024 TypeFox GmbH * This program and the accompanying materials are made available under the * terms of the MIT License, which is available in the project root. ******************************************************************************/ import { NO_PARAMETER_NAME } from '../kinds/function/function-kind.js'; import { toArray } from '../utils/utils.js'; /** * This implementation realizes operators as functions and creates types of kind 'function'. * If Typir does not use the function kind so far, it will be automatically added. * (Alternative implementation strategies for operators would be a dedicated kind for operators, which might extend the 'function' kind) * * Nevertheless, there are some differences between operators and functions: * - Operators have no declaration. * - It is not possible to have references to operators. * * The same operator (i.e. same operator name, e.g. "+" or "XOR") with different types for its operands will be realized as different function types, * e.g. there are two functions for "+" for numbers and for strings. * * All operands are mandatory. */ export class DefaultOperatorFactory { constructor(services) { this.services = services; } createUnary(typeDetails) { return new OperatorConfigurationUnaryChainImpl(this.services, typeDetails); } createBinary(typeDetails) { return new OperatorConfigurationBinaryChainImpl(this.services, typeDetails); } createTernary(typeDetails) { return new OperatorConfigurationTernaryChainImpl(this.services, typeDetails); } createGeneric(typeDetails) { return new OperatorConfigurationGenericChainImpl(this.services, typeDetails); } } class OperatorConfigurationUnaryChainImpl { constructor(services, typeDetails) { this.services = services; this.typeDetails = Object.assign(Object.assign({}, typeDetails), { inferenceRules: [] }); } inferenceRule(rule) { this.typeDetails.inferenceRules.push(rule); return this; } finish() { const signatures = toSignatureArray(this.typeDetails); const result = []; for (const signature of signatures) { const generic = new OperatorConfigurationGenericChainImpl(this.services, { name: this.typeDetails.name, outputType: signature.return, inputParameter: [ { name: 'operand', type: signature.operand }, ], }); // the same inference rule is used (and required) for all overloads, since multiple FunctionTypes are created! this.typeDetails.inferenceRules.forEach(rule => generic.inferenceRule(rule)); result.push(generic.finish()); } return result; } } class OperatorConfigurationBinaryChainImpl { constructor(services, typeDetails) { this.services = services; this.typeDetails = Object.assign(Object.assign({}, typeDetails), { inferenceRules: [] }); } inferenceRule(rule) { this.typeDetails.inferenceRules.push(rule); return this; } finish() { const signatures = toSignatureArray(this.typeDetails); const result = []; for (const signature of signatures) { const generic = new OperatorConfigurationGenericChainImpl(this.services, { name: this.typeDetails.name, outputType: signature.return, inputParameter: [ { name: 'left', type: signature.left }, { name: 'right', type: signature.right }, ], }); // the same inference rule is used (and required) for all overloads, since multiple FunctionTypes are created! this.typeDetails.inferenceRules.forEach(rule => generic.inferenceRule(rule)); result.push(generic.finish()); } return result; } } class OperatorConfigurationTernaryChainImpl { constructor(services, typeDetails) { this.services = services; this.typeDetails = Object.assign(Object.assign({}, typeDetails), { inferenceRules: [] }); } inferenceRule(rule) { this.typeDetails.inferenceRules.push(rule); return this; } finish() { const signatures = toSignatureArray(this.typeDetails); const result = []; for (const signature of signatures) { const generic = new OperatorConfigurationGenericChainImpl(this.services, { name: this.typeDetails.name, outputType: signature.return, inputParameter: [ { name: 'first', type: signature.first }, { name: 'second', type: signature.second }, { name: 'third', type: signature.third }, ], }); // the same inference rule is used (and required) for all overloads, since multiple FunctionTypes are created! this.typeDetails.inferenceRules.forEach(rule => generic.inferenceRule(rule)); result.push(generic.finish()); } return result; } } class OperatorConfigurationGenericChainImpl { constructor(services, typeDetails) { this.services = services; this.typeDetails = Object.assign(Object.assign({}, typeDetails), { inferenceRules: [] }); } inferenceRule(rule) { this.typeDetails.inferenceRules.push(rule); return this; } finish() { // define/register the wanted operator as "special" function const functionFactory = this.getFunctionFactory(); const operatorName = this.typeDetails.name; // create the operator as type of kind 'function' const newOperatorType = functionFactory.create({ functionName: operatorName, outputParameter: { name: NO_PARAMETER_NAME, type: this.typeDetails.outputType }, inputParameters: this.typeDetails.inputParameter, }); // infer the operator when the operator is called! for (const inferenceRule of this.typeDetails.inferenceRules) { newOperatorType.inferenceRuleForCalls({ languageKey: inferenceRule.languageKey, filter: inferenceRule.filter ? ((languageNode) => inferenceRule.filter(languageNode, this.typeDetails.name)) : undefined, matching: (languageNode) => inferenceRule.matching(languageNode, this.typeDetails.name), inputArguments: (languageNode) => this.getInputArguments(inferenceRule, languageNode), validation: toArray(inferenceRule.validation).map(validationRule => (functionCall, functionType, accept, typir) => validationRule(functionCall, operatorName, functionType, accept, typir)), validateArgumentsOfFunctionCalls: inferenceRule.validateArgumentsOfCalls, }); } // operators have no declaration in the code => no inference rule for the operator declaration! return newOperatorType.finish(); } getInputArguments(inferenceRule, languageNode) { return 'operands' in inferenceRule ? inferenceRule.operands(languageNode, this.typeDetails.name) : [inferenceRule.operand(languageNode, this.typeDetails.name)]; } getFunctionFactory() { return this.services.factory.Functions; } } function toSignatureArray(values) { const result = toArray(values.signatures, { newArray: true }); // create a new array in order to prevent side-effects in the given array if (values.signature) { result.push(values.signature); } if (result.length <= 0) { throw new Error('At least one signature must be given!'); } return result; } //# sourceMappingURL=operator.js.map