krl-compiler
Advanced tools
Comparing version 0.52.3 to 1.0.0-alpha.0
{ | ||
"name": "krl-compiler", | ||
"version": "0.52.3", | ||
"version": "1.0.0-alpha.0", | ||
"description": "KRL compiler", | ||
@@ -33,3 +33,3 @@ "main": "src/index.js", | ||
"devDependencies": { | ||
"ava": "^1.2.1", | ||
"ava": "^2.1.0", | ||
"contra": "^1.9.4", | ||
@@ -44,7 +44,10 @@ "diff-lines": "^1.1.0", | ||
"estree-loc": "^2.0.0", | ||
"krl-parser": "^0.52.3", | ||
"lodash": "^4.17.4", | ||
"minimist": "^1.2.0" | ||
"krl-parser": "^1.0.0-alpha.0", | ||
"krl-stdlib": "^1.0.0-alpha.0", | ||
"lodash": "^4.17.11", | ||
"minimist": "^1.2.5", | ||
"symbol-table": "^1.3.1", | ||
"to-js-identifier": "^1.0.0" | ||
}, | ||
"gitHead": "90020b0033b51d5bd3c3606466a92da3308fb6b7" | ||
"gitHead": "26aee8aeb73a2450f9224f2d8d81f3a3d49b33f2" | ||
} |
@@ -1,2 +0,2 @@ | ||
var _ = require('lodash') | ||
const _ = require('lodash') | ||
@@ -11,13 +11,41 @@ module.exports = function (ast, comp, e) { | ||
return e('acall', e('id', 'runAction'), [ | ||
e('id', 'ctx'), | ||
ast.action.domain | ||
? e('str', ast.action.domain, ast.action.loc) | ||
: e('void', e('number', 0)), | ||
e('str', ast.action.value), | ||
comp(ast.args), | ||
e('array', _.map(ast.setting, function (set) { | ||
return e('str', set.value, set.loc) | ||
})) | ||
let actionFn = comp(ast.action, { isGoingToBeApplied: true }) | ||
let actionType = actionFn.$$Annotation | ||
? actionFn.$$Annotation.type | ||
: 'Unknown' | ||
switch (actionType) { | ||
case 'Action': | ||
case 'ActionFunction': | ||
break// great! | ||
case 'Unknown': | ||
// runtime check the type | ||
actionFn = e('call', e('id', '$ctx.krl.assertAction'), [actionFn]) | ||
break// ok | ||
default: | ||
throw comp.error(ast.action.loc, 'Not an action: ' + actionType) | ||
} | ||
let estree = e('acall', actionFn, [ | ||
comp.scope.has('$$is_inside_fn_or_action_body$$') | ||
? e('this') | ||
: e('id', '$ctx'), | ||
comp(ast.args) | ||
]) | ||
if (_.size(ast.setting) === 1) { | ||
const id = ast.setting[0] | ||
comp.scope.set(id.value, { type: 'Unknown' }) | ||
return e('var', | ||
e('id', comp.jsId(id.value), id.loc), | ||
estree, | ||
id.loc | ||
) | ||
} else if (_.size(ast.setting) > 1) { | ||
throw comp.error(ast.setting[1].loc, 'Actions only return on value') | ||
} else { | ||
return e(';', estree, ast.loc) | ||
} | ||
} |
@@ -10,3 +10,3 @@ var _ = require('lodash') | ||
body.push(e('var', 'fired', condition)) | ||
body.push(e('var', '$fired', condition)) | ||
@@ -25,3 +25,3 @@ var ifBody = [] | ||
return e('case', e('string', action.label.value, action.label.loc), [ | ||
e(';', comp(action), action.loc), | ||
comp(action), | ||
e('break', action.loc) | ||
@@ -35,3 +35,3 @@ ], action.loc) | ||
return e('case', e('number', i, action.loc), [ | ||
e(';', comp(action), action.loc), | ||
comp(action), | ||
e('break', action.loc) | ||
@@ -41,5 +41,5 @@ ], action.loc) | ||
} else if (blockType === 'every') { | ||
ifBody = ifBody.concat(_.map(ast.actions, function (action) { | ||
return e(';', comp(action)) | ||
})) | ||
_.map(ast.actions, function (action) { | ||
ifBody.push(comp(action)) | ||
}) | ||
} else { | ||
@@ -49,5 +49,5 @@ throw new Error('ActionBlock.blockType = "' + blockType + '" not supported') | ||
body.push(e('if', e('id', 'fired'), e('block', ifBody))) | ||
body.push(e('if', e('id', '$fired'), e('block', ifBody))) | ||
return body | ||
} |
var _ = require('lodash') | ||
var callStdLibFn = require('../utils/callStdLibFn') | ||
module.exports = function (ast, comp, e) { | ||
if (ast.callee.type === 'DomainIdentifier' && | ||
ast.callee.domain === 'event' && | ||
ast.callee.value === 'attrs' | ||
) { | ||
throw comp.error(ast.callee.loc, '`event:attrs` is a Map, not a Function. Use `event:attrs{key}` instead') | ||
} | ||
let callee | ||
let args | ||
if (ast.callee.type === 'MemberExpression' && | ||
@@ -14,41 +22,53 @@ ast.callee.method === 'dot' && | ||
return e('acall', e('id', 'ctx.applyFn'), [ | ||
comp(ast.callee.property), | ||
e('id', 'ctx'), | ||
comp(_.assign({}, ast.args, { | ||
// inject left-hand of the dot as the first argument | ||
args: [ast.callee.object].concat(ast.args.args) | ||
})) | ||
]) | ||
} | ||
callee = comp(ast.callee.property) | ||
args = comp(_.assign({}, ast.args, { | ||
// inject left-hand of the dot as the first argument | ||
args: [ast.callee.object].concat(ast.args.args) | ||
})) | ||
if (ast.callee.type === 'DomainIdentifier' && | ||
ast.callee.domain === 'event' && | ||
ast.callee.value === 'attrs' | ||
) { | ||
comp.warn(ast.callee.loc, 'DEPRECATED change `event:attrs()` to `event:attrs`') | ||
return comp(ast.callee) | ||
// event:attrs("the_attr") | ||
if (ast.callee.object.type === 'DomainIdentifier' && | ||
ast.callee.object.domain === 'event' && | ||
ast.callee.object.value === 'attrs' && | ||
ast.callee.property.type === 'Identifier' && | ||
ast.callee.property.value === 'get' | ||
) { | ||
let arg0 = ast.args.args[0] | ||
if (arg0 && arg0.type === 'String') { | ||
comp.eventScope.addAttr(arg0.value) | ||
} | ||
} | ||
} else if (ast.callee.type === 'DomainIdentifier') { | ||
callee = comp(ast.callee, { isGoingToBeApplied: true }) | ||
args = comp(ast.args) | ||
if (ast.callee.domain === 'event' && ast.callee.value === 'attr') { | ||
let arg0 = ast.args.args[0] | ||
if (arg0 && arg0.type === 'String') { | ||
comp.eventScope.addAttr(arg0.value) | ||
} | ||
} | ||
} else { | ||
callee = comp(ast.callee) | ||
args = comp(ast.args) | ||
} | ||
if (ast.callee.type === 'DomainIdentifier' && | ||
ast.callee.domain === 'keys' | ||
) { | ||
var domainId = 'keys:' + ast.callee.value | ||
if (ast.args.args.length > 0) { | ||
comp.warn(ast.callee.loc, 'DEPRECATED change `' + domainId + '(name)` to `' + domainId + '{name}`') | ||
return callStdLibFn(e, 'get', [ | ||
comp(ast.callee), | ||
comp(ast.args.args[0]) | ||
], ast.loc) | ||
} else { | ||
comp.warn(ast.callee.loc, 'DEPRECATED change `' + domainId + '()` to `' + domainId + '`') | ||
return comp(ast.callee) | ||
} | ||
let calleeType = callee.$$Annotation | ||
? callee.$$Annotation.type | ||
: 'Unknown' | ||
switch (calleeType) { | ||
case 'Function': | ||
break// great! | ||
case 'Unknown': | ||
// runtime check the type | ||
callee = e('call', e('id', '$ctx.krl.assertFunction'), [callee]) | ||
break// ok | ||
default: | ||
throw comp.error(ast.callee.loc, 'Not a function: ' + calleeType) | ||
} | ||
return e('acall', e('id', 'ctx.applyFn'), [ | ||
comp(ast.callee), | ||
e('id', 'ctx'), | ||
comp(ast.args) | ||
return e('acall', callee, [ | ||
e('id', '$ctx'), | ||
args | ||
]) | ||
} |
module.exports = function (ast, comp, e) { | ||
if (ast.variable.domain !== 'ent') { | ||
throw comp.error(ast.loc, 'ClearPersistentVariable only works for `ent:`, not `' + ast.op + ':`') | ||
} | ||
var key = e('str', ast.variable.value, ast.variable.loc) | ||
if (ast.path_expression) { | ||
key = e('obj', { | ||
key: key, | ||
path: comp(ast.path_expression) | ||
}) | ||
// TODO optimize | ||
return e(';', e('acall', e('id', '$ctx.rsCtx.putEnt'), [ | ||
key, | ||
e('acall', e('id', '$stdlib.delete'), [ | ||
e('id', '$ctx'), | ||
e('array', [ | ||
e('acall', e('id', '$ctx.rsCtx.getEnt'), [key]), | ||
comp(ast.path_expression) | ||
]) | ||
]) | ||
])) | ||
} | ||
return e(';', e('acall', e('id', 'ctx.modules.del'), [ | ||
e('id', 'ctx'), | ||
e('str', ast.variable.domain, ast.variable.loc), | ||
key | ||
])) | ||
return e(';', e('acall', e('id', '$ctx.rsCtx.delEnt'), [key])) | ||
} |
@@ -1,14 +0,1 @@ | ||
var callStdLibFn = require('../utils/callStdLibFn') | ||
var ePathSet = function (ast, comp, e, path) { | ||
return e(';', e('call', e('id', 'ctx.scope.set'), [ | ||
e('str', ast.left.object.value, ast.left.loc), | ||
callStdLibFn(e, 'set', [ | ||
comp(ast.left.object), | ||
path, | ||
comp(ast.right) | ||
], ast.left.loc) | ||
])) | ||
} | ||
module.exports = function (ast, comp, e) { | ||
@@ -19,20 +6,24 @@ if (ast.op !== '=') { | ||
if (ast.left.type === 'MemberExpression') { | ||
// TODO This is actually an assignment, should we allow this? | ||
if (ast.left.method === 'path') { | ||
return ePathSet(ast, comp, e, comp(ast.left.property)) | ||
} else if (ast.left.method === 'index') { | ||
return ePathSet(ast, comp, e, e('array', [ | ||
comp(ast.left.property) | ||
], ast.left.property.loc)) | ||
} | ||
} else if (ast.left.type === 'Identifier') { | ||
if (ast.left.value === 'null') { | ||
throw comp.error(ast.loc, 'Cannot declare: ' + ast.left.value) | ||
} | ||
return e(';', e('call', e('id', 'ctx.scope.set'), [ | ||
e('str', ast.left.value, ast.left.loc), | ||
comp(ast.right) | ||
])) | ||
throw comp.error(ast.left.loc, 'This is an assignment, not a declaration') | ||
} | ||
throw comp.error(ast.loc, 'Cannot declare ' + ast.left.type) | ||
if (ast.left.type !== 'Identifier') { | ||
throw comp.error(ast.loc, 'Cannot declare ' + ast.left.type) | ||
} | ||
if (ast.left.value === 'null') { | ||
throw comp.error(ast.loc, 'Cannot declare: ' + ast.left.value) | ||
} | ||
if (ast.right.type === 'Function' || ast.right.type === 'Action') { | ||
// for recursion, declare the symbol first, then assign type if found | ||
comp.scope.set(ast.left.value, { type: 'Unknown' }) | ||
} | ||
const estree = comp(ast.right) | ||
comp.scope.set(ast.left.value, estree.$$Annotation | ||
? estree.$$Annotation | ||
: { type: 'Unknown' }) | ||
return e('const', | ||
e('id', comp.jsId(ast.left.value), ast.left.loc), | ||
estree | ||
) | ||
} |
var _ = require('lodash') | ||
var declarationBlock = require('../utils/declarationBlock') | ||
module.exports = function (ast, comp, e) { | ||
var body = comp(ast.params) | ||
var body = [] | ||
_.each(ast.body, function (d) { | ||
body.push(comp(d)) | ||
}) | ||
comp.scope.push() | ||
comp.scope.set('$$is_inside_fn_or_action_body$$', true) | ||
// compile the params first, so params get defined in comp.scope before the action body compiles | ||
const { params, defaultSetups } = comp(ast.params) | ||
body = body.concat(defaultSetups) | ||
body = body.concat(declarationBlock(ast.body, comp)) | ||
body = body.concat(comp(ast.action_block)) | ||
body.push(e('return', e('array', _.map(ast.returns, function (ret) { | ||
return comp(ret) | ||
})))) | ||
if (ast.return) { | ||
body.push(e('return', comp(ast.return))) | ||
} | ||
comp.scope.pop() | ||
var paramNames = [] | ||
var paramOrder = e('array', _.map(ast.params.params, function (p) { | ||
paramNames.push(p.id.value) | ||
return e('string', p.id.value, p.id.loc) | ||
}), ast.params.loc) | ||
return e('call', e('id', 'ctx.mkAction'), [ | ||
const estree = e('call', e('id', '$ctx.krl.Action'), [ | ||
paramOrder, | ||
e('asyncfn', [ | ||
'ctx', | ||
'args', | ||
'runAction' | ||
], body) | ||
{ | ||
type: 'FunctionExpression', | ||
params: params, | ||
body: { | ||
type: 'BlockStatement', | ||
body: body | ||
}, | ||
async: true | ||
} | ||
]) | ||
estree.$$Annotation = { | ||
type: 'Action', | ||
params: paramNames | ||
} | ||
return estree | ||
} |
@@ -1,7 +0,26 @@ | ||
module.exports = function (ast, comp, e) { | ||
return e('acall', e('id', 'ctx.modules.get'), [ | ||
e('id', 'ctx'), | ||
e('str', ast.domain), | ||
e('str', ast.value) | ||
]) | ||
module.exports = function (ast, comp, e, context) { | ||
if (ast.domain === 'ent') { | ||
return e('acall', e('id', '$ctx.rsCtx.getEnt'), [ | ||
e('str', ast.value) | ||
]) | ||
} | ||
if (ast.domain === 'event' && ast.value === 'attrs') { | ||
return e('id', '$event.data.attrs') | ||
} | ||
if (ast.domain === 'event' && ast.value === 'eci') { | ||
return e('id', '$event.eci') | ||
} | ||
if (ast.domain === 'event' && ast.value === 'attr') { | ||
comp.warn(ast.loc, 'event:attr(key) is deprecated. Use event:attrs{key} instead') | ||
} | ||
const mod = e('get', e('call', e('id', '$ctx.module'), [ | ||
e('str', ast.domain) | ||
]), e('str', ast.value)) | ||
if (context && context.isGoingToBeApplied) { | ||
return mod | ||
} | ||
return e('call', mod, [e('id', '$ctx')]) | ||
} |
module.exports = function (ast, comp, e) { | ||
return e( | ||
ast.level === 'error' ? 'return' : ';', | ||
e('acall', e('id', 'ctx.raiseError'), [ | ||
e('id', 'ctx'), | ||
e('string', ast.level), | ||
comp(ast.expression) | ||
const ruleName = comp.scope.get('$rule_name') | ||
const raise = e(';', e('acall', e('id', '$ctx.rsCtx.raiseEvent'), [ | ||
e('str', 'system'), | ||
e('str', 'error'), | ||
e('obj', { | ||
level: e('string', ast.level), | ||
data: comp(ast.expression), | ||
rid: e('id', '$ctx.rsCtx.ruleset.rid'), | ||
rule_name: typeof ruleName === 'string' | ||
? e('string', ruleName) | ||
: e('null'), | ||
genus: e('string', 'user') | ||
}), | ||
e('id', '$ctx.rsCtx.ruleset.rid') | ||
])) | ||
if (ast.level === 'error') { | ||
return e('block', [ | ||
e(';', e('call', e('id', '$last'), [])), | ||
e(';', e('call', e('id', '$ctx.rsCtx.clearSchedule'), [])), | ||
raise, | ||
e('return') | ||
]) | ||
) | ||
} | ||
return raise | ||
} |
@@ -10,30 +10,10 @@ var _ = require('lodash') | ||
comp.eventScope.add(ast.event_domain.value, ast.event_type.value) | ||
var fnBody = [] | ||
if (ast.where) { | ||
// inject attrs as varibles in the scope | ||
fnBody.push(e('var', 'event_attrs', e('acall', | ||
e('id', 'ctx.modules.get'), | ||
[e('id', 'ctx'), e('str', 'event'), e('str', 'attrs')] | ||
))) | ||
var attrKeys = e('call', e('id', 'Object.keys'), [e('id', 'event_attrs')]) | ||
fnBody.push(e(';', e('call', e('.', attrKeys, e('id', 'forEach')), [ | ||
e('fn', ['attr'], [ | ||
// don't stomp over global scope | ||
e('if', e('!', e('call', e('id', 'ctx.scope.has'), [e('id', 'attr')])), | ||
e(';', e('call', e('id', 'ctx.scope.set'), [ | ||
e('id', 'attr'), | ||
e('get', e('id', 'event_attrs'), e('id', 'attr')) | ||
])) | ||
) | ||
]) | ||
]))) | ||
} | ||
if (!_.isEmpty(ast.event_attrs)) { | ||
// select when domain type <attr> re#..# | ||
fnBody.push(e('var', 'matches', e('array', []))) | ||
fnBody.push(e('var', 'setting', e('obj', {}))) | ||
fnBody.push(e('var', 'm')) | ||
@@ -48,8 +28,17 @@ fnBody.push(e('var', 'j')) | ||
var key = e('string', a.key.value, a.key.loc) | ||
var attr = e('call', id('getAttrString'), [id('ctx', a.key.loc), key], a.key.loc) | ||
comp.eventScope.addAttr(a.key.value) | ||
var regexExec = e('.', comp(a.value), id('exec', a.value.loc), a.value.loc) | ||
fnBody.push(e(';', e('=', id('m'), e('call', regexExec, [attr], a.value.loc), a.value.loc))) | ||
fnBody.push(e(';', e('=', id('m'), e('call', regexExec, [ | ||
e('?', e('call', e('id', 'Object.prototype.hasOwnProperty.call'), [id('$event.data.attrs'), key]), e('call', e('id', '$stdlib.as', a.key.loc), [ | ||
e('id', '$ctx', a.key.loc), | ||
e('array', [ | ||
e('get', id('$event.data.attrs'), key, a.key.loc), | ||
e('str', 'String', a.key.loc) | ||
], a.key.loc) | ||
], a.key.loc), e('str', '', a.key.loc)) | ||
], a.value.loc), a.value.loc))) | ||
// if !m, then the EventExpression doesn't match | ||
fnBody.push(e('if', e('!', id('m')), e('return', e('false')))) | ||
fnBody.push(e('if', e('!', id('m')), e('return', e('obj', { match: e(false) })))) | ||
@@ -68,11 +57,19 @@ // append to matches | ||
_.each(ast.setting, function (s, i) { | ||
fnBody.push(e(';', | ||
e('call', e('id', 'setting', s.loc), [ | ||
e('str', s.value, s.loc), | ||
e('get', e('id', 'matches', s.loc), e('num', i, s.loc), s.loc) | ||
], s.loc), s.loc)) | ||
comp.scope.set(s.value, { type: 'String' }) | ||
comp.scope.get('$selectVars').push(s.value) | ||
fnBody.push( | ||
e('var', | ||
comp.jsId(s.value), | ||
e('=', | ||
e('get', e('id', 'setting', s.loc), e('str', s.value, s.loc), s.loc), | ||
e('get', e('id', 'matches', s.loc), e('num', i, s.loc), s.loc), | ||
s.loc | ||
), | ||
s.loc | ||
) | ||
) | ||
}) | ||
if (ast.where) { | ||
fnBody.push(e('if', e('!', comp(ast.where)), e('return', e('false')))) | ||
fnBody.push(e('if', e('!', comp(ast.where)), e('return', e('obj', { match: e(false) })))) | ||
} | ||
@@ -82,25 +79,48 @@ | ||
fnBody.push(e(';', | ||
e('acall', | ||
e('id', 'aggregateEvent', ast.aggregator.loc), | ||
[ | ||
e('id', 'ctx', ast.aggregator.loc), | ||
e('string', ast.aggregator.op, ast.aggregator.loc), | ||
e('array', _.map(ast.aggregator.args, function (a, i) { | ||
return e('array', [ | ||
e('string', a.value, a.loc), | ||
e('get', e('id', 'matches', a.loc), e('num', i, a.loc), a.loc) | ||
], a.loc) | ||
}), ast.aggregator.loc) | ||
], | ||
ast.aggregator.loc | ||
e('=', | ||
e('id', '$state', ast.aggregator.loc), | ||
e('acall', | ||
e('id', '$ctx.krl.aggregateEvent', ast.aggregator.loc), | ||
[ | ||
e('id', '$state', ast.aggregator.loc), | ||
e('string', ast.aggregator.op, ast.aggregator.loc), | ||
e('array', _.map(ast.aggregator.args, function (a, i) { | ||
comp.scope.set(a.value, { type: 'Unknown' }) | ||
comp.scope.get('$selectVars').push(a.value) | ||
return e('array', [ | ||
e('string', a.value, a.loc), | ||
e('get', e('id', 'matches', a.loc), e('num', i, a.loc), a.loc) | ||
], a.loc) | ||
}), ast.aggregator.loc) | ||
], | ||
ast.aggregator.loc | ||
), ast.aggregator.loc | ||
), ast.aggregator.loc)) | ||
} | ||
if (fnBody.length === 0) { | ||
return e(true) | ||
const ee = [ | ||
e('str', `${ast.event_domain.value}:${ast.event_type.value}`) | ||
] | ||
if (fnBody.length > 0) { | ||
fnBody.push(e('return', e('obj', { | ||
match: e(true), | ||
state: _.isEmpty(ast.event_attrs) | ||
? e('id', '$state') | ||
: e('call', e('id', 'Object.assign'), [ | ||
e('obj', {}), | ||
e('id', '$state'), | ||
e('obj', { | ||
setting: e('call', e('id', 'Object.assign'), [ | ||
e('obj', {}), | ||
e('||', e('id', '$state.setting'), e('obj', {})), | ||
e('id', 'setting') | ||
]) | ||
}) | ||
]) | ||
}))) | ||
ee.push(e('asyncfn', ['$event', '$state'], fnBody)) | ||
} | ||
fnBody.push(e('return', e(true))) | ||
return e('asyncfn', ['ctx', 'aggregateEvent', 'getAttrString', 'setting'], fnBody) | ||
return e('call', e('id', '$ctx.krl.SelectWhen.e'), ee) | ||
} |
@@ -23,5 +23,3 @@ var _ = require('lodash') | ||
} | ||
return e('asyncfn', ['ctx'], [ | ||
e('return', e('*', comp(ast.expression), e('num', multiplier))) | ||
]) | ||
return e('*', comp(ast.expression), e('num', multiplier)) | ||
} |
var _ = require('lodash') | ||
var declarationBlock = require('../utils/declarationBlock') | ||
module.exports = function (ast, comp, e) { | ||
var body = comp(ast.params) | ||
var body = [] | ||
_.each(ast.body, function (part, i) { | ||
return body.push(comp(part)) | ||
}) | ||
comp.scope.push() | ||
comp.scope.set('$$is_inside_fn_or_action_body$$', true) | ||
// compile the params first, so params get defined in comp.scope before the function body compiles | ||
const { params, defaultSetups } = comp(ast.params) | ||
body = body.concat(defaultSetups) | ||
body = body.concat(declarationBlock(ast.body, comp)) | ||
body.push(e('return', comp(ast.return))) | ||
comp.scope.pop() | ||
var paramNames = [] | ||
var paramOrder = e('array', _.map(ast.params.params, function (p) { | ||
paramNames.push(p.id.value) | ||
return e('string', p.id.value, p.id.loc) | ||
}), ast.params.loc) | ||
return e('call', e('id', 'ctx.mkFunction'), [ | ||
const estree = e('call', e('id', '$ctx.krl.Function'), [ | ||
paramOrder, | ||
e('asyncfn', ['ctx', 'args'], body) | ||
{ | ||
type: 'FunctionExpression', | ||
params: params, | ||
body: { | ||
type: 'BlockStatement', | ||
body: body | ||
}, | ||
async: true | ||
} | ||
]) | ||
estree.$$Annotation = { | ||
type: 'Function', | ||
params: paramNames | ||
} | ||
return estree | ||
} |
@@ -6,5 +6,5 @@ module.exports = function (ast, comp, e) { | ||
// if not inside a foreach, consider it true | ||
e('===', e('typeof', e('id', 'foreach_is_final')), e('string', 'undefined')), | ||
e('===', e('typeof', e('id', '$foreach_is_final')), e('string', 'undefined')), | ||
// inside a foreach `foreach_is_final` is defined | ||
e('id', 'foreach_is_final') | ||
e('id', '$foreach_is_final') | ||
), | ||
@@ -11,0 +11,0 @@ comp(ast.statement) |
@@ -0,3 +1,17 @@ | ||
const krlStdlib = require('krl-stdlib') | ||
module.exports = function (ast, comp, e, context) { | ||
return e('call', e('id', 'ctx.scope.get'), [e('str', ast.value)]) | ||
const id = ast.value | ||
if (krlStdlib.stdlib[id] && !comp.stdlibToInject[id] && comp.scope.getItsHeight(id) === 1) { | ||
comp.stdlibToInject[id] = ast | ||
} | ||
const estree = e('id', comp.jsId(id)) | ||
const scopeMeta = comp.scope.get(id) | ||
if (typeof scopeMeta.type === 'string' && scopeMeta.type !== 'Unknown') { | ||
estree.$$Annotation = scopeMeta | ||
} | ||
return estree | ||
} |
module.exports = function (ast, comp, e) { | ||
return e(';', e('call', e('id', 'ctx.stopRulesetExecution'), [e('id', 'ctx')])) | ||
return e('return', e('call', e('id', '$last'), [])) | ||
} |
module.exports = function (ast, comp, e) { | ||
return e(';', e('call', e('id', 'ctx.log'), [ | ||
e('string', ast.level), | ||
return e(';', e('call', e('id', '$ctx.log.' + ast.level), [ | ||
comp(ast.expression) | ||
])) | ||
} |
@@ -14,13 +14,28 @@ var callStdLibFn = require('../utils/callStdLibFn') | ||
if (ast.object.type === 'DomainIdentifier' && | ||
(ast.object.domain === 'ent' || ast.object.domain === 'app') | ||
(ast.object.domain === 'ent') | ||
) { | ||
return e('acall', e('id', 'ctx.modules.get'), [ | ||
e('id', 'ctx'), | ||
e('str', ast.object.domain), | ||
e('obj', { | ||
key: e('str', ast.object.value), | ||
path: comp(ast.property) | ||
}) | ||
], ast.loc) | ||
// TODO use optimized version | ||
// TODO return e('acall', e('id', '$ctx.rsCtx.getEnt'), [ | ||
// TODO e('str', ast.object.value), | ||
// TODO comp(ast.property) | ||
// TODO ]) | ||
return e('acall', e('id', '$stdlib.get'), [ | ||
e('id', '$ctx'), | ||
e('array', [ | ||
e('acall', e('id', '$ctx.rsCtx.getEnt'), [ | ||
e('str', ast.object.value) | ||
]), | ||
comp(ast.property) | ||
]) | ||
]) | ||
} | ||
if (ast.object.type === 'DomainIdentifier' && | ||
ast.object.domain === 'event' && | ||
ast.object.value === 'attrs' | ||
) { | ||
const val = ast.property | ||
if (val && val.type === 'String') { | ||
comp.eventScope.addAttr(val.value) | ||
} | ||
} | ||
return callStdLibFn(e, 'get', [ | ||
@@ -27,0 +42,0 @@ comp(ast.object), |
module.exports = function (ast, comp, e) { | ||
var mkIdStr = function () { | ||
return e('string', ast.id.value, ast.id.loc) | ||
} | ||
comp.scope.set(ast.id.value, { type: 'Unknown' }) | ||
var getArg = e('get', e('id', 'args'), mkIdStr()) | ||
let estree = e('id', comp.jsId(ast.id.value), ast.id.loc) | ||
var val = getArg | ||
if (ast['default']) { | ||
// only evaluate default if needed i.e. default may be calling an function | ||
val = e('?', | ||
e('call', e('id', 'args.hasOwnProperty'), [mkIdStr()]), | ||
getArg, | ||
comp(ast['default']) | ||
) | ||
const dflt = comp(ast['default']) | ||
if (dflt.$$Annotation) { | ||
comp.scope.set(ast.id.value, dflt.$$Annotation) | ||
} | ||
estree = { | ||
type: 'AssignmentPattern', | ||
left: estree, | ||
right: comp(ast['default']) | ||
} | ||
} | ||
return e(';', e('call', e('id', 'ctx.scope.set'), [ | ||
mkIdStr(), | ||
val | ||
])) | ||
return estree | ||
} |
@@ -1,7 +0,11 @@ | ||
var _ = require('lodash') | ||
const _ = require('lodash') | ||
module.exports = function (ast, comp, e) { | ||
var usedIds = {} | ||
var hasSeenDefault = false | ||
return _.map(ast.params, function (param) { | ||
const usedIds = {} | ||
let hasSeenDefault = false | ||
// javascript doesn't allow `await` in the defaults, so we need to do those in the function body | ||
const defaultSetups = [] | ||
const params = _.map(ast.params, function (param) { | ||
var id = param.id.value | ||
@@ -18,4 +22,27 @@ if (usedIds[id]) { | ||
} | ||
return comp(param) | ||
const estree = comp(param) | ||
if (estree.type === 'AssignmentPattern') { | ||
switch (estree.right.type) { | ||
case 'Literal': | ||
break | ||
default: | ||
const right = estree.right | ||
estree.right = e('id', '$default') | ||
defaultSetups.push(e('if', | ||
e('==', estree.left, e('id', '$default')), | ||
e('block', [ | ||
e(';', e('=', estree.left, right)) | ||
]) | ||
)) | ||
} | ||
} | ||
return estree | ||
}) | ||
return { | ||
params, | ||
defaultSetups | ||
} | ||
} |
@@ -5,4 +5,4 @@ module.exports = function (ast, comp, e) { | ||
} | ||
if (ast.left.type !== 'DomainIdentifier' || !/^(ent|app)$/.test(ast.left.domain)) { | ||
throw comp.error(ast.left.loc, 'PersistentVariableAssignment - only works on ent:* or app:* variables') | ||
if (ast.left.type !== 'DomainIdentifier' || !/^ent$/.test(ast.left.domain)) { | ||
throw comp.error(ast.left.loc, 'PersistentVariableAssignment - only works on ent:* variables') | ||
} | ||
@@ -26,8 +26,8 @@ | ||
// Use the optimized append | ||
return e(';', e('acall', e('id', 'ctx.modules.append'), [ | ||
e('id', 'ctx'), | ||
e('str', ast.left.domain, ast.left.loc), | ||
e('str', ast.left.value, ast.left.loc), | ||
e('array', comp(ast.right.args.args)) | ||
])) | ||
// TODO return e(';', e('acall', e('id', 'ctx.modules.append'), [ | ||
// TODO e('id', 'ctx'), | ||
// TODO e('str', ast.left.domain, ast.left.loc), | ||
// TODO e('str', ast.left.value, ast.left.loc), | ||
// TODO e('array', comp(ast.right.args.args)) | ||
// TODO ])) | ||
} | ||
@@ -42,11 +42,20 @@ } | ||
if (ast.path_expression) { | ||
key = e('obj', { | ||
key: key, | ||
path: comp(ast.path_expression) | ||
}) | ||
// TODO use optimized version | ||
// TODO key = e('obj', { | ||
// TODO key: key | ||
// TODO }) | ||
return e(';', e('acall', e('id', '$ctx.rsCtx.putEnt'), [ | ||
key, | ||
e('acall', e('id', '$stdlib.set'), [ | ||
e('id', '$ctx'), | ||
e('array', [ | ||
e('acall', e('id', '$ctx.rsCtx.getEnt'), [key]), | ||
comp(ast.path_expression), | ||
valueToStore | ||
]) | ||
]) | ||
])) | ||
} | ||
return e(';', e('acall', e('id', 'ctx.modules.set'), [ | ||
e('id', 'ctx'), | ||
e('str', ast.left.domain, ast.left.loc), | ||
return e(';', e('acall', e('id', '$ctx.rsCtx.putEnt'), [ | ||
key, | ||
@@ -53,0 +62,0 @@ valueToStore |
module.exports = function (ast, comp, e) { | ||
const args = {} | ||
// TODO ?? for_rid: ast.for_rid ? comp(ast.for_rid) : e('nil') | ||
const attrs = ast.event_attrs ? comp(ast.event_attrs) : e('nil') | ||
if (ast.event_domainAndType) { | ||
args.domainAndType = comp(ast.event_domainAndType) | ||
} else { | ||
args.domain = e('string', ast.event_domain.value, ast.event_domain.loc) | ||
args.type = comp(ast.event_type) | ||
// TODO | ||
return e('block', [ | ||
e( | ||
'let', | ||
e('id', '$parts'), | ||
e( | ||
'call', | ||
e( | ||
'.', | ||
e( | ||
'call', | ||
e( | ||
'.', | ||
e('call', e('id', '$ctx.krl.toString'), [ | ||
comp(ast.event_domainAndType) | ||
]), | ||
e('id', 'replace') | ||
), | ||
[ | ||
{ | ||
type: 'Literal', | ||
regex: { | ||
pattern: '\\s+', | ||
flags: 'g' | ||
} | ||
}, | ||
e('str', '') | ||
] | ||
), | ||
e('id', 'split') | ||
), | ||
[e('str', ':')] | ||
) | ||
), | ||
e( | ||
';', | ||
e('acall', e('id', '$ctx.rsCtx.raiseEvent'), [ | ||
e('get', e('id', '$parts'), e('num', 0)), | ||
e( | ||
'call', | ||
e( | ||
'.', | ||
e('call', e('.', e('id', '$parts'), e('id', 'slice')), [ | ||
e('num', 1) | ||
]), | ||
e('id', 'join') | ||
), | ||
[e('str', ':')] | ||
), | ||
attrs | ||
]) | ||
) | ||
]) | ||
} | ||
args.attributes = ast.event_attrs ? comp(ast.event_attrs) : e('nil') | ||
args.for_rid = ast.for_rid ? comp(ast.for_rid) : e('nil') | ||
return e(';', e('acall', e('id', 'ctx.raiseEvent'), [e('obj', args)])) | ||
return e( | ||
';', | ||
e('acall', e('id', '$ctx.rsCtx.raiseEvent'), [ | ||
e('string', ast.event_domain.value, ast.event_domain.loc), | ||
comp(ast.event_type), | ||
attrs | ||
]) | ||
) | ||
} |
var _ = require('lodash') | ||
var declarationBlock = require('../utils/declarationBlock') | ||
module.exports = function (ast, comp, e) { | ||
var rule = { | ||
name: e('string', ast.name.value, ast.name.loc) | ||
} | ||
function Rule (ast, comp, e) { | ||
// TODO use symbol-table to store ast.name.value | ||
if (ast.rule_state !== 'active') { | ||
rule.rule_state = e('string', ast.rule_state) | ||
comp.warn(ast.loc, 'rule ' + ast.name.value + ' is inactive, i.e. commented out') | ||
return e('obj', rule) | ||
return e(';', e('null')) | ||
} | ||
@@ -16,6 +13,35 @@ if (!ast.select) { | ||
} | ||
rule.select = comp(ast.select) | ||
var ruleBody = [] | ||
comp.scope.set('$selectVars', []) | ||
const selectWhenRule = comp(ast.select) | ||
var ruleBody = [ | ||
e(';', e('call', e('id', '$ctx.setCurrentRuleName'), [e('str', ast.name.value)])), | ||
e(';', e('call', e('id', '$ctx.log.debug'), [ | ||
e('str', 'rule selected'), | ||
e('obj', { rule_name: e('str', ast.name.value) }) | ||
])) | ||
] | ||
const selectVars = _.uniq(comp.scope.get('$selectVars')) | ||
_.each(selectVars, function (selectVar) { | ||
ruleBody.push(e('var', comp.jsId(selectVar), e('get', e('id', '$state.setting'), e('str', selectVar)))) | ||
}) | ||
if (_.size(selectVars) > 0) { | ||
ruleBody.push(e(';', e('=', e('id', 'this.rule.state'), e('call', e('id', 'Object.assign'), [ | ||
e('obj', {}), | ||
e('id', '$state'), | ||
e('obj', { | ||
setting: e('obj', {}) | ||
}) | ||
])))) | ||
} | ||
_.each(ast.foreach, function (foreach) { | ||
_.each(foreach.setting, function (set, i) { | ||
comp.scope.set(set.value, { type: 'Unknown' }) | ||
}) | ||
}) | ||
if (!_.isEmpty(ast.prelude)) { | ||
@@ -27,8 +53,12 @@ ruleBody = ruleBody.concat(declarationBlock(ast.prelude, comp)) | ||
} else { | ||
ruleBody.push(e('var', 'fired', e('true'))) | ||
ruleBody.push(e('var', '$fired', e('true'))) | ||
} | ||
ruleBody.push(e('if', e('id', 'fired'), | ||
e(';', e('call', e('id', 'ctx.emit'), [e('str', 'debug'), e('str', 'fired')])), | ||
e(';', e('call', e('id', 'ctx.emit'), [e('str', 'debug'), e('str', 'not fired')])) | ||
ruleBody.push(e('if', e('id', '$fired'), | ||
e(';', e('call', e('id', '$ctx.log.debug'), [ | ||
e('str', 'fired') | ||
])), | ||
e(';', e('call', e('id', '$ctx.log.debug'), [ | ||
e('str', 'not fired') | ||
])) | ||
)) | ||
@@ -56,5 +86,23 @@ | ||
rule.body = e('asyncfn', ['ctx', 'runAction', 'toPairs'], ruleBody) | ||
return e(';', e('call', e('id', '$rs.when'), [ | ||
selectWhenRule, | ||
e('asyncfn', ['$event', '$state', '$last'], [ | ||
{ | ||
type: 'TryStatement', | ||
block: e('block', | ||
ruleBody | ||
), | ||
finalizer: e('block', [ | ||
e(';', e('call', e('id', '$ctx.setCurrentRuleName'), [e('null')])) | ||
]) | ||
}]) | ||
])) | ||
} | ||
return e('obj', rule) | ||
module.exports = function (ast, comp, e) { | ||
comp.scope.push() | ||
comp.scope.set('$rule_name', ast.name.value) | ||
const estree = Rule(ast, comp, e) | ||
comp.scope.pop() | ||
return estree | ||
} |
@@ -1,9 +0,9 @@ | ||
var _ = require('lodash') | ||
const _ = require('lodash') | ||
var mkId = function (e, i, key) { | ||
return e('id', 'foreach' + i + '_' + key) | ||
function mkId (e, i, key) { | ||
return e('id', '$foreach' + i + '_' + key) | ||
} | ||
var mkIsFinal = function (e, nIndexes) { | ||
var mkEq = function (i) { | ||
function mkIsFinal (e, nIndexes) { | ||
function mkEq (i) { | ||
return e( | ||
@@ -15,4 +15,4 @@ '===', | ||
} | ||
var curr = mkEq(0) | ||
var i = 1 | ||
let curr = mkEq(0) | ||
let i = 1 | ||
while (i < nIndexes) { | ||
@@ -26,15 +26,15 @@ curr = e('&&', curr, mkEq(i)) | ||
module.exports = function (ast, comp, e, context) { | ||
var id = function (key) { | ||
function id (key) { | ||
return mkId(e, context.foreach_i, key) | ||
} | ||
var stmts = [] | ||
let stmts = [] | ||
var body = [] | ||
let body = [] | ||
if (context.foreach_n_left === 0) { | ||
// the last loop | ||
body.push(e('var', 'foreach_is_final', mkIsFinal(e, context.foreach_i + 1))) | ||
body.push(e('let', '$foreach_is_final', mkIsFinal(e, context.foreach_i + 1))) | ||
} | ||
_.each(ast.setting, function (set, i) { | ||
var val | ||
let val | ||
if (i === 0) { | ||
@@ -47,12 +47,10 @@ val = e('get', e('get', id('pairs'), id('i')), e('number', 1))// value | ||
} | ||
body.push(e(';', e('call', e('id', 'ctx.scope.set'), [ | ||
e('string', set.value, set.loc), | ||
val | ||
]))) | ||
comp.scope.set(set.value, { type: 'Unknown' }) | ||
body.push(e('let', comp.jsId(set.value), val, set.loc)) | ||
}) | ||
body = body.concat(context.foreach_body) | ||
stmts.push(e('var', id('pairs'), e('call', e('id', 'toPairs'), [comp(ast.expression)]))) | ||
stmts.push(e('var', id('len'), e('.', id('pairs'), e('id', 'length')))) | ||
stmts.push(e('var', id('i'))) | ||
stmts.push(e('let', id('pairs'), e('call', e('id', '$ctx.krl.toPairs'), [comp(ast.expression)]))) | ||
stmts.push(e('let', id('len'), e('.', id('pairs'), e('id', 'length')))) | ||
stmts.push(e('let', id('i'))) | ||
stmts.push(e('for', | ||
@@ -59,0 +57,0 @@ e('=', id('i'), e('number', 0)), |
module.exports = function (ast, comp, e) { | ||
var body = [] | ||
if (ast.fired) { | ||
body.push(e('if', e('id', 'fired'), e('block', comp(ast.fired)))) | ||
body.push(e('if', e('id', '$fired'), e('block', comp(ast.fired)))) | ||
} | ||
if (ast.notfired) { | ||
body.push(e('if', e('!', e('id', 'fired')), e('block', comp(ast.notfired)))) | ||
body.push(e('if', e('!', e('id', '$fired')), e('block', comp(ast.notfired)))) | ||
} | ||
@@ -9,0 +9,0 @@ if (ast.always) { |
@@ -1,471 +0,39 @@ | ||
var _ = require('lodash') | ||
module.exports = function (ast, comp, e) { | ||
if (ast.kind === 'when') { | ||
const selectWhenRule = comp(ast.event) | ||
var wrapInOr = function (states) { | ||
if (_.size(states) === 1) { | ||
return _.head(states) | ||
} | ||
return ['or', _.head(states), wrapInOr(_.tail(states))] | ||
} | ||
var permute = function (arr) { | ||
return arr.reduce(function permute (res, item, key, arr) { | ||
return res.concat(arr.length > 1 | ||
? arr | ||
.slice(0, key) | ||
.concat(arr.slice(key + 1)) | ||
.reduce(permute, []) | ||
.map(function (perm) { | ||
return [item].concat(perm) | ||
}) | ||
: item | ||
) | ||
}, []) | ||
} | ||
var StateMachine = function () { | ||
var start = _.uniqueId('state_') | ||
var end = _.uniqueId('state_') | ||
var transitions = [] | ||
var join = function (state1, state2) { | ||
_.each(transitions, function (t) { | ||
if (t[0] === state1) { | ||
t[0] = state2 | ||
} | ||
if (t[2] === state1) { | ||
t[2] = state2 | ||
} | ||
}) | ||
} | ||
return { | ||
start: start, | ||
end: end, | ||
add: function (fromState, onEvent, toState) { | ||
transitions.push([fromState, onEvent, toState]) | ||
}, | ||
getTransitions: function () { | ||
return transitions | ||
}, | ||
concat: function (other) { | ||
_.each(other.getTransitions(), function (t) { | ||
transitions.push(_.cloneDeep(t)) | ||
}) | ||
}, | ||
join: join, | ||
optimize: function () { | ||
// Find all cases where the same event goes to different states and join those states into one | ||
while (true) { | ||
let toJoin = [] | ||
let groupped = {} | ||
_.each(transitions, function (t) { | ||
var key = t[0] + JSON.stringify(t[1])// stringify b/c ["not","expr_1"] | ||
var state = t[2] | ||
if (_.has(groupped, key)) { | ||
if (state !== groupped[key]) { | ||
toJoin.push([state, groupped[key]]) | ||
} | ||
} else { | ||
groupped[key] = state | ||
} | ||
}) | ||
if (toJoin.length === 0) { | ||
break | ||
} | ||
toJoin.forEach(function (j) { | ||
join(j[0], j[1]) | ||
}) | ||
} | ||
// Remove duplicate transitions | ||
let tree = {} | ||
_.each(transitions, function (t) { | ||
_.set(tree, [JSON.stringify(t[1]), t[0], t[2]], true) | ||
}) | ||
transitions = [] | ||
_.each(tree, function (froms, onEvent) { | ||
_.each(froms, function (tos, fromState) { | ||
_.each(tos, function (bool, toState) { | ||
transitions.push([fromState, JSON.parse(onEvent), toState]) | ||
}) | ||
}) | ||
}) | ||
}, | ||
compile: function () { | ||
// we want to ensure we get the same output on every compile | ||
// that is why we are re-naming states and sorting the output | ||
var outStates = {} | ||
outStates[start] = 'start' | ||
outStates[end] = 'end' | ||
var i = 0 | ||
var toOutState = function (state) { | ||
if (_.has(outStates, state)) { | ||
return outStates[state] | ||
} | ||
outStates[state] = 's' + (i++) | ||
return outStates[state] | ||
} | ||
var outTransitions = _.sortBy(_.map(transitions, function (t) { | ||
return [toOutState(t[0]), t[1], toOutState(t[2])] | ||
}), function (t) { | ||
var score = 0 | ||
if (t[0] === 'start') { | ||
score -= Infinity | ||
} | ||
if (t[0] === 'end') { | ||
score += Infinity | ||
} | ||
if (/^s[0-9]+$/.test(t[0])) { | ||
score += _.parseInt(t[0].substring(1), 10) || 0 | ||
} | ||
return score | ||
}) | ||
var stm = {} | ||
_.each(outTransitions, function (t) { | ||
if (!_.has(stm, t[0])) { | ||
stm[t[0]] = [] | ||
} | ||
stm[t[0]].push([t[1], t[2]]) | ||
}) | ||
return stm | ||
if (ast.within) { | ||
return e('call', e('id', '$ctx.krl.SelectWhen.within'), [ | ||
comp(ast.within), | ||
selectWhenRule, | ||
e('fn', ['$event', '$state'], [ | ||
e('return', e('call', e('id', 'Object.assign'), [ | ||
e('obj', {}), | ||
e('id', '$state'), | ||
e('obj', { | ||
setting: e('obj', {}) | ||
}) | ||
])) | ||
]) | ||
]) | ||
} | ||
} | ||
} | ||
var toLispArgs = function (ast, traverse) { | ||
return _.map(ast.args, traverse) | ||
} | ||
return selectWhenRule | ||
} else if (ast.kind === 'where') { | ||
const expr = comp(ast.expression) | ||
var eventOps = { | ||
'before': { | ||
toLispArgs: toLispArgs, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var prev | ||
_.each(args, function (arg, j) { | ||
var a = evalEELisp(arg) | ||
s.concat(a) | ||
if (j === 0) { | ||
s.join(a.start, s.start) | ||
} | ||
if (j === _.size(args) - 1) { | ||
s.join(a.end, s.end) | ||
} | ||
if (prev) { | ||
s.join(prev.end, a.start) | ||
} | ||
prev = a | ||
}) | ||
return s | ||
} | ||
}, | ||
'after': { | ||
toLispArgs: toLispArgs, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var prev | ||
_.each(_.range(_.size(args) - 1, -1), function (i, j) { | ||
var a = evalEELisp(args[i]) | ||
s.concat(a) | ||
if (j === 0) { | ||
s.join(a.start, s.start) | ||
} | ||
if (j === _.size(args) - 1) { | ||
s.join(a.end, s.end) | ||
} | ||
if (prev) { | ||
s.join(prev.end, a.start) | ||
} | ||
prev = a | ||
}) | ||
return s | ||
} | ||
}, | ||
'then': { | ||
toLispArgs: toLispArgs, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var mergePoints = [] | ||
var prev | ||
_.each(args, function (arg, j) { | ||
var a = evalEELisp(arg) | ||
s.concat(a) | ||
if (j === 0) { | ||
s.join(a.start, s.start) | ||
} | ||
if (j === _.size(args) - 1) { | ||
s.join(a.end, s.end) | ||
} | ||
if (prev) { | ||
s.join(prev.end, a.start) | ||
mergePoints.push(a.start) | ||
} | ||
prev = a | ||
}) | ||
var transitions = s.getTransitions() | ||
_.each(mergePoints, function (daState) { | ||
// if not daState return to start | ||
var notB = wrapInOr(_.uniq(_.compact(_.map(transitions, function (t) { | ||
if (t[0] === daState) { | ||
return ['not', t[1]] | ||
} | ||
})))) | ||
s.add(daState, notB, s.start) | ||
}) | ||
return s | ||
} | ||
}, | ||
'and': { | ||
toLispArgs: toLispArgs, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
_.each(permute(_.range(0, _.size(args))), function (indices) { | ||
var prev | ||
_.each(indices, function (i, j) { | ||
var a = evalEELisp(args[i]) | ||
s.concat(a) | ||
if (j === 0) { | ||
s.join(a.start, s.start) | ||
} | ||
if (j === _.size(indices) - 1) { | ||
s.join(a.end, s.end) | ||
} | ||
if (prev) { | ||
s.join(prev.end, a.start) | ||
} | ||
prev = a | ||
}) | ||
}) | ||
return s | ||
} | ||
}, | ||
'or': { | ||
toLispArgs: toLispArgs, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
_.each(args, function (arg) { | ||
var a = evalEELisp(arg) | ||
s.concat(a) | ||
s.join(a.start, s.start) | ||
s.join(a.end, s.end) | ||
}) | ||
return s | ||
} | ||
}, | ||
'between': { | ||
toLispArgs: toLispArgs, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var a = evalEELisp(args[0]) | ||
var b = evalEELisp(args[1]) | ||
var c = evalEELisp(args[2]) | ||
s.concat(a) | ||
s.concat(b) | ||
s.concat(c) | ||
s.join(b.start, s.start) | ||
s.join(b.end, a.start) | ||
s.join(a.end, c.start) | ||
s.join(c.end, s.end) | ||
return s | ||
} | ||
}, | ||
'not between': { | ||
toLispArgs: toLispArgs, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var a = evalEELisp(args[0]) | ||
var b = evalEELisp(args[1]) | ||
var c = evalEELisp(args[2]) | ||
s.concat(a) | ||
s.concat(b) | ||
s.concat(c) | ||
// start:b -> c -> end | ||
s.join(b.start, s.start) | ||
s.join(b.end, c.start) | ||
s.join(c.end, s.end) | ||
// a -> start | ||
s.join(a.start, c.start) | ||
s.join(a.end, s.start) | ||
return s | ||
} | ||
}, | ||
'any': { | ||
toLispArgs: function (ast, traverse) { | ||
var num = _.head(ast.args) | ||
return [num.value].concat(_.map(_.tail(ast.args), traverse)) | ||
}, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var num = _.head(args) | ||
var eventexs = _.tail(args) | ||
var indicesGroups = _.uniqWith(_.map(permute(_.range(0, _.size(eventexs))), function (indices) { | ||
return _.take(indices, num) | ||
}), _.isEqual) | ||
_.each(indicesGroups, function (indices) { | ||
indices = _.take(indices, num) | ||
var prev | ||
_.each(indices, function (i, j) { | ||
var a = evalEELisp(eventexs[i]) | ||
s.concat(a) | ||
if (j === 0) { | ||
s.join(a.start, s.start) | ||
} | ||
if (j === _.size(indices) - 1) { | ||
s.join(a.end, s.end) | ||
} | ||
if (prev) { | ||
s.join(prev.end, a.start) | ||
} | ||
prev = a | ||
}) | ||
}) | ||
return s | ||
} | ||
}, | ||
'count': { | ||
toLispArgs: function (ast, traverse) { | ||
return [ast.n.value].concat(_.map([ast.event], traverse)) | ||
}, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var num = _.head(args) | ||
var eventex = _.head(_.tail(args)) | ||
var prev | ||
_.each(_.range(0, num), function (i, j) { | ||
var a = evalEELisp(eventex) | ||
s.concat(a) | ||
if (j === 0) { | ||
s.join(a.start, s.start) | ||
} | ||
if (j === num - 1) { | ||
s.join(a.end, s.end) | ||
} | ||
if (prev) { | ||
s.join(prev.end, a.start) | ||
} | ||
prev = a | ||
}) | ||
return s | ||
} | ||
}, | ||
'repeat': { | ||
toLispArgs: function (ast, traverse) { | ||
return [ast.n.value].concat(_.map([ast.event], traverse)) | ||
}, | ||
mkStateMachine: function (args, evalEELisp) { | ||
var s = StateMachine() | ||
var num = _.head(args) | ||
var eventex = _.head(_.tail(args)) | ||
var prev | ||
_.each(_.range(0, num), function (i, j) { | ||
var a = evalEELisp(eventex) | ||
s.concat(a) | ||
if (j === 0) { | ||
s.join(a.start, s.start) | ||
} | ||
if (j === num - 1) { | ||
s.join(a.end, s.end) | ||
} | ||
if (prev) { | ||
s.join(prev.end, a.start) | ||
} | ||
prev = a | ||
}) | ||
s.add(s.end, eventex, s.end) | ||
return s | ||
} | ||
return e('call', e('id', '$ctx.krl.SelectWhen.e'), | ||
[ | ||
e('str', '*'), | ||
e('asyncfn', ['$event', '$state'], [ | ||
e('return', e('obj', { | ||
match: expr, | ||
state: e('id', '$state') | ||
})) | ||
]) | ||
] | ||
) | ||
} | ||
} | ||
module.exports = function (ast, comp, e) { | ||
if (ast.kind !== 'when') { | ||
throw new Error('RuleSelect.kind not supported: ' + ast.kind) | ||
} | ||
var eeId = 0 | ||
var graph = {} | ||
var onEE = function (ast) { | ||
var domain = ast.event_domain.value | ||
var type = ast.event_type.value | ||
var ee = comp(ast) | ||
var id = 'expr_' + (eeId++) | ||
_.set(graph, [domain, type, id], ee) | ||
return id | ||
} | ||
var traverse = function (ast) { | ||
switch (ast.type) { | ||
case 'EventExpression': | ||
return onEE(ast) | ||
case 'EventOperator': | ||
case 'EventGroupOperator': | ||
if (!_.has(eventOps, ast.op)) { | ||
throw new Error(ast.type + '.op not supported: ' + ast.op) | ||
} | ||
return [ast.op].concat(eventOps[ast.op].toLispArgs(ast, traverse)) | ||
default: | ||
throw new Error('invalid event ast node: ' + ast.type) | ||
} | ||
} | ||
var evalEELisp = function (lisp) { | ||
var s | ||
if (_.isString(lisp)) { | ||
s = StateMachine() | ||
s.add(s.start, lisp, s.end) | ||
return s | ||
} | ||
if (_.has(eventOps, lisp[0])) { | ||
s = eventOps[lisp[0]].mkStateMachine(lisp.slice(1), evalEELisp) | ||
s.optimize() | ||
return s | ||
} else { | ||
throw new Error('EventOperator.op not supported: ' + ast.op) | ||
} | ||
} | ||
var lisp = traverse(ast.event) | ||
var stateMachine = evalEELisp(lisp) | ||
var r = { | ||
graph: e('obj', _.mapValues(graph, function (types, domain) { | ||
return e('obj', _.mapValues(types, function (exprs, type) { | ||
return e('obj', exprs) | ||
})) | ||
})), | ||
state_machine: e('json', stateMachine.compile()) | ||
} | ||
if (ast.within) { | ||
r.within = comp(ast.within) | ||
} | ||
return e('obj', r) | ||
throw new Error('RuleSelect.kind not supported: ' + ast.kind) | ||
} |
@@ -5,22 +5,175 @@ var _ = require('lodash') | ||
module.exports = function (ast, comp, e) { | ||
var rulesObj = {} | ||
_.each(ast.rules, function (rule) { | ||
if (_.has(rulesObj, rule.name.value)) { | ||
throw comp.error(rule.name.loc, 'Duplicate rule name: ' + rule.name.value) | ||
} | ||
rulesObj[rule.name.value] = comp(rule) | ||
}) | ||
var rs = { | ||
rid: comp(ast.rid) | ||
} | ||
if (ast.version) { | ||
comp.warn(ast.loc, 'ruleset{version ...} is deprecated. Use ruleset{meta{version ...}} instead') | ||
ast.meta.properties.push({ | ||
loc: ast.version.loc, | ||
type: 'RulesetMetaProperty', | ||
key: { loc: ast.version.loc, type: 'Keyword', value: 'version' }, | ||
value: ast.version | ||
}) | ||
} | ||
const shares = [] | ||
const provides = [] | ||
const esBodyModules = [] | ||
if (ast.meta) { | ||
rs.meta = comp(ast.meta) | ||
_.each(ast.meta.properties, function (prop) { | ||
if (prop.key.value === 'shares') { | ||
_.each(prop.value.ids, function (id) { | ||
shares.push(id) | ||
}) | ||
} else if (prop.key.value === 'provides') { | ||
_.each(prop.value.ids, function (id) { | ||
provides.push(id) | ||
}) | ||
} else if (prop.key.value === 'configure') { | ||
for (const dec of prop.value.declarations) { | ||
const estree = comp(dec.right) | ||
comp.scope.set(dec.left.value, estree.$$Annotation || { type: 'Unknown' }) | ||
esBodyModules.push(e('const', comp.jsId(dec.left.value), e('call', e('id', '$ctx.configure', dec.loc), [ | ||
e('str', dec.left.value, dec.left.loc), | ||
estree | ||
], dec.loc), dec.left.loc)) | ||
} | ||
} else if (prop.key.value === 'use') { | ||
const ast = prop.value | ||
if (ast.kind !== 'module') { | ||
throw comp.error(ast.loc, `use ${ast.kind} is not supported`) | ||
} | ||
const args = [ | ||
e('str', ast.rid.value, ast.rid.loc), | ||
ast.alias | ||
? e('str', ast.alias.value, ast.alias.loc) | ||
: e('null', ast.rid.loc) | ||
] | ||
if (ast['with']) { | ||
const withObj = {} | ||
for (const dec of ast['with']) { | ||
withObj[dec.left.value] = comp(dec.right) | ||
} | ||
args.push(e('obj', withObj, ast.loc)) | ||
} | ||
esBodyModules.push(e(';', e('acall', e('id', '$ctx.useModule', prop.loc), args, prop.loc), prop.loc)) | ||
} | ||
}) | ||
} | ||
if (!_.isEmpty(ast.global)) { | ||
rs.global = e('asyncfn', ['ctx'], declarationBlock(ast.global, comp)) | ||
const esBodyGlobal = declarationBlock(ast.global, comp) | ||
const esBodyRules = [] | ||
esBodyRules.push(e('const', '$rs', e('new', e('id', '$ctx.krl.SelectWhen.SelectWhen'), []))) | ||
const rulesObj = {} | ||
_.each(ast.rules, function (rule) { | ||
if (rulesObj[rule.name.value]) { | ||
throw comp.error(rule.name.loc, 'Duplicate rule name: ' + rule.name.value) | ||
} | ||
rulesObj[rule.name.value] = true | ||
esBodyRules.push(comp(rule)) | ||
}) | ||
const testingJSON = { | ||
queries: [], | ||
events: comp.eventScope.getTestingJSON() | ||
} | ||
rs.rules = e('obj', rulesObj) | ||
const queries = {} | ||
for (const share of shares) { | ||
const annotation = comp.scope.get(share.value) | ||
if (!annotation) { | ||
throw comp.error(share.loc, 'Trying to share: ' + share.value + ' but it\'s not defined in global') | ||
} | ||
if (annotation && annotation.type === 'Action') { | ||
throw comp.error(annotation.loc, 'Actions cannot be used queries: ' + share.value) | ||
} else { | ||
queries[share.value] = e('fn', ['query', 'qid'], [ | ||
e(';', e('call', e('id', '$ctx.setQuery'), [e('call', e('id', 'Object.assign'), [ | ||
e('obj', {}), | ||
e('id', 'query'), | ||
e('obj', { qid: e('id', 'qid') }) | ||
])])), | ||
{ | ||
type: 'TryStatement', | ||
block: e('block', [ | ||
annotation && annotation.type === 'Function' | ||
? e('return', e('call', e('id', comp.jsId(share.value)), [e('id', '$ctx'), e('id', 'query.args')])) | ||
: e('return', e('id', comp.jsId(share.value))) | ||
]), | ||
finalizer: e('block', [ | ||
e(';', e('call', e('id', '$ctx.setQuery'), [e('null')])) | ||
]) | ||
} | ||
]) | ||
testingJSON.queries.push({ | ||
name: share.value, | ||
args: annotation && annotation.type === 'Function' | ||
? annotation.params | ||
: [] | ||
}) | ||
} | ||
} | ||
queries['__testing'] = e('fn', [], [e('return', e('id', comp.jsId('__testing')))]) | ||
let esBody = [] | ||
esBody.push(e('const', '$default', e('call', e('id', 'Symbol'), [e('str', 'default')]))) | ||
esBody.push(e('const', '$ctx', e('call', e('id', '$mkCtx'), [e('id', '$rsCtx')]))) | ||
esBody.push(e('const', '$stdlib', e('call', e('id', '$ctx.module'), [e('str', 'stdlib')]))) | ||
_.each(comp.stdlibToInject, function (ast, id) { | ||
esBody.push(e('const', comp.jsId(id), e('get', e('id', '$stdlib', ast.loc), e('str', id, ast.loc), ast.loc), ast.loc)) | ||
}) | ||
esBody.push(e('const' | ||
, '__testing1' // NOTE not using comp.jsId b/c we want this var to be the root scope so it can be shadowed | ||
, e('json', testingJSON) | ||
)) | ||
esBody = esBody.concat(esBodyModules) | ||
esBody = esBody.concat(esBodyGlobal) | ||
esBody = esBody.concat(esBodyRules) | ||
const returnObj = { | ||
event: e('asyncfn', ['event', 'eid'], [ | ||
e(';', e('call', e('id', '$ctx.setEvent'), [e('call', e('id', 'Object.assign'), [ | ||
e('obj', {}), | ||
e('id', 'event'), | ||
e('obj', { eid: e('id', 'eid') }) | ||
])])), | ||
{ | ||
type: 'TryStatement', | ||
block: e('block', [ | ||
e(';', e('acall', e('id', '$rs.send'), [e('id', 'event')])) | ||
]), | ||
finalizer: e('block', [ | ||
e(';', e('call', e('id', '$ctx.setEvent'), [e('null')])) | ||
]) | ||
}, | ||
e('return', e('call', e('id', '$ctx.drainDirectives'), [])) | ||
]), | ||
query: e('obj', queries) | ||
} | ||
if (provides.length > 0) { | ||
const provideObj = {} | ||
for (const provide of provides) { | ||
const annotation = comp.scope.get(provide.value) | ||
if (!annotation) { | ||
throw comp.error(provide.loc, 'Trying to provide: ' + provide.value + ' but it\'s not defined in global') | ||
} | ||
provideObj[provide.value] = e('id', comp.jsId(provide.value)) | ||
} | ||
returnObj.provides = e('obj', provideObj) | ||
} | ||
esBody.push(e('return', e('obj', returnObj))) | ||
rs.init = e('asyncfn', ['$rsCtx', '$mkCtx'], esBody) | ||
return [ | ||
e(';', e('=', e('id', 'module.exports'), e('obj', rs))) | ||
e(';', e('=', e('id', 'module.exports'), | ||
e('obj', rs) | ||
)) | ||
] | ||
} |
var _ = require('lodash') | ||
var propTypes = { | ||
'version': function (props, comp, e) { | ||
if (_.size(props) > 1) { | ||
throw comp.error(props[1].loc, 'only 1 meta.version allowed') | ||
} | ||
return comp(_.head(props).value) | ||
}, | ||
'name': function (props, comp, e) { | ||
if (_.size(props) !== 1) { | ||
throw new Error('only 1 meta.name allowed') | ||
if (_.size(props) > 1) { | ||
throw comp.error(props[1].loc, 'only 1 meta.name allowed') | ||
} | ||
@@ -11,4 +17,4 @@ return comp(_.head(props).value) | ||
'description': function (props, comp, e) { | ||
if (_.size(props) !== 1) { | ||
throw new Error('only 1 meta.description allowed') | ||
if (_.size(props) > 1) { | ||
throw comp.error(props[1].loc, 'only 1 meta.description allowed') | ||
} | ||
@@ -18,4 +24,4 @@ return comp(_.head(props).value) | ||
'author': function (props, comp, e) { | ||
if (_.size(props) !== 1) { | ||
throw new Error('only 1 meta.author allowed') | ||
if (_.size(props) > 1) { | ||
throw comp.error(props[1].loc, 'only 1 meta.author allowed') | ||
} | ||
@@ -25,6 +31,8 @@ return comp(_.head(props).value) | ||
'logging': function (props, comp, e) { | ||
if (_.size(props) !== 1) { | ||
throw new Error('only 1 meta.logging allowed') | ||
if (_.size(props) > 1) { | ||
throw comp.error(props[1].loc, 'only 1 meta.logging allowed') | ||
} | ||
return comp(_.head(props).value) | ||
const prop = _.head(props) | ||
comp.warn(prop.loc, 'DEPRECATED meta.logging is no longer needed, logging is always on') | ||
return comp(prop.value) | ||
}, | ||
@@ -41,7 +49,6 @@ 'use': function (props, comp, e) { | ||
} | ||
if (ast.version) { | ||
obj.version = comp(ast.version) | ||
} | ||
if (ast['with']) { | ||
obj['with'] = e('asyncfn', ['ctx'], comp(ast['with']), ast['with'].loc) | ||
obj['with'] = e('arr', ast['with'].map(dec => { | ||
return e('str', dec.left.value, dec.left.loc) | ||
})) | ||
} | ||
@@ -52,7 +59,11 @@ return e('obj', obj, ast.loc) | ||
'configure': function (props, comp, e) { | ||
if (_.size(props) !== 1) { | ||
throw new Error('only 1 meta.configure allowed') | ||
const ids = [] | ||
for (const ast of props) { | ||
for (const dec of ast.value.declarations) { | ||
ids.push(dec.left) | ||
} | ||
} | ||
var ast = _.head(props) | ||
return e('asyncfn', ['ctx'], comp(ast.value.declarations), ast.value.loc) | ||
return e('arr', _.map(ids, function (id) { | ||
return e('str', id.value, id.loc) | ||
})) | ||
}, | ||
@@ -70,39 +81,2 @@ 'shares': function (props, comp, e) { | ||
})) | ||
}, | ||
'provides_keys': function (props, comp, e) { | ||
var json = {} | ||
_.each(props, function (p) { | ||
_.each(p.value.ids, function (idAst) { | ||
var id = idAst.value | ||
if (!_.has(json, id)) { | ||
json[id] = { to: [] } | ||
} | ||
_.each(p.value.rulesets, function (r) { | ||
json[id].to.push(r.value) | ||
}) | ||
}) | ||
}) | ||
return e('json', json) | ||
}, | ||
'keys': function (props, comp, e) { | ||
var obj = {} | ||
_.each(props, function (p) { | ||
switch (_.get(p, ['value', 1, 'type'])) { | ||
case 'String': | ||
break | ||
case 'Map': | ||
_.each(p.value[1].value, function (pair) { | ||
var vAstType = pair.value.type | ||
if (vAstType !== 'String') { | ||
throw new Error('A ruleset key that is Map, can only use Strings as values') | ||
} | ||
}) | ||
break | ||
default: | ||
throw new Error('Ruleset keys must be a String, or Map of Strings') | ||
} | ||
obj[p.value[0].value] = comp(p.value[1]) | ||
}) | ||
return e('obj', obj) | ||
} | ||
@@ -114,10 +88,10 @@ } | ||
if (p.type !== 'RulesetMetaProperty') { | ||
throw new Error('RulesetMeta.properties should all be RulesetMetaProperty ast nodes') | ||
throw comp.error(p.loc, 'RulesetMeta.properties should all be RulesetMetaProperty ast nodes') | ||
} | ||
if (p.key.type !== 'Keyword') { | ||
throw new Error('RulesetMetaProperty.key should a Keyword') | ||
throw comp.error(p.key.loc, 'RulesetMetaProperty.key should a Keyword') | ||
} | ||
if (_.has(p.value, 'operator')) { | ||
if (p.value.operator.type !== 'Keyword') { | ||
throw new Error('RulesetMetaProperty.operator should a Keyword') | ||
throw comp.error(p.value.operator.loc, 'RulesetMetaProperty.operator should a Keyword') | ||
} | ||
@@ -129,3 +103,3 @@ return p.key.value + '_' + p.value.operator.value | ||
if (!_.has(propTypes, key)) { | ||
throw new Error('RulesetMetaProperty not supported: ' + key) | ||
throw comp.error(props[0].loc, 'RulesetMetaProperty not supported: ' + key) | ||
} | ||
@@ -132,0 +106,0 @@ return propTypes[key](props, comp, e) |
@@ -1,6 +0,7 @@ | ||
var _ = require('lodash') | ||
const _ = require('lodash') | ||
module.exports = function (ast, comp, e) { | ||
var args = { | ||
attributes: ast.event_attrs ? comp(ast.event_attrs) : e('nil') | ||
const args = { | ||
eci: e('id', '$event.eci'), | ||
attrs: ast.event_attrs ? comp(ast.event_attrs) : e('nil') | ||
} | ||
@@ -12,18 +13,25 @@ | ||
args.domain = e('string', ast.event_domain.value, ast.event_domain.loc) | ||
args.type = comp(ast.event_type) | ||
args.name = comp(ast.event_type) | ||
} | ||
let addFunction | ||
if (_.has(ast, 'at')) { | ||
args.at = comp(ast.at) | ||
} | ||
if (_.has(ast, 'timespec')) { | ||
args.time = comp(ast.at) | ||
addFunction = 'at' | ||
} else if (_.has(ast, 'timespec')) { | ||
args.timespec = comp(ast.timespec) | ||
addFunction = 'repeat' | ||
} else { | ||
throw comp.error(ast.loc, 'error') | ||
} | ||
var moduleCall = e('acall', e('id', 'ctx.scheduleEvent'), [e('obj', args)]) | ||
var moduleCall = e('acall', e('get', e('call', e('id', '$ctx.module'), [e('str', 'schedule')]), e('str', addFunction)), [e('id', '$ctx'), e('obj', args)]) | ||
if (ast.setting) { | ||
return e(';', e('call', e('id', 'ctx.scope.set', ast.setting.loc), [ | ||
e('str', ast.setting.value, ast.setting.loc), | ||
moduleCall | ||
], ast.setting.loc)) | ||
comp.scope.set(ast.setting.value, { type: 'Unknown' }) | ||
return e('var', | ||
e('id', comp.jsId(ast.setting.value), ast.setting.loc), | ||
moduleCall, | ||
ast.setting.loc | ||
) | ||
} else { | ||
@@ -30,0 +38,0 @@ return e(';', moduleCall) |
var _ = require('lodash') | ||
var mkTree = require('estree-builder') | ||
var krlStdlib = require('krl-stdlib') | ||
var toJsIdentifier = require('to-js-identifier') | ||
var SymbolTableStack = require('symbol-table/stack') | ||
@@ -19,2 +22,4 @@ var compByType = { | ||
'EventExpression': require('./c/EventExpression'), | ||
'EventGroupOperator': require('./c/EventGroupOperator'), | ||
'EventOperator': require('./c/EventOperator'), | ||
'EventWithin': require('./c/EventWithin'), | ||
@@ -106,2 +111,45 @@ 'Function': require('./c/Function'), | ||
var scope = SymbolTableStack() | ||
// inject stdlib into the root scope | ||
Object.keys(krlStdlib.stdlib).forEach(id => { | ||
scope.set(id, { type: krlStdlib.krl.typeOf(krlStdlib.stdlib[id]) }) | ||
}) | ||
scope.set('__testing', { type: 'Map' })// defined in root scope so it can be shadowed in global | ||
scope.push()// new scope for user's KRL code | ||
var stdlibToInject = {} | ||
var eventScope = (function () { | ||
const map = {} | ||
return { | ||
add (domain, name) { | ||
const rname = scope.get('$rule_name') | ||
const path = [rname, domain, name] | ||
if (!_.has(map, path)) { | ||
_.set(map, path, { attrs: {} }) | ||
} | ||
}, | ||
addAttr (key) { | ||
const rname = scope.get('$rule_name') | ||
for (const domain of Object.keys(map[rname] || {})) { | ||
for (const name of Object.keys(map[rname][domain])) { | ||
_.set(map, [rname, domain, name, 'attrs', key], true) | ||
} | ||
} | ||
}, | ||
getTestingJSON () { | ||
const result = [] | ||
for (const rname of Object.keys(map)) { | ||
for (const domain of Object.keys(map[rname])) { | ||
for (const name of Object.keys(map[rname][domain])) { | ||
const e = map[rname][domain][name] | ||
const attrs = Object.keys(e.attrs) | ||
result.push({ domain, name, attrs }) | ||
} | ||
} | ||
} | ||
return result | ||
} | ||
} | ||
}()) | ||
var compile = function compile (ast, context) { | ||
@@ -125,2 +173,11 @@ if (_.isArray(ast)) { | ||
comp.warn = warn | ||
comp.scope = scope | ||
comp.eventScope = eventScope | ||
comp.stdlibToInject = stdlibToInject | ||
comp.jsId = function (id) { | ||
if (!comp.scope.has(id)) { | ||
throw comp.error(ast.loc, 'Undefined id: ' + id) | ||
} | ||
return toJsIdentifier(id) + scope.getItsHeight(id) | ||
} | ||
@@ -141,16 +198,17 @@ var estree | ||
var body = compile(ast) | ||
body = _.isArray(body) ? body : [body] | ||
body = body.map(function (estree) { | ||
if (!/Statement/.test(estree.type)) { | ||
return mkTree(';', estree, estree.loc) | ||
var rid | ||
if (ast && ast.type === 'Ruleset') { | ||
if (ast.rid && ast.rid.type === 'RulesetID') { | ||
rid = ast.rid.value | ||
} | ||
return estree | ||
}) | ||
} | ||
var estree = compile(ast) | ||
estree = _.isArray(estree) ? estree : [] | ||
return { | ||
body: body, | ||
warnings: warnings | ||
rid, | ||
estree, | ||
warnings | ||
} | ||
} |
@@ -22,3 +22,3 @@ var _ = require('lodash') | ||
'type': 'Program', | ||
'body': compiled.body | ||
'body': compiled.estree | ||
}, { | ||
@@ -38,2 +38,3 @@ format: { | ||
code: out.code, | ||
rid: compiled.rid, | ||
warnings: compiled.warnings | ||
@@ -40,0 +41,0 @@ } |
module.exports = function (e, domain, id, args, loc) { | ||
var moduleVal = e('acall', | ||
e('id', 'ctx.modules.get', loc), | ||
[ | ||
e('id', 'ctx', loc), | ||
e('str', domain, loc), | ||
e('str', id, loc) | ||
], | ||
var moduleVal = e('get', | ||
e('call', e('id', '$ctx.module', loc), [ | ||
e('str', domain, loc) | ||
], loc), | ||
e('str', id, loc), | ||
loc | ||
) | ||
return e('acall', e('id', 'ctx.applyFn'), [ | ||
moduleVal, | ||
e('id', 'ctx', loc), | ||
return e('acall', moduleVal, [ | ||
e('id', '$ctx', loc), | ||
args | ||
], loc) | ||
} |
var _ = require('lodash') | ||
module.exports = function (e, name, args, loc) { | ||
return e('acall', e('id', 'ctx.applyFn'), [ | ||
e('call', e('id', 'ctx.scope.get', loc), [e('string', name, loc)], loc), | ||
e('id', 'ctx'), | ||
return e('acall', e('get', e('id', '$stdlib'), e('str', name)), [ | ||
e('id', '$ctx'), | ||
_.isArray(args) | ||
@@ -8,0 +7,0 @@ ? e('array', args) |
var _ = require('lodash') | ||
module.exports = function (astList, comp) { | ||
var usedIDs = {} | ||
return _.map(astList, function (ast) { | ||
var id | ||
if (ast.type === 'Declaration') { | ||
if (ast.left.type === 'Identifier') { | ||
id = ast.left.value | ||
} | ||
} else { | ||
const declaredIds = [] | ||
const compiled = {} | ||
for (const ast of astList) { | ||
if (ast.type !== 'Declaration' || ast.left.type !== 'Identifier') { | ||
throw comp.error(ast.loc, 'Only declarations should be in this block') | ||
} | ||
if (id) { | ||
if (usedIDs[id]) { | ||
// TODO make this an error, but right now some code relies on this | ||
comp.warn(ast.loc, 'Duplicate declaration: ' + id) | ||
} | ||
usedIDs[id] = true | ||
const id = ast.left.value | ||
if (declaredIds.includes(id)) { | ||
throw comp.error(ast.loc, 'Duplicate declaration: ' + id) | ||
} | ||
return comp(ast) | ||
declaredIds.push(id) | ||
if (ast.right.type === 'Function' || ast.right.type === 'Action') { | ||
// don't compile yet, compile all expression declarations first | ||
comp.scope.set(ast.left.value, { type: 'Unknown' }) | ||
} else { | ||
compiled[id] = comp(ast) | ||
} | ||
} | ||
// second pass for functions/actions | ||
for (const ast of astList) { | ||
const id = ast.left.value | ||
if (!compiled[id]) { | ||
compiled[id] = comp(ast) | ||
} | ||
} | ||
return _.map(declaredIds, function (id) { | ||
return compiled[id] | ||
}) | ||
} |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
57350
52
1629
10
2
+ Addedkrl-stdlib@^1.0.0-alpha.0
+ Addedsymbol-table@^1.3.1
+ Addedto-js-identifier@^1.0.0
+ Addedabstract-leveldown@6.2.36.3.0(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbuffer@5.7.1(transitive)
+ Addedcharwise@3.0.1(transitive)
+ Addedcuid@2.1.8(transitive)
+ Addeddeferred-leveldown@5.3.0(transitive)
+ Addedencoding-down@6.3.0(transitive)
+ Addederrno@0.1.8(transitive)
+ Addedfunctional-red-black-tree@1.0.1(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedimmediate@3.2.33.3.0(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedis-typedarray@1.0.0(transitive)
+ Addedjson-log@3.0.1(transitive)
+ Addedkrl-parser@1.3.0(transitive)
+ Addedkrl-stdlib@1.3.0(transitive)
+ Addedlevel-codec@9.0.2(transitive)
+ Addedlevel-concat-iterator@2.0.1(transitive)
+ Addedlevel-errors@2.0.1(transitive)
+ Addedlevel-iterator-stream@4.0.2(transitive)
+ Addedlevel-json-coerce-null@1.0.1(transitive)
+ Addedlevel-supports@1.0.1(transitive)
+ Addedlevelup@4.4.0(transitive)
+ Addedltgt@2.2.1(transitive)
+ Addedmemdown@5.1.0(transitive)
+ Addedpico-framework@0.6.1(transitive)
+ Addedprr@1.0.1(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedreserved@0.1.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedselect-when@0.1.9(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedsymbol-table@1.3.2(transitive)
+ Addedto-js-identifier@1.0.0(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedxtend@4.0.2(transitive)
- Removedkrl-parser@0.52.3(transitive)
Updatedkrl-parser@^1.0.0-alpha.0
Updatedlodash@^4.17.11
Updatedminimist@^1.2.5