183 lines
3.9 KiB
JavaScript
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
|
|
}
|
|
}
|