From 1fb436efa8cd7fb3f247e9b65132c416b8f91162 Mon Sep 17 00:00:00 2001 From: Pagan Gazzard Date: Fri, 10 Jun 2016 21:41:57 -0700 Subject: [PATCH] Added support for optionally matching keys of an object --- CHANGELOG.md | 1 + lib/ometajs/core.js | 19 +++++++++++++++---- src/bs-ometa-compiler.ometajs | 7 ++++++- test/files/simple.ometajs | 4 ++++ test/unit/api-test.js | 3 ++- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76d71488..2ed69bd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +* Added support for optionally matching keys of an object, eg `@{ foo?: true }` * Added support for matching some keys of an object against corresponding rules, eg `%{ foo: FooRule }` * Added support for matching all keys of an object against corresponding rules, diff --git a/lib/ometajs/core.js b/lib/ometajs/core.js index 22311594..41b2e0bc 100644 --- a/lib/ometajs/core.js +++ b/lib/ometajs/core.js @@ -694,12 +694,23 @@ var ruleKeys = Object.keys(x), result = {}; if(matchAllInput) { - this._pred(ruleKeys.length === Object.keys(v).length); + var inputLength = Object.keys(v).length; + this._pred(ruleKeys.length >= inputLength); } for(var i = 0; i < ruleKeys.length; i++) { - var key = ruleKeys[i]; - this._pred(v.hasOwnProperty(key)); - result[key] = this._applyWithArgs('applyFn', x[key], v[key]); + var key = ruleKeys[i], + exists = v.hasOwnProperty(key), + ruleFn = x[key][0], + required = x[key][1]; + if(required) { + this._pred(exists); + } else if(!exists) { + continue; + } + result[key] = this._applyWithArgs('applyFn', ruleFn, v[key]); + } + if(matchAllInput) { + this._pred(Object.keys(result).length === inputLength); } return v; }, diff --git a/src/bs-ometa-compiler.ometajs b/src/bs-ometa-compiler.ometajs index 1fad8413..787efc64 100644 --- a/src/bs-ometa-compiler.ometajs +++ b/src/bs-ometa-compiler.ometajs @@ -64,7 +64,10 @@ export ometa BSOMetaParser { objectRules = {{}}:o (objectRule:x ',' -> x)*:a objectRule:x -> a.concat([x]), - objectRule = BSJSParser.jsonPropName:n ":" expr:x -> [n, x], + objectRule = BSJSParser.jsonPropName:n + ( "?" -> false + | -> true + ):required ":" expr:x -> [n, x, required], param = ":" name:n -> n, ruleName = name | spaces tsString, @@ -109,6 +112,8 @@ export ometa BSOMetaTranslator { Lookahead transFn:x -> ['this._lookahead(', x, ')'] .join(''), Object [ ( [ string:key transFn:x + (true|false):required + {['[', x, ',', required, ']'].join('')}:x ] -> [JSON.stringify(key), x].join(':') )+:o diff --git a/test/files/simple.ometajs b/test/files/simple.ometajs index e75b7a37..8cbff1e5 100644 --- a/test/files/simple.ometajs +++ b/test/files/simple.ometajs @@ -35,4 +35,8 @@ export ometa Simple { %{ 'simple': true, 'simpler': true } -> 'should fail' | -> 'ok' + , + ObjectRulesMatchOptional = + @{ 'simple': true, 'simpler'?: true } + -> 'ok' } diff --git a/test/unit/api-test.js b/test/unit/api-test.js index 6bf6a2c1..fa7b69b5 100644 --- a/test/unit/api-test.js +++ b/test/unit/api-test.js @@ -25,7 +25,8 @@ var testMatches = { ObjectRulesMatchAll1: { 'simple': true }, ObjectRulesMatchAll2: { 'simple': true, 'simpler': true }, ObjectRulesMatchPartial1: { 'simple': true, 'simpler': true }, - ObjectRulesMatchPartial2: { 'simple': true } + ObjectRulesMatchPartial2: { 'simple': true }, + ObjectRulesMatchOptional: { 'simple': true } }; var runTests = function(test, grammar) {