|
|
|
"use strict";
|
|
|
|
|
|
|
|
// Inference match result of the rule. Can be:
|
|
|
|
// -1: negative result, always fails
|
|
|
|
// 0: neutral result, may be fail, may be match
|
|
|
|
// 1: positive result, always match
|
|
|
|
function inferenceMatchResult( ast, session ) {
|
|
|
|
|
|
|
|
let inference;
|
|
|
|
function sometimesMatch( node ) {
|
|
|
|
|
|
|
|
node.match = 0;
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
}
|
|
|
|
function alwaysMatch( node ) {
|
|
|
|
|
|
|
|
inference( node.expression );
|
|
|
|
|
|
|
|
node.match = 1;
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function inferenceExpression( node ) {
|
|
|
|
|
|
|
|
node.match = inference( node.expression );
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
}
|
|
|
|
function inferenceElements( elements, forChoice ) {
|
|
|
|
|
|
|
|
const length = elements.length;
|
|
|
|
let always = 0;
|
|
|
|
let never = 0;
|
|
|
|
|
|
|
|
for ( let i = 0; i < length; ++i ) {
|
|
|
|
|
|
|
|
const result = inference( elements[ i ] );
|
|
|
|
|
|
|
|
if ( result > 0 ) {
|
|
|
|
|
|
|
|
++always;
|
|
|
|
|
|
|
|
}
|
|
|
|
if ( result < 0 ) {
|
|
|
|
|
|
|
|
++never;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( always === length ) {
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
if ( forChoice ) {
|
|
|
|
|
|
|
|
return never === length ? -1 : 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return never > 0 ? -1 : 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
inference = session.buildVisitor( {
|
|
|
|
rule( node ) {
|
|
|
|
|
|
|
|
let oldResult;
|
|
|
|
let count = 0;
|
|
|
|
|
|
|
|
if ( typeof node.match === "undefined" ) {
|
|
|
|
|
|
|
|
node.match = 0;
|
|
|
|
do {
|
|
|
|
|
|
|
|
oldResult = node.match;
|
|
|
|
node.match = inference( node.expression );
|
|
|
|
// 6 == 3! -- permutations count for all transitions from one match
|
|
|
|
// state to another.
|
|
|
|
// After 6 iterations the cycle with guarantee begins
|
|
|
|
// istanbul ignore next
|
|
|
|
if ( ++count > 6 ) {
|
|
|
|
|
|
|
|
session.error(
|
|
|
|
"Infinity cycle detected when trying to evaluate node match result",
|
|
|
|
node.location
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} while ( oldResult !== node.match );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
},
|
|
|
|
named: inferenceExpression,
|
|
|
|
choice( node ) {
|
|
|
|
|
|
|
|
node.match = inferenceElements( node.alternatives, true );
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
},
|
|
|
|
action: inferenceExpression,
|
|
|
|
sequence( node ) {
|
|
|
|
|
|
|
|
node.match = inferenceElements( node.elements, false );
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
},
|
|
|
|
labeled: inferenceExpression,
|
|
|
|
text: inferenceExpression,
|
|
|
|
simple_and: inferenceExpression,
|
|
|
|
simple_not( node ) {
|
|
|
|
|
|
|
|
node.match = -inference( node.expression );
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
},
|
|
|
|
optional: alwaysMatch,
|
|
|
|
zero_or_more: alwaysMatch,
|
|
|
|
one_or_more: inferenceExpression,
|
|
|
|
group: inferenceExpression,
|
|
|
|
semantic_and: sometimesMatch,
|
|
|
|
semantic_not: sometimesMatch,
|
|
|
|
rule_ref( node ) {
|
|
|
|
|
|
|
|
const rule = ast.findRule( node.name );
|
|
|
|
node.match = inference( rule );
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
},
|
|
|
|
literal( node ) {
|
|
|
|
|
|
|
|
// Empty literal always match on any input
|
|
|
|
node.match = node.value.length === 0 ? 1 : 0;
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
},
|
|
|
|
class( node ) {
|
|
|
|
|
|
|
|
// Empty character class never match on any input
|
|
|
|
node.match = node.parts.length === 0 ? -1 : 0;
|
|
|
|
|
|
|
|
return node.match;
|
|
|
|
|
|
|
|
},
|
|
|
|
// |any| not match on empty input
|
|
|
|
any: sometimesMatch
|
|
|
|
} );
|
|
|
|
|
|
|
|
inference( ast );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = inferenceMatchResult;
|