You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

128 lines
3.4 KiB

var rules = require("./rules");
var results = require("./parsing-results");
exports.parser = function(name, prefixRules, infixRuleBuilders) {
var self = {
rule: rule,
leftAssociative: leftAssociative,
rightAssociative: rightAssociative
};
var infixRules = new InfixRules(infixRuleBuilders.map(createInfixRule));
var prefixRule = rules.firstOf(name, prefixRules);
function createInfixRule(infixRuleBuilder) {
return {
name: infixRuleBuilder.name,
rule: lazyRule(infixRuleBuilder.ruleBuilder.bind(null, self))
};
}
function rule() {
return createRule(infixRules);
}
function leftAssociative(name) {
return createRule(infixRules.untilExclusive(name));
}
function rightAssociative(name) {
return createRule(infixRules.untilInclusive(name));
}
function createRule(infixRules) {
return apply.bind(null, infixRules);
}
function apply(infixRules, tokens) {
var leftResult = prefixRule(tokens);
if (leftResult.isSuccess()) {
return infixRules.apply(leftResult);
} else {
return leftResult;
}
}
return self;
};
function InfixRules(infixRules) {
function untilExclusive(name) {
return new InfixRules(infixRules.slice(0, ruleNames().indexOf(name)));
}
function untilInclusive(name) {
return new InfixRules(infixRules.slice(0, ruleNames().indexOf(name) + 1));
}
function ruleNames() {
return infixRules.map(function(rule) {
return rule.name;
});
}
function apply(leftResult) {
var currentResult;
var source;
while (true) {
currentResult = applyToTokens(leftResult.remaining());
if (currentResult.isSuccess()) {
source = leftResult.source().to(currentResult.source());
leftResult = results.success(
currentResult.value()(leftResult.value(), source),
currentResult.remaining(),
source
)
} else if (currentResult.isFailure()) {
return leftResult;
} else {
return currentResult;
}
}
}
function applyToTokens(tokens) {
return rules.firstOf("infix", infixRules.map(function(infix) {
return infix.rule;
}))(tokens);
}
return {
apply: apply,
untilExclusive: untilExclusive,
untilInclusive: untilInclusive
}
}
exports.infix = function(name, ruleBuilder) {
function map(func) {
return exports.infix(name, function(parser) {
var rule = ruleBuilder(parser);
return function(tokens) {
var result = rule(tokens);
return result.map(function(right) {
return function(left, source) {
return func(left, right, source);
};
});
};
});
}
return {
name: name,
ruleBuilder: ruleBuilder,
map: map
};
}
// TODO: move into a sensible place and remove duplication
var lazyRule = function(ruleBuilder) {
var rule;
return function(input) {
if (!rule) {
rule = ruleBuilder();
}
return rule(input);
};
};