UNPKG

diet-yadda

Version:

A true BDD framework for JavaScript - slimmed down

136 lines (111 loc) 5.15 kB
/* * Copyright 2010 Acuminous Ltd / Energized Work Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jslint node: true */ "use strict"; var $ = require('./Array'); var RegularExpression = require('./RegularExpression'); var pass_through_converter = require('./converters/pass-through-converter'); // Understands term definitions var Dictionary = function(prefix) { /* jslint shadow: true */ var prefix = prefix || '$'; var definitions = {}; var term_grouping_pattern = new RegularExpression(new RegExp('(?:^|[^\\\\])\\' + prefix + '(\\w+)', 'g')); var term_splitting_pattern = new RegExp('(\\' + prefix + '\\w+)'); var _this = this; this.define = function(term, pattern, converters) { if (is_defined(term)) throw new Error('Duplicate term: [' + term + ']'); if (converters && is_expandable(pattern)) throw new Error('Expandable terms cannot use converters: [' + term + ']'); if (converters && !is_compatible(converters, pattern)) throw new Error('Wrong number of converters for: [' + term + ']'); if (!is_expandable(pattern) && !converters) converters = get_matching_group_converters(pattern); definitions[term] = { pattern: normalise(pattern), converters: $(converters) }; return this; }; this.merge = function(other) { if (other._prefix() != this._prefix()) throw new Error('Cannot merge dictionaries with different prefixes'); return new Dictionary(prefix)._merge(this)._merge(other); }; this._merge = function(other) { other.each(function(term, definition) { _this.define(term, definition.pattern); }); return this; }; this._prefix = function() { return prefix; }; this.each = function(callback) { for (var term in definitions) { callback(term, definitions[term]); } }; this.expand = function(signature, already_expanding) { var text = normalise(signature); return is_expandable(text) ? { pattern: expand_sub_terms(text, $(already_expanding)), converters: get_converters(text) } : { pattern: text, converters: get_converters(text) }; }; function expand_sub_terms(text, already_expanding) { return get_sub_terms(text).each(function(sub_term) { if (already_expanding.in_array(sub_term)) throw new Error('Circular Definition: [' + already_expanding.join(', ') + ']'); var sub_term_grouping_pattern = expand_sub_term(sub_term, already_expanding); text = text.replace(prefix + sub_term, sub_term_grouping_pattern); return text; }); } function get_sub_terms(text) { return term_grouping_pattern.groups(text); } function expand_sub_term(sub_term, already_expanding) { var pattern = definitions[sub_term] ? definitions[sub_term].pattern : '(.+)'; return is_expandable(pattern) ? _this.expand(pattern, already_expanding.concat(sub_term)).pattern : pattern; } function normalise(pattern) { return pattern.toString().replace(/^\/|\/$/g, ''); } function is_defined(term) { return !!definitions[term]; } function is_expandable(text) { return term_grouping_pattern.test(text); } function is_compatible(converters, pattern) { return count_converter_arguments(converters) === count_matching_groups(pattern); } function get_converters(text) { return $(text.split(term_splitting_pattern)).inject($(), function(converters, fragment) { return converters.push_all(is_expandable(fragment) ? get_sub_term_converters(fragment) : get_matching_group_converters(fragment)); }); } function get_matching_group_converters(text) { return $().fill(pass_through_converter, count_matching_groups(text)); } function get_sub_term_converters(text) { return get_sub_terms(text).inject($(), function(converters, sub_term) { return is_defined(sub_term) ? converters.push_all(definitions[sub_term].converters) : converters.push_all(get_converters(expand_sub_term(sub_term, []))); }); } function count_matching_groups(pattern) { return new RegExp(pattern + '|').exec('').length - 1; } function count_converter_arguments(converters) { return $(converters).inject(0, function(sum, converter) { return sum + converter.length - 1; }); } }; module.exports = Dictionary;