Compare commits
14 Commits
master
...
transforms
Author | SHA1 | Date | |
---|---|---|---|
|
158b287116 | ||
|
fd8686b305 | ||
|
5a1d655ce8 | ||
|
78708856eb | ||
|
230a48e409 | ||
|
fd7accb887 | ||
|
f0c040b533 | ||
|
839753a636 | ||
|
a134e6ed60 | ||
|
0112fcd205 | ||
|
cb5a2befea | ||
|
19b309a29e | ||
|
e357ccea01 | ||
|
97a0accfd1 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,3 +18,4 @@ dist/
|
||||
*.ps
|
||||
*.toc
|
||||
*.gz
|
||||
index.html
|
||||
|
@ -1,7 +1,9 @@
|
||||
module.exports = [
|
||||
"conceptualise an ~ entity ~ E",
|
||||
"conceptualise a ~ transform ~ T that has the value W as ~ output ~ and has the value F as ~ transform function ~",
|
||||
"conceptualise a ~ transformer ~ T that ~ uses ~ the transform U",
|
||||
"conceptualise an ~ imageable thing ~ I that has the value V as ~ image ~",
|
||||
"conceptualise a ~ timestamp ~ T that is an entity",
|
||||
"conceptualise a ~ timestamp ~ T that is a transformer and has the value V as ~ date ~",
|
||||
"conceptualise an ~ agent ~ A that is an entity and has the value V as ~ address ~",
|
||||
"conceptualise an ~ individual ~ I that is an ~ agent ~",
|
||||
"conceptualise a ~ card ~ C that is an entity and has the timestamp T as ~ timestamp ~ and has the value V as ~ content ~ and has the value W as ~ linked content ~ and has the value V as ~ number of keystrokes ~ and has the timestamp T as ~ start time ~ and has the value W as ~ submit time ~ and has the value L as ~ latitude ~ and has the value M as ~ longitude ~",
|
||||
@ -21,5 +23,6 @@ module.exports = [
|
||||
"conceptualise a ~ listen policy ~ P that is a policy",
|
||||
"conceptualise a ~ listen onbehalfof policy ~ P that is a policy",
|
||||
"conceptualise a ~ forwardall policy ~ P that is a policy and has the timestamp T as ~ start time ~ and has the value V as ~ all agents ~",
|
||||
"conceptualise a ~ feedback policy ~ P that is a policy and has the value V as ~ acknowledgement ~"
|
||||
"conceptualise a ~ feedback policy ~ P that is a policy and has the value V as ~ acknowledgement ~",
|
||||
"conceptualise a ~ transform policy ~ P that is a policy and has the value V as ~ transform type ~ and ~ uses ~ the transform U"
|
||||
];
|
||||
|
@ -158,6 +158,7 @@ class CEInstance {
|
||||
if (propagate !== false) {
|
||||
this.node.ruleEngine.enactRules(this, 'value', valueInstance, source);
|
||||
}
|
||||
this.node.transformEngine.enactTransforms(this, label, valueInstance, source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -197,6 +198,7 @@ class CEInstance {
|
||||
if (propagate !== false) {
|
||||
this.node.ruleEngine.enactRules(this, 'relationship', relationshipInstance, source);
|
||||
}
|
||||
this.node.transformEngine.enactTransforms(this, label, relationshipInstance, source);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ const CEParser = require('./CEParser.js');
|
||||
const QuestionParser = require('./QuestionParser.js');
|
||||
const NLParser = require('./NLParser.js');
|
||||
const RuleEngine = require('./RuleEngine.js');
|
||||
const TransformEngine = require('./TransformEngine.js');
|
||||
|
||||
class CENode {
|
||||
|
||||
@ -248,6 +249,7 @@ class CENode {
|
||||
this.questionParser = new QuestionParser(this);
|
||||
this.nlParser = new NLParser(this);
|
||||
this.ruleEngine = new RuleEngine(this);
|
||||
this.transformEngine = new TransformEngine(this);
|
||||
this.concepts = [];
|
||||
this.instances = [];
|
||||
this.conceptDict = {};
|
||||
|
@ -151,7 +151,7 @@ class CEParser {
|
||||
instance.sentences.push(t);
|
||||
|
||||
const remainder = t.replace(/^there is an? (?:[a-zA-Z0-9 ]*) named (?:[a-zA-Z0-9_]*|'[a-zA-Z0-9_ ]*') that/, '');
|
||||
const facts = remainder.replace(/\band\b/g, '+').match(/(?:'(?:\\.|[^'])*'|[^+])+/g);
|
||||
const facts = remainder.replace(/\band\b/g, '!+').match(/(?:'(?:\\.|[^'])*'|[^!+])+/g);
|
||||
for (const fact of facts) {
|
||||
this.processInstanceFact(instance, fact, source);
|
||||
}
|
||||
@ -189,7 +189,7 @@ class CEParser {
|
||||
const tokens = t.split(' ');
|
||||
tokens.splice(0, 1 + concept.name.split(' ').length + instanceName.split(' ').length);
|
||||
const remainder = tokens.join(' ');
|
||||
const facts = remainder.replace(/\band\b/g, '+').match(/(?:'(?:\\.|[^'])*'|[^+])+/g);
|
||||
const facts = remainder.replace(/\band\b/g, '!+').match(/(?:'(?:\\.|[^'])*'|[^!+])+/g);
|
||||
if (facts) {
|
||||
for (const fact of facts) {
|
||||
this.processInstanceFact(instance, fact, source);
|
||||
@ -199,7 +199,7 @@ class CEParser {
|
||||
}
|
||||
|
||||
processInstanceFact(instance, fact, source) {
|
||||
const input = fact.trim().replace(/\+/g, 'and');
|
||||
const input = fact.trim().replace(/!\+/g, 'and');
|
||||
if (input.match(/^(?!has)([a-zA-Z0-9 ]*) the ([a-zA-Z0-9 ]*) ([a-zA-Z0-9_' ]*)/)) {
|
||||
const re = /^(?!has)([a-zA-Z0-9 ]*) the ([a-zA-Z0-9 ]*) ([a-zA-Z0-9_' ]*)/;
|
||||
const match = re.exec(input);
|
||||
|
@ -213,7 +213,17 @@ class PolicyHandler {
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
'transform policy': (policy) => {
|
||||
if (policy.transform_type && policy.uses){
|
||||
for (const instance of this.node.getInstances(policy.transform_type)) {
|
||||
if (!instance.uses) {
|
||||
instance.addRelationship('uses', policy.uses, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle(policy) {
|
||||
|
95
src/TransformEngine.js
Normal file
95
src/TransformEngine.js
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2017 W.M. Webberley & A.D. Preece (Cardiff University)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
class TransformEngine {
|
||||
|
||||
enable () {
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
disable () {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
evaluate (instance, func) {
|
||||
if (!this.enabled) {
|
||||
return 'Transforms disabled';
|
||||
}
|
||||
if (func.indexOf('require') > -1){
|
||||
return 'Invalid function';
|
||||
}
|
||||
|
||||
try {
|
||||
const sandbox = {
|
||||
name: instance.name,
|
||||
type: instance.concept.name
|
||||
};
|
||||
for (const attr of instance.values.concat(instance.relationships)) {
|
||||
const value = attr.instance.name ? attr.instance.name : attr.instance;
|
||||
sandbox[attr.label.toLowerCase().replace(/ /g, '_').replace(/'/g, '')] = value;
|
||||
}
|
||||
if (typeof window !== 'undefined' && window.document) {
|
||||
return function () {
|
||||
return eval(func);
|
||||
}.call(sandbox);
|
||||
}
|
||||
else {
|
||||
const vm = require('vm');
|
||||
const script = new vm.Script(func);
|
||||
const context = new vm.createContext(sandbox);
|
||||
return script.runInContext(context, {timeout: 30});
|
||||
}
|
||||
}
|
||||
catch (err){
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
enactTransforms(instance, label, targetInstance, source) {
|
||||
let doTransform = false;
|
||||
let transforms = [];
|
||||
|
||||
// Find out if the target property is a transform
|
||||
if (targetInstance.concept && targetInstance.concept.name === 'transform'){
|
||||
transforms = [targetInstance];
|
||||
}
|
||||
|
||||
// Otherwise check if the 'label' is a dependency of any existing transforms on this instance
|
||||
else {
|
||||
const newLabel = 'this.' + label.toLowerCase().replace(/ /g, '_').replace(/'/g, '');
|
||||
for (const rel of instance.relationships){
|
||||
if (rel.instance.concept && rel.instance.concept.name === 'transform' && rel.instance.transform_function && rel.instance.transform_function.indexOf(newLabel) > -1){
|
||||
transforms.push(rel.instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const transform of transforms) {
|
||||
const e = this.evaluate(instance, transform.transform_function);
|
||||
if (e){
|
||||
instance.addValue(transform.output, e.toString(), true, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(node) {
|
||||
this.node = node;
|
||||
this.enabled = false;
|
||||
}
|
||||
}
|
||||
module.exports = TransformEngine;
|
@ -150,6 +150,38 @@ describe('CEParser', function() {
|
||||
expect(node.instances.betty_hughes.lives_in.name).to.be('Camden');
|
||||
expect(node.instances.sally.lives_in.name).to.be('Kensington and Chelsea');
|
||||
});
|
||||
|
||||
it('allow for transforms', () => {
|
||||
node = new CENode(CEModels.core);
|
||||
node.addCE('conceptualise a ~ person ~ P that is a transformer and has the value A as ~ first name ~ and has the value B as ~ last name ~ and has the value C as ~ full name ~');
|
||||
node.addCE('there is a transform named t1 that has \'full name\' as output and has \'this.first_name + " " + this.last_name\' as transform function');
|
||||
node.addCE('there is a person named p1 that has Jane as first name and has Smith as last name and uses the transform t1');
|
||||
expect(node.instances.p1.full_name).to.be('Jane Smith');
|
||||
node.addCE('the person p1 has Harry as first name');
|
||||
expect(node.instances.p1.first_name).to.be('Harry');
|
||||
expect(node.instances.p1.full_name).to.be('Harry Smith');
|
||||
|
||||
node.addCE('there is a transform named t2 that has date as output and has \'new Date(parseInt(this.name))\' as transform function');
|
||||
node.addCE('there is a timestamp named \'715263762315\' that uses the transform t2');
|
||||
expect(node.getInstanceByName('715263762315').date).to.contain('1992');
|
||||
});
|
||||
|
||||
it('allow for transform chaining', () => {
|
||||
const node = new CENode(CEModels.core);
|
||||
node.addSentence("conceptualise a ~ person ~ P that is a transformer and has the value A as ~ first name ~ and has the value B as ~ last name ~ and has the value C as ~ full name ~ and has the value D as ~ username ~")
|
||||
|
||||
node.addSentence("there is a transform named t1 that has 'full name' as output and has 'this.first_name + \\' \\' + this.last_name' as transform function")
|
||||
node.addSentence("there is a transform named t2 that has 'username' as output and has 'this.full_name.toLowerCase().replace(/ /g, \\'_\\')' as transform function");
|
||||
|
||||
node.addSentence("there is a person named p1 that has 'Jane' as first name and has 'Smith' as last name and uses the transform t1 and uses the transform t2");
|
||||
|
||||
expect(node.instances.p1.full_name).to.be('Jane Smith');
|
||||
expect(node.instances.p1.username).to.be('jane_smith');
|
||||
|
||||
node.addSentence('the person p1 has \'Harry\' as first name');
|
||||
expect(node.instances.p1.full_name).to.be('Harry Smith');
|
||||
expect(node.instances.p1.username).to.be('harry_smith');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Specific Examples', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user