Skip to content

Commit

Permalink
Add argument variety to CFG.rule()
Browse files Browse the repository at this point in the history
Closes #2.
In addition to providing a Rule instance or just a string to `rule()`,
the method should take arguments as if a Rule were being constructed
(Sym, Sym[], Function), and it should take a valuation Function if a
stringy rule is given.
  • Loading branch information
patgrasso committed Aug 5, 2016
1 parent 78da7ac commit 06933d0
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 5 deletions.
35 changes: 30 additions & 5 deletions lib/cfg.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,16 @@ Object.setPrototypeOf(CFG.prototype, Array.prototype);

/**
* Adds a rule to the grammar. The rule can be either an instance of
* {@link Rule}, or a string of the form
* {@link Rule}, or arguments for the Rule constructor, or a string of the form
* <pre>
* A -> B C D
* </pre>
*
* @example
* grammar.rule(Rule(S, [NP, VP], (np, vp) => ...));
* grammar.rule(S, [NP, VP], (np, vp) => ...);
* grammar.rule('S -> NP VP', (np, vp) => ...);
*
* Symbols in a stringy rule (such as 'A', 'B', 'C', and 'D' above) will be
* searched for by name in existing rules and replaced if a match is found, or
* if no match is found a new Sym will be created.
Expand All @@ -52,17 +58,28 @@ Object.setPrototypeOf(CFG.prototype, Array.prototype);
* converted into RegExp objects
*
* @method rule
* @param {string|Rule} theRule - Rule object or string describing a production
* @param {string|Sym|Rule} theRule - Rule object or a LHS Sym or string
* describing a production
* @param {Function=|Sym[]} maybeRHS - If `theRule` is a string, this will be
* the valuation function. If `theRule` is a Sym, this will be the RHS
* @param {Function=} maybeValuator - If `theRule` is a Sym and `maybeRHS` is
* an array of Syms (so the arguments mimic the Rule constructor), then this
* is an optional valuation function
* @return {Rule} The rule that was either passed in, or created from the string
* passed in, and subsequently added to the grammar
* @throws {SyntaxError} If a stringy rule is passed in that doesn't have a ->
* separator
*/
CFG.prototype.rule = function (theRule) {
CFG.prototype.rule = function (theRule, maybeRHS, maybeValuator) {
var rhs, lhs, syms, match;

if (typeof theRule === 'string') {
// case 1: Rule constructor
if (theRule instanceof Sym) {
theRule = new Rule(theRule, maybeRHS, maybeValuator);
this.push(theRule);

// case 2: stringy production
} else if (typeof theRule === 'string') {
// split sides by separator arrow
lhs = theRule.split('->').map((side) => side.trim());
rhs = lhs[1];
Expand Down Expand Up @@ -91,8 +108,16 @@ CFG.prototype.rule = function (theRule) {
// otherwise, find || make a Sym for it
return syms[name] || (syms[name] = new Sym(name));
});
theRule = new Rule(lhs, rhs);

// check for the optional valuator
if (maybeRHS instanceof Function) {
theRule = new Rule(lhs, rhs, maybeRHS);
} else {
theRule = new Rule(lhs, rhs);
}
this.push(theRule);

// case 3: actual instance of Rule
} else if (theRule instanceof Rule) {
this.push(theRule);
}
Expand Down
33 changes: 33 additions & 0 deletions spec/cfg-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,39 @@ describe('CFG', () => {
}
});

it('accepts a Sym, Sym[], and Function (args to Rule constructor) as args', () => {
r = cfg.rule(s, [np, vp], (hello) => hello + ' world');
expect(cfg.length).toBe(1);
expect(cfg[0]).toBe(r);
expect(r.lhs).toBe(s);
expect(r).toEqual(jasmine.arrayContaining([np, vp]));
expect(r.evaluate(['hello'])).toBe('hello world');
});

it('accepts a stringy rule ("A -> B C D") and a valuation function', () => {
r = cfg.rule('A -> B C D', (b, c, d) => b + c + d);
expect(cfg.length).toBe(1);
expect(cfg[0]).toBe(r);
expect(r.lhs).toEqual(Sym('A'));
expect(r.length).toBe(3);
expect(r.evaluate(['1', '2', '3'])).toBe('123');
});

it('fails if a Sym is the first and only argument', () => {
let f = () => cfg.rule(Sym('A'));
expect(f).toThrowError();
});

it('fails if a Sym is the first arg and a non-array is the second', () => {
let f = () => cfg.rule(Sym('A'), 'not an array');
expect(f).toThrowError();
});

it('ignores the "valuation function" if it is\'t actually a function', () => {
r = cfg.rule('A -> B', 'spaghettios');
expect(r.evaluate('something')).toBeNull();
});

});

describe('getSymbols()', () => {
Expand Down

0 comments on commit 06933d0

Please sign in to comment.