lidl
Version:
The LIDL Interaction Desccription Language
339 lines (288 loc) • 10.8 kB
JavaScript
var _ = require('lodash');
var operator = require('./operator.js');
var serializer = require('./serializer.js');
var stringify = require('json-stringify-safe');
// get the list of all interaction operators in an interaction expression
function listOfInteractions(theInteraction) {
switch (theInteraction.type) {
case "InteractionSimple":
var res = [theInteraction.operator];
var i;
for (i = 0; i < theInteraction.operand.length; i++) {
res = _.union(res, listOfInteractions(theInteraction.operand[i]));
}
return res;
case "InteractionNative":
return [theInteraction.lang + '`' + '<code>' + '`'];
default:
throw "Trying to get the list of interactions of something which is not an interaction expression";
}
}
// Fully expand an interaction definition into a composition of base interactions
function expand(interactionDefinition) {
var definitions = _.map(interactionDefinition.definitions, expand);
var interactionDefinitionWithChildrenExpanded = {
type: "Definition",
interaction: interactionDefinition.interaction,
signature: interactionDefinition.signature,
definitions: definitions,
parent: interactionDefinition.parent
};
var interaction = interactionDefinition.interaction;
var interactionsToExpandAlongWithTheirMatchingDefinition;
do {
interactionsToExpandAlongWithTheirMatchingDefinition =
_.filter(
_.map(listNonBaseInteractions(interaction),
function(x) {
return {
interaction: x,
definition: findMatchingDefinition(x, interactionDefinitionWithChildrenExpanded)
};
}),
function(y) {
return !isDefinitionOfAnArgument(y.definition) && y.interaction.type==="InteractionSimple";
});
// For each of these interactions to expand, we instatiate them
_.forEach(interactionsToExpandAlongWithTheirMatchingDefinition, function(x) {
interaction = instantiate(interaction, x.definition);
});
} while (interactionsToExpandAlongWithTheirMatchingDefinition.length > 0);
return {
type: "Definition",
interaction: interaction,
signature: interactionDefinition.signature,
definitions: definitions,
parent: interactionDefinition.parent
};
}
// instantiate an interaction (expand this interaction) using a single definition
function instantiate(interaction, interactionDefinition) {
switch (interaction.type) {
case 'InteractionNative':
{
// this is a native interaction, no need to instantiate
return interaction;
}
case 'InteractionSimple':
{
// maybe we can instatiate
// if it is an argument, then there is no need to instantiate
if (isDefinitionOfAnArgument(interactionDefinition)) {
return interaction;
} else {
// First we instantiate the operands
var instantiatedOperands = _.map(interaction.operand, function(x) {
return instantiate(x, interactionDefinition);
});
// Do we substitute this interaction or not ?
if (interactionMatchesDefinition(interaction, interactionDefinition)) {
return _.reduce(
_.zip(interactionDefinition.signature.operand, instantiatedOperands),
function(accumulator, value, key, collection) {
return substituteInInteraction(accumulator, {
type: "InteractionSimple",
operator: value[0].name,
operand: []
}, value[1]);
}, _.cloneDeep(interactionDefinition.interaction));
} else {
return {
type: "InteractionSimple",
operator: interaction.operator,
operand: instantiatedOperands
};
}
}
}
default:
throw new Error("trying to instantiate an invalid interaction");
}
}
// Finds the definition that matches an interaction, in the context of an interaction definition
function findMatchingDefinition(interaction, interactionDefinition) {
// First case : No definition
if (interactionDefinition === undefined) {
throw new Error("could not find definition matching interaction " + serializer.serialize(interaction));
}
if (interaction.type === 'InteractionNative') {
return {
type: "Definition",
interaction: interaction,
signature: "function",
definitions: [],
parent: null
};
}
// Second case : The interaction definition specifies that it is an argument (not really possible ?)
if (isDefinitionOfAnArgument(interactionDefinition)) {
return "Argument";
}
// Third case : The interaction is an argument of the definition
if (_.any(interactionDefinition.signature.operand, "name", interaction.operator)) {
// return {type:'Definition',interaction:"Argument",signature:{operand:[{"name":interaction.operator}]}}; /* TODO precise this return value*/
return "Argument"; /* TODO problem is here !*/
}
// Fourth case : The interaction is defined within the sub definitions of the definition
for (var i = 0; i < interactionDefinition.definitions.length; i++) {
if (interactionMatchesDefinition(interaction, interactionDefinition.definitions[i])) {
return interactionDefinition.definitions[i];
}
}
// Fifth case: The interaction is defined in the context of the parent definition (the definition is a sub definition of its parent)
if(interactionDefinition.parent !== undefined)
return findMatchingDefinition(interaction, interactionDefinition.parent);
throw new Error("cannot find definition of interaction "+interaction.operator);
}
// Checks if an interaction definition states that an interaction is an argument
function isDefinitionOfAnArgument(definition) {
// {type:'Definition',interaction:interaction,signature:{type:'Signature',interface:interface,operator:temp.operator,operand:temp.operand},definitions:(definitions===null?[]:definitions)};
return definition === "Argument";
}
// Check if an interaction matches an InteractionDefinition, simple for the moment, will get more complicated later
function interactionMatchesDefinition(interaction, interactiondefinition) {
if (isDefinitionOfAnArgument(interactiondefinition)) return true;
return interaction.operator === interactiondefinition.signature.operator;
}
// Compare two interactions, returns 0 if they are equal
function compare(a, b) {
if (a.type !== 'InteractionSimple' || b.type !== 'InteractionSimple') {
if(a.type==="InteractionNative" && b.type === "InteractionNative") {
return ((_.isEqual(a.code,b.code))?(0):(1));
}
return -1;
}
if (a.operator > b.operator) {
return 1;
} else {
if (a.operator < b.operator) {
return -1;
} else {
if ((a.operand.length - b.operand.length) !== 0) {
return (a.operand.length - b.operand.length);
} else {
var x = _.zip(a.operand, b.operand);
var f = _.spread(compare);
for (var i = 0; i < x.length; i++) {
var res = f(x[i]);
if (res !== 0) return res;
}
return 0;
}
}
}
}
// Substitute a target interaction with another one in an interaction expression
function substituteInInteraction(theInteraction, target, substitute) {
switch (theInteraction.type) {
case 'InteractionNative':
{
return theInteraction;
}
case 'InteractionSimple':
{
if (compare(theInteraction, target) === 0) {
return _.cloneDeep(substitute);
} else {
return {
type: theInteraction.type,
operator: theInteraction.operator,
operand: _.map(theInteraction.operand, function(x) {
return substituteInInteraction(x, target, substitute);
})
};
}
}
break;
default:
throw new Error('Trying to substitute in an invalid interaction');
}
}
// Checks if a given interaction is made of only base interactions
function isOnlyMadeOfBaseInteractions(interaction) {
if (!isBaseInteraction(interaction)) {
return false;
} else {
return _.every(interaction.operand, isOnlyMadeOfBaseInteractions);
}
}
// Gives a list of non base interactions in an interaction expression
function listNonBaseInteractions(interaction) {
if (interaction.type === 'InteractionSimple') {
return (isBaseInteraction(interaction) ? [] : [interaction]).concat(_.flatten(_.map(interaction.operand, listNonBaseInteractions)));
} else {
return [];
}
}
// Checks if a given interaction is a base interaction
function isBaseInteraction(interaction) {
// console.log(interaction.type + " " + interaction.operator);
switch (interaction.type) {
case 'InteractionNative':
{
return true;
}
case 'InteractionSimple':
{
var theOperator = operator.parse(interaction.operator);
switch (theOperator) {
case "Composition":
case "Previous":
case "FunctionApplication":
case "Identifier":
case "Function":
case "Void":
return true;
case "Custom":
return false;
default:
throw new Error('problem parsing interaction operator ' + theOperator);
}
}
break;
default:
throw new Error('invalid interaction type ' + interaction.type);
}
}
function toListOfElements(interaction) {
var i=0;
return interaction.operator.replace(/\$/g,"_$_").split("_").filter(function(x){
return x !=='';
}).map(function(x){
var res;
if(x==="$"){
res= toListOfElements(interaction.operand[i]);
i++;
}else{
res= x;
}
return res;
});
}
function toShallowListOfElements(interaction) {
var i=0;
return interaction.operator.replace(/\$/g,"_$_").split("_").filter(function(x){
return x !=='';
}).map(function(x){
var res;
if(x==="$"){
res= interaction.operand[i];
i++;
}else{
res= x;
}
return res;
});
}
module.exports.toShallowListOfElements = toShallowListOfElements;
module.exports.toListOfElements = toListOfElements;
module.exports.listOfInteractions = listOfInteractions;
module.exports.isBaseInteraction = isBaseInteraction;
module.exports.isOnlyMadeOfBaseInteractions = isOnlyMadeOfBaseInteractions;
module.exports.compare = compare;
module.exports.substituteInInteraction = substituteInInteraction;
module.exports.instantiate = instantiate;
module.exports.interactionMatchesDefinition = interactionMatchesDefinition;
module.exports.expand = expand;
module.exports.findMatchingDefinition = findMatchingDefinition;
module.exports.listNonBaseInteractions = listNonBaseInteractions;