securityos/node_modules/regl/lib/util/codegen.js

183 lines
3.9 KiB
JavaScript

var extend = require('./extend')
function slice (x) {
return Array.prototype.slice.call(x)
}
function join (x) {
return slice(x).join('')
}
module.exports = function createEnvironment () {
// Unique variable id counter
var varCounter = 0
// Linked values are passed from this scope into the generated code block
// Calling link() passes a value into the generated scope and returns
// the variable name which it is bound to
var linkedNames = []
var linkedValues = []
function link (value) {
for (var i = 0; i < linkedValues.length; ++i) {
if (linkedValues[i] === value) {
return linkedNames[i]
}
}
var name = 'g' + (varCounter++)
linkedNames.push(name)
linkedValues.push(value)
return name
}
// create a code block
function block () {
var code = []
function push () {
code.push.apply(code, slice(arguments))
}
var vars = []
function def () {
var name = 'v' + (varCounter++)
vars.push(name)
if (arguments.length > 0) {
code.push(name, '=')
code.push.apply(code, slice(arguments))
code.push(';')
}
return name
}
return extend(push, {
def: def,
toString: function () {
return join([
(vars.length > 0 ? 'var ' + vars.join(',') + ';' : ''),
join(code)
])
}
})
}
function scope () {
var entry = block()
var exit = block()
var entryToString = entry.toString
var exitToString = exit.toString
function save (object, prop) {
exit(object, prop, '=', entry.def(object, prop), ';')
}
return extend(function () {
entry.apply(entry, slice(arguments))
}, {
def: entry.def,
entry: entry,
exit: exit,
save: save,
set: function (object, prop, value) {
save(object, prop)
entry(object, prop, '=', value, ';')
},
toString: function () {
return entryToString() + exitToString()
}
})
}
function conditional () {
var pred = join(arguments)
var thenBlock = scope()
var elseBlock = scope()
var thenToString = thenBlock.toString
var elseToString = elseBlock.toString
return extend(thenBlock, {
then: function () {
thenBlock.apply(thenBlock, slice(arguments))
return this
},
else: function () {
elseBlock.apply(elseBlock, slice(arguments))
return this
},
toString: function () {
var elseClause = elseToString()
if (elseClause) {
elseClause = 'else{' + elseClause + '}'
}
return join([
'if(', pred, '){',
thenToString(),
'}', elseClause
])
}
})
}
// procedure list
var globalBlock = block()
var procedures = {}
function proc (name, count) {
var args = []
function arg () {
var name = 'a' + args.length
args.push(name)
return name
}
count = count || 0
for (var i = 0; i < count; ++i) {
arg()
}
var body = scope()
var bodyToString = body.toString
var result = procedures[name] = extend(body, {
arg: arg,
toString: function () {
return join([
'function(', args.join(), '){',
bodyToString(),
'}'
])
}
})
return result
}
function compile () {
var code = ['"use strict";',
globalBlock,
'return {']
Object.keys(procedures).forEach(function (name) {
code.push('"', name, '":', procedures[name].toString(), ',')
})
code.push('}')
var src = join(code)
.replace(/;/g, ';\n')
.replace(/}/g, '}\n')
.replace(/{/g, '{\n')
var proc = Function.apply(null, linkedNames.concat(src))
return proc.apply(null, linkedValues)
}
return {
global: globalBlock,
link: link,
block: block,
proc: proc,
scope: scope,
cond: conditional,
compile: compile
}
}