Permalink
Browse files

v0.1.10

* 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...
1 parent c8d46f4 commit fcec129cd1057ef4dac917b9bf8fe3b2500b5558 @doug-martin doug-martin committed Sep 4, 2013
View
@@ -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: {
View
@@ -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>
View
@@ -209,6 +209,7 @@
<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>
@@ -977,6 +978,51 @@
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 {
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -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
View
@@ -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) {
View
@@ -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);
});
@@ -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(", [");
View
@@ -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" &&
@@ -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) {
@@ -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);
},
@@ -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) {
@@ -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);
};
View
@@ -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);
}
@@ -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 '/';"],
@@ -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": [
@@ -145,7 +143,7 @@ var grammar = {
],
"REGEXP_EXPRESSION": [
- [ "REGEXP", "$$ = [RegExp(yytext.replace(/^\\/|\\/$/g, '')), null, 'regexp'];" ]
+ [ "REGEXP", "$$ = [yytext, null, 'regexp'];" ]
],
"BOOLEAN_EXPRESSION": [
@@ -168,9 +166,8 @@ var grammar = {
"REGEXP_EXPRESSION",
"BOOLEAN_EXPRESSION",
"NULL_EXPRESSION",
- "FUNCTION",
- "ARRAY_EXPRESSION",
"OBJECT_EXPRESSION",
+ "ARRAY_EXPRESSION",
[ "( EXPRESSION )", "$$ = [$2, null, 'composite']" ]
],

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -11,7 +11,7 @@
}
};
- exports.parseRuleSet = function (source) {
- return noolParser.parse(source);
+ exports.parseRuleSet = function (source, file) {
+ return noolParser.parse(source, file);
};
})();
@@ -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;
};
View
@@ -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 () {
@@ -165,7 +166,7 @@ var ruleTokens = {
})()
};
-module.exports = {
+var topLevelTokens = {
"/": function (orig) {
if (orig.match(/^\/\*/)) {
// Block Comment parse
@@ -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*)/);
@@ -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;
@@ -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], "");
@@ -264,4 +305,5 @@ module.exports = {
}
};
+module.exports = topLevelTokens;
Oops, something went wrong.

0 comments on commit fcec129

Please sign in to comment.