StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
195 lines
5.0 KiB
195 lines
5.0 KiB
4 years ago
|
"use strict"
|
||
|
|
||
|
var esprima = require("esprima")
|
||
|
var uniq = require("uniq")
|
||
|
|
||
|
var PREFIX_COUNTER = 0
|
||
|
|
||
|
function CompiledArgument(name, lvalue, rvalue) {
|
||
|
this.name = name
|
||
|
this.lvalue = lvalue
|
||
|
this.rvalue = rvalue
|
||
|
this.count = 0
|
||
|
}
|
||
|
|
||
|
function CompiledRoutine(body, args, thisVars, localVars) {
|
||
|
this.body = body
|
||
|
this.args = args
|
||
|
this.thisVars = thisVars
|
||
|
this.localVars = localVars
|
||
|
}
|
||
|
|
||
|
function isGlobal(identifier) {
|
||
|
if(identifier === "eval") {
|
||
|
throw new Error("cwise-parser: eval() not allowed")
|
||
|
}
|
||
|
if(typeof window !== "undefined") {
|
||
|
return identifier in window
|
||
|
} else if(typeof global !== "undefined") {
|
||
|
return identifier in global
|
||
|
} else if(typeof self !== "undefined") {
|
||
|
return identifier in self
|
||
|
} else {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getArgNames(ast) {
|
||
|
var params = ast.body[0].expression.callee.params
|
||
|
var names = new Array(params.length)
|
||
|
for(var i=0; i<params.length; ++i) {
|
||
|
names[i] = params[i].name
|
||
|
}
|
||
|
return names
|
||
|
}
|
||
|
|
||
|
function preprocess(func) {
|
||
|
var src = ["(", func, ")()"].join("")
|
||
|
var ast = esprima.parse(src, { range: true })
|
||
|
|
||
|
//Compute new prefix
|
||
|
var prefix = "_inline_" + (PREFIX_COUNTER++) + "_"
|
||
|
|
||
|
//Parse out arguments
|
||
|
var argNames = getArgNames(ast)
|
||
|
var compiledArgs = new Array(argNames.length)
|
||
|
for(var i=0; i<argNames.length; ++i) {
|
||
|
compiledArgs[i] = new CompiledArgument([prefix, "arg", i, "_"].join(""), false, false)
|
||
|
}
|
||
|
|
||
|
//Create temporary data structure for source rewriting
|
||
|
var exploded = new Array(src.length)
|
||
|
for(var i=0, n=src.length; i<n; ++i) {
|
||
|
exploded[i] = src.charAt(i)
|
||
|
}
|
||
|
|
||
|
//Local variables
|
||
|
var localVars = []
|
||
|
var thisVars = []
|
||
|
var computedThis = false
|
||
|
|
||
|
//Retrieves a local variable
|
||
|
function createLocal(id) {
|
||
|
var nstr = prefix + id.replace(/\_/g, "__")
|
||
|
localVars.push(nstr)
|
||
|
return nstr
|
||
|
}
|
||
|
|
||
|
//Creates a this variable
|
||
|
function createThisVar(id) {
|
||
|
var nstr = "this_" + id.replace(/\_/g, "__")
|
||
|
thisVars.push(nstr)
|
||
|
return nstr
|
||
|
}
|
||
|
|
||
|
//Rewrites an ast node
|
||
|
function rewrite(node, nstr) {
|
||
|
var lo = node.range[0], hi = node.range[1]
|
||
|
for(var i=lo+1; i<hi; ++i) {
|
||
|
exploded[i] = ""
|
||
|
}
|
||
|
exploded[lo] = nstr
|
||
|
}
|
||
|
|
||
|
//Remove any underscores
|
||
|
function escapeString(str) {
|
||
|
return "'"+(str.replace(/\_/g, "\\_").replace(/\'/g, "\'"))+"'"
|
||
|
}
|
||
|
|
||
|
//Returns the source of an identifier
|
||
|
function source(node) {
|
||
|
return exploded.slice(node.range[0], node.range[1]).join("")
|
||
|
}
|
||
|
|
||
|
//Computes the usage of a node
|
||
|
var LVALUE = 1
|
||
|
var RVALUE = 2
|
||
|
function getUsage(node) {
|
||
|
if(node.parent.type === "AssignmentExpression") {
|
||
|
if(node.parent.left === node) {
|
||
|
if(node.parent.operator === "=") {
|
||
|
return LVALUE
|
||
|
}
|
||
|
return LVALUE|RVALUE
|
||
|
}
|
||
|
}
|
||
|
if(node.parent.type === "UpdateExpression") {
|
||
|
return LVALUE|RVALUE
|
||
|
}
|
||
|
return RVALUE
|
||
|
}
|
||
|
|
||
|
//Handle visiting a node
|
||
|
(function visit(node, parent) {
|
||
|
node.parent = parent
|
||
|
if(node.type === "MemberExpression") {
|
||
|
//Handle member expression
|
||
|
if(node.computed) {
|
||
|
visit(node.object, node)
|
||
|
visit(node.property, node)
|
||
|
} else if(node.object.type === "ThisExpression") {
|
||
|
rewrite(node, createThisVar(node.property.name))
|
||
|
} else {
|
||
|
visit(node.object, node)
|
||
|
}
|
||
|
} else if(node.type === "ThisExpression") {
|
||
|
throw new Error("cwise-parser: Computed this is not allowed")
|
||
|
} else if(node.type === "Identifier") {
|
||
|
//Handle identifier
|
||
|
var name = node.name
|
||
|
var argNo = argNames.indexOf(name)
|
||
|
if(argNo >= 0) {
|
||
|
var carg = compiledArgs[argNo]
|
||
|
var usage = getUsage(node)
|
||
|
if(usage & LVALUE) {
|
||
|
carg.lvalue = true
|
||
|
}
|
||
|
if(usage & RVALUE) {
|
||
|
carg.rvalue = true
|
||
|
}
|
||
|
++carg.count
|
||
|
rewrite(node, carg.name)
|
||
|
} else if(isGlobal(name)) {
|
||
|
//Don't rewrite globals
|
||
|
} else {
|
||
|
rewrite(node, createLocal(name))
|
||
|
}
|
||
|
} else if(node.type === "Literal") {
|
||
|
if(typeof node.value === "string") {
|
||
|
rewrite(node, escapeString(node.value))
|
||
|
}
|
||
|
} else if(node.type === "WithStatement") {
|
||
|
throw new Error("cwise-parser: with() statements not allowed")
|
||
|
} else {
|
||
|
//Visit all children
|
||
|
var keys = Object.keys(node)
|
||
|
for(var i=0, n=keys.length; i<n; ++i) {
|
||
|
if(keys[i] === "parent") {
|
||
|
continue
|
||
|
}
|
||
|
var value = node[keys[i]]
|
||
|
if(value) {
|
||
|
if(value instanceof Array) {
|
||
|
for(var j=0; j<value.length; ++j) {
|
||
|
if(value[j] && typeof value[j].type === "string") {
|
||
|
visit(value[j], node)
|
||
|
}
|
||
|
}
|
||
|
} else if(typeof value.type === "string") {
|
||
|
visit(value, node)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
})(ast.body[0].expression.callee.body, undefined)
|
||
|
|
||
|
//Remove duplicate variables
|
||
|
uniq(localVars)
|
||
|
uniq(thisVars)
|
||
|
|
||
|
//Return body
|
||
|
var routine = new CompiledRoutine(source(ast.body[0].expression.callee.body), compiledArgs, thisVars, localVars)
|
||
|
return routine
|
||
|
}
|
||
|
|
||
|
module.exports = preprocess
|