Skip to content

Instantly share code, notes, and snippets.

@nhz-io
Created February 5, 2017 03:26
Show Gist options
  • Save nhz-io/a79ba3bb42793e2e32d81d0ea237dc9d to your computer and use it in GitHub Desktop.
Save nhz-io/a79ba3bb42793e2e32d81d0ea237dc9d to your computer and use it in GitHub Desktop.
AST Histograms created by nhz-io - https://repl.it/F71R/12
pre, code {
display: none;
}
body {
background: #EEE;
}
.container {
font-size: 0;
}
canvas {
padding: 2px;
margin: 2px;
border: solid rgba(0,0,0,0.2) 1px;
background: white;
}
.histogram {
display: inline-block;
vertical-align: top;
padding: 0;
margin: 0;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>AST Histograms</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/esprima/2.7.3/esprima.js"></script>
<script src="//rawgit.com/nhz-io/gen-utils/master/src/common/index.js"></script>
<script src="//rawgit.com/nhz-io/gen-utils/master/src/ast/index.js"></script>
<link href="index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container">
<div class="histogram">
<pre class="code">
function name() {
/* ... */
}
</pre>
</div>
<div class="histogram">
<pre class="code">
function name(arg) {
/* ... */
}
</pre>
</div>
<div class="histogram">
<pre class="code">
function name(arg1, arg2) {
/* ... */
}
</pre>
</div>
<div class="histogram">
<pre class="code">
function name(...args) {
/* ... */
}
</pre>
</div>
<div class="histogram">
<pre class="code">
function name(arg1, arg2, ...args) {
/* ... */
}
</pre>
</div>
<div class="histogram">
<pre class="code">
(function () {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function (arg) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function (arg1, arg2) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function (...args) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function (arg1, arg2, ...args) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function name() {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function name(arg) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function name(arg1, arg2) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function name(...args) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(function name(arg1, arg2, ...args) {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(() => {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
(arg => {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
((arg1, arg2) => {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
((...args) => {
/* ... */
})()
</pre>
</div>
<div class="histogram">
<pre class="code">
((arg1, arg2, ...args) => {
/* ... */
})()
</pre>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
const fontSize = 12
const fontFamily = 'Monospace'
const font = `${fontSize}px ${fontFamily}`
const textFill = '#555'
const chartFill = 'rgba(100,100,0,0.25)'
const chartHeight = 20
const margin = 2
const separatorMargin = margin
const typeMargin = margin * 3
const chartMargin = margin * 2
const slotHeight = (
typeMargin +
fontSize +
chartMargin +
separatorMargin +
chartHeight
)
const canvasWidth = 400
const data = []
d3.selectAll('.histogram .code').each(function() {
const indent = new RegExp(`^${this.innerText.match(/^\s*/)[0]}`, 'gmi')
const code = this.innerText.replace(indent, '')
const ast = esprima.parse(code, {range: true})
const list = Array.from(astNodes(ast)).sort((a, b) =>
a.range[0] - b.range[0]
)
const types = []
const nodes = {}
const min = list.map(i => i.range[0]).sort((a, b) => a - b)[0]
const max = list.map(i => i.range[1]).sort((a, b) => b - a)[0]
for (const item of list) {
const type = item.type
if (type !== 'Program') {
if (types.indexOf(type) === -1) {
types.push(type)
}
nodes[type] = nodes[type] || []
nodes[type].push(item)
}
}
data.push({
types,
nodes,
code,
min,
max,
data: Array.from(
map(types, type => ({type, nodes: nodes[type]}))
),
})
})
d3.selectAll('.histogram').data(data)
.append('canvas')
.attr('width', canvasWidth)
.each(function (d, i) {
const {types, data, min, max, nodes, code} = d
const lines = code.split(/\n/)
const codeHeight = (fontSize + margin) * lines.length
const canvasHeight = (
codeHeight +
typeMargin +
slotHeight * types.length +
chartMargin * 2 +
chartHeight +
margin
)
d3.select(this).attr('height', canvasHeight)
const ctx = this.getContext('2d')
ctx.font = font
const s = d3.scaleLinear().domain([min, max]).range([1, canvasWidth])
ctx.fillStyle = 'white'
ctx.fillRect(0, 0, canvasWidth, canvasHeight)
ctx.fillStyle = textFill
let y = 0
lines.forEach((line, i) => {
y += fontSize + margin
ctx.fillText(line, margin, y)
})
ctx.fillStyle = chartFill
ctx.fillRect(0, y - margin * 2, canvasWidth, 1)
types.forEach((type, i) => {
let y = (
codeHeight +
typeMargin +
slotHeight * i +
fontSize +
typeMargin +
chartMargin * 2 +
chartHeight
)
ctx.fillStyle = textFill
ctx.fillText(`${type} (${nodes[type].length})`, margin, y)
y += chartMargin
ctx.fillStyle = chartFill
ctx.fillRect(0, y, canvasWidth, 1)
y += separatorMargin
ctx.fillStyle = chartFill
nodes[type].forEach(node => {
const width = s(node.range[1] - node.range[0]) - 1
ctx.fillRect(
s(node.range[0]),
codeHeight + chartMargin,
width < 1 ? 1 : width,
chartHeight
)
ctx.fillRect(
s(node.range[0]),
y,
width < 1 ? 1 : width,
chartHeight
)
})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment