Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checkin for extending javascript types #182

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions lib/compile/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ var createFunction = function (body, defined, scope, scopeNames, definedNames) {

var createDefined = (function () {

var _createDefined = function (action, defined, scope) {
var _createDefined = function (options, defined, scope) {
var action = options.properties
,proto, base;
//
if (isString(action)) {
var declares = [];
extd(defined).keys().forEach(function (i) {
Expand Down Expand Up @@ -57,7 +60,14 @@ var createDefined = (function () {
}
}
};
var proto = ret.prototype;
if (options.extend) {
base = defined[options.extend];
proto = Object.create(base.prototype);
ret.prototype = proto;
}
else {
proto = ret.prototype;
}
for (var i in action) {
proto[i] = action[i];
}
Expand All @@ -66,7 +76,7 @@ var createDefined = (function () {
};

return function (options, defined, scope) {
return _createDefined(options.properties, defined, scope);
return _createDefined(options, defined, scope);
};
})();

Expand Down
39 changes: 30 additions & 9 deletions lib/compile/transpile.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,47 @@ var extd = require("../extended"),
constraintMatcher = require("../constraintMatcher"),
parser = require("../parser");

function definedToJs(options) {
function definedToJs(options, extend) {
/*jshint evil:true*/
options = isString(options) ? new Function("return " + options + ";")() : options;
var ret = ["(function(){"], value;

var ret = ["(function(flow){"], value;
if (options.hasOwnProperty("constructor") && "function" === typeof options.constructor) {
ret.push("var Defined = " + options.constructor.toString() + ";");
} else {
ret.push("var Defined = function(opts){ for(var i in opts){if(opts.hasOwnProperty(i)){this[i] = opts[i];}}};");
}
ret.push("var proto = Defined.prototype;");
if (extend) {
ret.push("var proto = Object.create(flow.getDefined('" + extend + "').prototype); proto.constructor = Defined; Defined.prototype = proto;");
}
else {
ret.push("var proto = Defined.prototype;");
}

for (var key in options) {
if (options.hasOwnProperty(key)) {
var str;
if (options.hasOwnProperty(key) && key !== 'constructor') {
value = options[key];
ret.push("proto." + key + " = " + (extd.isFunction(value) ? value.toString() : extd.format("%j", value)) + ";");
str = ("proto." + key + " = ");
if (extd.isFunction(value)) {
str += value.toString();
}
else {
if (value instanceof Array) {
str += JSON.stringify(value);
}
else {
str += extd.format("%j", value);
}
str += ";"
}
ret.push(str);
}
}
ret.push("return Defined;");
ret.push("}())");
return ret.join("");
ret.push("}(this))");
var sJoin = ret.join("");
return sJoin;

}

Expand Down Expand Up @@ -148,7 +169,7 @@ exports.transpile = function (flowObj, options) {
ret.push(extd(flowObj.define || []).map(function (defined) {
var name = defined.name;
defined[name] = {};
return ["var", name, "= defined." + name, "= this.addDefined('" + name + "',", definedToJs(defined.properties) + ");"].join(" ");
return ["var", name, "= defined." + name, "= this.addDefined('" + name + "',", definedToJs(defined.properties, defined.extend) + ");"].join(" ");
}).value().join("\n"));
ret.push(extd(flowObj.scope || []).map(function (s) {
var name = s.name;
Expand Down
7 changes: 6 additions & 1 deletion lib/parser/nools/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,17 @@ var topLevelTokens = {
var name = src.match(/^([a-zA-Z_$][0-9a-zA-Z_$]*)/);
if (name) {
src = src.replace(name[0], "").replace(/^\s*|\s*$/g, "");
var extend = src.match(/^extends\s+([a-zA-Z_$][0-9a-zA-Z_$]*)/);
if (extend) {
src = src.replace(extend[0], "").replace(/^\s*|\s*$/g, "");
extend = extend[1];
}
if (utils.findNextToken(src) === "{") {
name = name[1];
var body = utils.getTokensBetween(src, "{", "}", true).join("");
src = src.replace(body, "");
//should
context.define.push({name: name, properties: "(" + body + ")"});
context.define.push({ name: name, extend: extend, properties: "(" + body + ")" });
return src;
} else {
throw new Error("unexpected token : expected : '{' found : '" + utils.findNextToken(src) + "'");
Expand Down
14 changes: 14 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,20 @@ session.modify(m);

**Note** `modify` is typically used during the execution of the rules.

# Type Declaration 'extends'
Defined types in the DSL no support 'extends' keyword for inheritance.

Any type present in the flow can be extended. Base types must be defined before extended types.

```
define EncodedMessage extends Message {
encoding: 'sha1'
,constructor: function(message, encoding) {
Message.call(this, message); // call superclass manually
this.encoding = encoding
}
}

<a name="get-facts"></a>
### Retrieving Facts

Expand Down
23 changes: 23 additions & 0 deletions test/flow.dsl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,4 +593,27 @@ it.describe("Flow dsl", function (it) {
});
});
});

it.describe("extended Fact types", function (it) {
var Person, Student, LongTermStudent, Count, session1Fired = {}, session1;
var flow = nools.compile(resolve(__dirname, "./rules/extends.nools"), {
name: 'extendsTest'
});
Person = flow.getDefined('Person');
Student = flow.getDefined('Student');
LongTermStudent = flow.getDefined('LongTermStudent');
//
session1 = flow.getSession(new Person('Bob'), new Student('Sue', 'harvard'), new LongTermStudent('Jim', 'princeton', 1)).on('fire', function (name) {
session1Fired[name] = session1Fired[name] || { cnt: 0 };
session1Fired[name].cnt++;
});
//
it.should(" fire rule(s) correctly according to type. each rule matching a single type ", function (next) {
return session1.match().then(function () {
assert(session1Fired['PersonTest'].cnt === 3);
assert(session1Fired['StudentTest'].cnt === 2);
assert(session1Fired['LongTermStudentTest'].cnt === 1);
})
});
});
});
3 changes: 3 additions & 0 deletions test/noolsParser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ it.describe("nools dsl parser", function (it) {
define: [
{
name: "Test",
extend: null,
properties: "({myProp : 'value'})"
}
],
Expand All @@ -28,6 +29,7 @@ it.describe("nools dsl parser", function (it) {
define: [
{
name: "Test",
extend: null,
properties: "({myFunc : function(){}})"
}
],
Expand Down Expand Up @@ -183,6 +185,7 @@ it.describe("nools dsl parser", function (it) {
"define": [
{
"name": "Count",
extend: null,
"properties": "({ constructor: function(){ this.called = 0; } })"
}
],
Expand Down
46 changes: 46 additions & 0 deletions test/rules/extends.nools
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

define Person {
constructor: function(name){
this.name = name;
}
}

define Student extends Person {
constructor: function(name, school) {
Person.call(this, name);
this.school = school;
}
}

define LongTermStudent extends Student {
constructor: function(name, school, years) {
Student.call(this, name, school);
this.years = years;
}
}


rule PersonTest {
when {
p: Person;
}
then {
}
}

rule StudentTest {
when {
s: Student;
}
then {
}
}


rule LongTermStudentTest {
when {
s: LongTermStudent;
}
then {
}
}