Skip to content

Commit

Permalink
v0.1.10
Browse files Browse the repository at this point in the history
* Added ability to `import` other nools files to create composible rules files [#58](#58)
* When using `global` to require other files you can now require other files relative to the nools file
* Added uglify-js as a dependency instead of devDependency #71
* Fixed issue #61 where transpile would not properly escape `"` character.
* Fixed issue #66 and #67 where regular expression matching was too greedy.
* Fixed issue #62 where constraints with a `"` character would produce invalid javascript when `transpiling`.
* Fixed issue #69 where rules names with a `'` character in a rule would produce invalid javascript when `transpiling`.
  • Loading branch information
doug-martin committed Sep 4, 2013
1 parent c8d46f4 commit fcec129
Show file tree
Hide file tree
Showing 30 changed files with 14,854 additions and 14,159 deletions.
4 changes: 2 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ module.exports = function (grunt) {
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;' +
' Licensed <%= pkg.license %> */\n'
//report: 'gzip'
' Licensed <%= pkg.license %> */\n',
report: 'gzip'
},
min: {
files: {
Expand Down
11 changes: 11 additions & 0 deletions docs/History.html
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@



<h1>0.1.10</h1>
<ul>
<li>Added ability to <code>import</code> other nools files to create composible rules files <a href="https://github.com/C2FO/nools/issues/58">#58</a></li>
<li>When using <code>global</code> to require other files you can now require other files relative to the nools file</li>
<li>Added uglify-js as a dependency instead of devDependency</li>
<li>Fixed issue <a href="https://github.com/C2FO/nools/issues/61">#61</a> where transpile would not properly escape <code>&quot;</code> character.</li>
<li>Fixed issue <a href="https://github.com/C2FO/nools/issues/66">#66</a> and <a href="https://github.com/C2FO/nools/issues/67">#67</a> where regular expression matching was too greedy.</li>
<li>Fixed issue <a href="https://github.com/C2FO/nools/issues/62">#62</a> where constraints with a <code>&quot;</code> character would produce invalid javascript when <code>transpiling</code>.</li>
<li>Fixed issue <a href="https://github.com/C2FO/nools/issues/69">#69</a> where rules names with a <code>&#39;</code> character in a rule would produce invalid javascript when <code>transpiling</code>.
*</li>
</ul>
<h1>0.1.9</h1>
<ul>
<li>Fixed issues where notNode was not retracting all contexts when a fact was retracted</li>
Expand Down
46 changes: 46 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ <h1>Usage</h1>
<li><a href="#constraints">Constraints</a></li>
<li><a href="#action">Actions</a></li>
<li><a href="#globals">Globals</a></li>
<li><a href="#import">Import</a></li>
</ul>
</li>
<li><a href="#browser-support">Browser Support</a></li>
Expand Down Expand Up @@ -977,6 +978,51 @@ <h3>Globals</h3>
util.log(&quot;HELLO WORLD&quot;);
}
}</code></pre>
<p><a name="import"></a></p>
<h3>Importing</h3>
<p>The <code>import</code> statement allows you to import other <code>nools</code> files into the current one. This can be used to split up logical flows into small reusable groups of rules.</p>
<p>Define our common model to be used across our flows.</p>
<pre class='prettyprint linenums lang-js'><code>//define.nools
define Count{
constructor: function(){
this.called = 0;
}
}</code></pre>
<p>Create a rules file which imports the <code>define.nools</code> to define our <code>Count</code> model.</p>
<pre class='prettyprint linenums lang-js'><code>//orRule.nools

//import define.nools
import(&quot;./define.nools&quot;);
rule orRule {
when {
or(
s : String s == &#39;hello&#39;,
s : String s == &#39;world&#39;
);
count : Count;
}
then {
count.called++;
count.s = s;
}
}</code></pre>
<p>Same as <code>orRule.nools</code> import our <code>define.nools</code></p>
<pre class='prettyprint linenums lang-js'><code>//notRule.nools
import(&quot;./defines.nools&quot;);
rule notRule {
when {
not(s : String s == &#39;hello&#39;);
count : Count
}
then {
count.called++;
}
}</code></pre>
<p>Now we can use <code>orRule.nools</code> and <code>notRule.nools</code> to compose a new flow that contains <code>define.nools</code>, <code>orRule.nools</code> and <code>notRule.nools</code>.</p>
<p><strong>Note</strong> <code>nools</code> will handle duplicate imports, in this case <code>define.nools</code> will only be imported once.</p>
<pre class='prettyprint linenums lang-js'><code>//import
import(&quot;./orRules.nools&quot;);
import(&quot;./notRules.nools&quot;);</code></pre>
<h2>Emitting custom events.</h2>
<p>You may also emit events from your rule actions using the sessions emit function.</p>
<pre class='prettyprint linenums lang-js'><code>then {
Expand Down
15 changes: 8 additions & 7 deletions docs/nools.js

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions history.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# 0.1.10

* Added ability to `import` other nools files to create composible rules files [#58](https://github.com/C2FO/nools/issues/58)
* When using `global` to require other files you can now require other files relative to the nools file
* Added uglify-js as a dependency instead of devDependency
* Fixed issue [#61](https://github.com/C2FO/nools/issues/61) where transpile would not properly escape `"` character.
* Fixed issue [#66](https://github.com/C2FO/nools/issues/66) and [#67](https://github.com/C2FO/nools/issues/67) where regular expression matching was too greedy.
* Fixed issue [#62](https://github.com/C2FO/nools/issues/62) where constraints with a `"` character would produce invalid javascript when `transpiling`.
* Fixed issue [#69](https://github.com/C2FO/nools/issues/69) where rules names with a `'` character in a rule would produce invalid javascript when `transpiling`.
*

# 0.1.9

* Fixed issues where notNode was not retracting all contexts when a fact was retracted
Expand Down
4 changes: 2 additions & 2 deletions lib/compile/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ var createRuleFromObject = (function () {
};
})();

exports.parse = function (src) {
exports.parse = function (src, file) {
//parse flow from file
return parser.parseRuleSet(src);
return parser.parseRuleSet(src, file);

};
exports.compile = function (flowObj, options, cb, Container) {
Expand Down
6 changes: 3 additions & 3 deletions lib/compile/transpile.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ function constraintsToJs(constraint, identifiers) {
ret.push('"', constraint.shift(), '", ');
}
identifiers.push(constraint[1]);
ret.push(constraint[0], ', "' + constraint[1].replace(/"/g, "\"") + '"');
ret.push(constraint[0], ', "' + constraint[1].replace(/"/g, "\\\"") + '"');
constraint.splice(0, 2);
if (constraint.length) {
//constraint
var c = constraint.shift();
if (extd.isString(c)) {
ret.push(',"' + c.replace(/"/g, "\""), '"');
ret.push(',"' + c.replace(/"/g, "\\\""), '"');
forEach(constraintMatcher.getIdentifiers(parser.parseConstraint(c)), function (i) {
identifiers.push(i);
});
Expand Down Expand Up @@ -128,7 +128,7 @@ exports.transpile = function (flowObj, options) {


ret.push(extd(flowObj.rules || []).map(function (rule) {
var identifiers = [], ret = ["this.rule('", rule.name, "'"], options = extd.merge(rule.options || {}, {scope: "scope"});
var identifiers = [], ret = ["this.rule('", rule.name.replace(/'/g, "\\'"), "'"], options = extd.merge(rule.options || {}, {scope: "scope"});
ret.push(",", extd.format("%j", [options]).replace(/(:"scope")/, ":scope"));
if (rule.constraints && !extd.isEmpty(rule.constraints)) {
ret.push(", [");
Expand Down
15 changes: 11 additions & 4 deletions lib/constraintMatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ var lang = {
//its an identifier so stop
return [rule[0]];
} else if (rule2 === "function") {
ret.push(rule[0]);
ret = ret.concat(this.getIdentifiers(rule[1]));
ret = ret.concat(this.getIdentifiers(rule[0])).concat(this.getIdentifiers(rule[1]));
} else if (rule2 !== "string" &&
rule2 !== "number" &&
rule2 !== "boolean" &&
Expand Down Expand Up @@ -168,6 +167,7 @@ var lang = {
rule2 === "in" ||
rule2 === "notIn" ||
rule2 === "prop" ||
rule2 === "propLookup" ||
rule2 === "function" ||
rule2 === "logicalNot") {
if (some(this.getIdentifiers(rule), function (i) {
Expand Down Expand Up @@ -206,6 +206,14 @@ var lang = {
}
},

propLookup: function (name, prop) {
if (prop[2] === "function") {
return [this.parse(name), this.parse(prop)].join(".");
} else {
return [this.parse(name), "[", this.parse(prop), "]"].join("");
}
},

unary: function (lhs) {
return -1 * this.parse(lhs);
},
Expand Down Expand Up @@ -288,7 +296,7 @@ var lang = {

"function": function (lhs, rhs) {
var args = this.parse(rhs);
return [lhs, "(", args, ")"].join("");
return [this.parse(lhs), "(", args, ")"].join("");
},

"string": function (lhs) {
Expand Down Expand Up @@ -338,7 +346,6 @@ var toJs = exports.toJs = function (rule, scope) {
return ret.join("");
}).join("") + " return !!(" + js + ");";
var f = new Function("fact, hash, definedFuncs, scope", body);

return function (fact, hash) {
return f(fact, hash, definedFuncs, scope);
};
Expand Down
2 changes: 1 addition & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ function isNoolsFile(file) {
function parse(source) {
var ret;
if (isNoolsFile(source)) {
ret = compile.parse(fs.readFileSync(source, "utf8"));
ret = compile.parse(fs.readFileSync(source, "utf8"), source);
} else {
ret = compile.parse(source);
}
Expand Down
21 changes: 9 additions & 12 deletions lib/parser/constraint/grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var grammar = {
["\'[^\']*\'", "return 'STRING';"],
['\"[^\"]*\"', "return 'STRING';"],
["([a-zA-Z_$][0-9a-zA-Z_$]*)", "return 'IDENTIFIER';"],
["\\/(.*)\\/", "return 'REGEXP';"],
["^\\/((?![\\s=])[^[\\/\\n\\\\]*(?:(?:\\\\[\\s\\S]|\\[[^\\]\\n\\\\]*(?:\\\\[\\s\\S][^\\]\\n\\\\]*)*])[^[\\/\\n\\\\]*)*\\/[imgy]{0,4})(?!\\w)", "return 'REGEXP';"],
["\\.", "return '.';"],
["\\*", "return '*';"],
["\\/", "return '/';"],
Expand Down Expand Up @@ -121,19 +121,17 @@ var grammar = {
["ARGUMENT_LIST , LITERAL_EXPRESSION", "$$ = [$1, $3, 'arguments']"]
],

"FUNCTION": [
["IDENTIFIER ( )", "$$ = [$1, [null, null, 'arguments'], 'function']"],
["IDENTIFIER ( ARGUMENT_LIST )", "$$ = [$1, $3, 'function']"]
"IDENTIFIER_EXPRESSION": [
[ "IDENTIFIER", "$$ = [String(yytext), null, 'identifier'];" ]
],

"OBJECT_EXPRESSION": [
"IDENTIFIER_EXPRESSION",
[ "OBJECT_EXPRESSION . IDENTIFIER_EXPRESSION", "$$ = [$1,$3, 'prop'];" ],
[ "OBJECT_EXPRESSION . FUNCTION", "$$ = [$1,$3, 'prop'];" ]
],

"IDENTIFIER_EXPRESSION": [
[ "IDENTIFIER", "$$ = [String(yytext), null, 'identifier'];" ]
[ "OBJECT_EXPRESSION [ STRING_EXPRESSION ]", "$$ = [$1,$3, 'propLookup'];" ],
[ "OBJECT_EXPRESSION [ OBJECT_EXPRESSION ]", "$$ = [$1,$3, 'propLookup'];" ],
[ "OBJECT_EXPRESSION ( )", "$$ = [$1, [null, null, 'arguments'], 'function']"],
[ "OBJECT_EXPRESSION ( ARGUMENT_LIST )", "$$ = [$1, $3, 'function']"]
],

"STRING_EXPRESSION": [
Expand All @@ -145,7 +143,7 @@ var grammar = {
],

"REGEXP_EXPRESSION": [
[ "REGEXP", "$$ = [RegExp(yytext.replace(/^\\/|\\/$/g, '')), null, 'regexp'];" ]
[ "REGEXP", "$$ = [yytext, null, 'regexp'];" ]
],

"BOOLEAN_EXPRESSION": [
Expand All @@ -168,9 +166,8 @@ var grammar = {
"REGEXP_EXPRESSION",
"BOOLEAN_EXPRESSION",
"NULL_EXPRESSION",
"FUNCTION",
"ARRAY_EXPRESSION",
"OBJECT_EXPRESSION",
"ARRAY_EXPRESSION",
[ "( EXPRESSION )", "$$ = [$2, null, 'composite']" ]
],

Expand Down
60 changes: 31 additions & 29 deletions lib/parser/constraint/parser.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions lib/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}
};

exports.parseRuleSet = function (source) {
return noolParser.parse(source);
exports.parseRuleSet = function (source, file) {
return noolParser.parse(source, file);
};
})();
4 changes: 2 additions & 2 deletions lib/parser/nools/nool.parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ var parse = function (src, keywords, context) {
}
};

exports.parse = function (src) {
var context = {define: [], rules: [], scope: []};
exports.parse = function (src, file) {
var context = {define: [], rules: [], scope: [], loaded: [], file: file};
parse(src, tokens, context);
return context;
};
Expand Down
48 changes: 45 additions & 3 deletions lib/parser/nools/tokens.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
"use strict";

var utils = require("./util.js");
var utils = require("./util.js"),
fs = require("fs"),
indexOf = require("../../extended").indexOf;

var isWhiteSpace = function (str) {
return str.replace(/[\s|\n|\r|\t]/g, "").length === 0;
};


var ruleTokens = {

salience: (function () {
Expand Down Expand Up @@ -165,7 +166,7 @@ var ruleTokens = {
})()
};

module.exports = {
var topLevelTokens = {
"/": function (orig) {
if (orig.match(/^\/\*/)) {
// Block Comment parse
Expand Down Expand Up @@ -195,6 +196,36 @@ module.exports = {
}
},

"import": function (orig, context, parse) {
if (typeof window !== 'undefined') {
throw new Error("import cannot be used in a browser");
}
var src = orig.replace(/^import\s*/, "");
if (utils.findNextToken(src) === "(") {
var file = utils.getParamList(src);
src = src.replace(file, "").replace(/^\s*|\s*$/g, "");
utils.findNextToken(src) === ";" && (src = src.replace(/\s*;/, ""));
file = file.replace(/[\(|\)]/g, "").split(",");
if (file.length === 1) {
file = utils.resolve(context.file || process.cwd(), file[0].replace(/["|']/g, ""));
if (indexOf(context.loaded, file) === -1) {
var origFile = context.file;
context.file = file;
parse(fs.readFileSync(file, "utf8"), topLevelTokens, context);
context.loaded.push(file);
context.file = origFile;
}
return src;
} else {
throw new Error("import accepts a single file");
}
} else {
throw new Error("unexpected token : expected : '(' found : '" + utils.findNextToken(src) + "'");
}

},

//define a global
"global": function (orig, context) {
var src = orig.replace(/^global\s*/, "");
var name = src.match(/^([a-zA-Z_$][0-9a-zA-Z_$]*\s*)/);
Expand All @@ -205,6 +236,14 @@ module.exports = {
var fullbody = utils.getTokensBetween(src, "=", ";", true).join("");
var body = fullbody.substring(1, fullbody.length - 1);
body = body.replace(/^\s+|\s+$/g, '');
if (/^require\(/.test(body)) {
var file = utils.getParamList(body.replace("require")).replace(/[\(|\)]/g, "").split(",");
if (file.length === 1) {
//handle relative require calls
file = file[0].replace(/["|']/g, "");
body = ["require('", utils.resolve(context.file || process.cwd(), file) , "')"].join("");
}
}
context.scope.push({name: name, body: body});
src = src.replace(fullbody, "");
return src;
Expand All @@ -216,8 +255,10 @@ module.exports = {
}
},

//define a function
"function": function (orig, context) {
var src = orig.replace(/^function\s*/, "");
//parse the function name
var name = src.match(/^([a-zA-Z_$][0-9a-zA-Z_$]*)\s*/);
if (name) {
src = src.replace(name[0], "");
Expand Down Expand Up @@ -264,4 +305,5 @@ module.exports = {

}
};
module.exports = topLevelTokens;

Loading

0 comments on commit fcec129

Please sign in to comment.