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

Implements the extends keyword for types defined in the dsl. #165

Closed
wants to merge 1 commit into from
Closed
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comma on previous line

//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty comment

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} 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
40 changes: 31 additions & 9 deletions lib/compile/transpile.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,48 @@ 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} 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);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should change this to use JSON.stringify or format since they are doing the same thing.

str += ";"
}
ret.push(str);
}
}
ret.push("return Defined;");
ret.push("}())");
return ret.join("");
ret.push("}(this))");
var sJoin = ret.join("");
debugger;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this debugger

return sJoin;

}

Expand Down Expand Up @@ -148,7 +170,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
15 changes: 13 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,19 @@ To use the flow
var flow = nools.compile(__dirname + "/helloworld.nools"),
Message = flow.getDefined("message");
```
# 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting

Message.call(this, message); // call superclass manually
this.encoding = encoding
}
}

### Flow Events

Expand Down Expand Up @@ -341,7 +354,6 @@ session.getFacts(Number); //[1, 2];
session.getFacts(String); //["A", "B"];
```


<a name="firing"></a>
## Firing the rules

Expand Down Expand Up @@ -1004,7 +1016,6 @@ flow1
```



<a name="rule-scope"></a>
### Scope

Expand Down
22 changes: 22 additions & 0 deletions test/flow.dsl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,4 +593,26 @@ 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');
//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty comments

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);
})
});
});
});
5 changes: 4 additions & 1 deletion test/noolsParser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ it.describe("nools dsl parser", function (it) {
define: [
{
name: "Test",
properties: "({myProp : 'value'})"
extend: null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting

properties: "({myProp : 'value'})"
}
],
"rules": [],
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spacing throughout this file

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 {
}
}