Published
Edited
Dec 13, 2021
Insert cell
Insert cell
Insert cell
[...new Set(nonlocal(parsed))].filter(e => !['console', 'JSON', 'Object', 'Date'].includes(e))
Insert cell
// TODO handle these weirder cases
// currently incorrect for [peer_id, pc], {command} (basically, for destructuring assignment)
`
channel.onmessage = (({ data }) => {
// handle signalling messages here
data = JSON.parse(data)
if (data == 'ping') {
channel.send(JSON.stringify('pong'))
return
}
//console.debug(data);
if (data.action == 'list') {
// data = {client_id: "my_id", response: ["peer_id1", "peer_id2"]}
// update the peer mesh with these peers
console.log('Got list command', data)
client_id = data.client_id
peers = data.response.filter(p => p != data.client_id)
const old_pcs = pcs
pcs = {}
peers.map(p => pcs[p] = old_pcs[p] || get_peer_connection({
polite: client_id < p,
send_signaling_message: obj => {
channel.send(JSON.stringify({action: 'message', body: JSON.stringify(obj), recipient: p}))
//console.debug('sending signaling message', obj, 'to', p)
},
install_signaling_message_handler: onmessage => message_handlers[p] = onmessage
})
)
// Make a simple mesh to check that connections are established.
const old_dcs = dcs
dcs = {}
Object.entries(pcs).map(([peer_id, pc]) => {
dcs[peer_id] = old_dcs[peer_id]
if (dcs[peer_id]) return;
pc.ondatachannel = ({ channel }) => channel.onmessage = ({ data }) => {
console.log("Got data from peer", peer_id, data)
const {command} = JSON.parse(data)
if (command) configure_connection(command)
}
const dc = pc.createDataChannel("mesh")
dcs[peer_id] = dc
dc.onopen = () => dc.send(JSON.stringify({client_id, time: Date.now()}))
})
} else if (data.action == 'message' && (data.sender in message_handlers)) {
message_handlers[data.sender](JSON.parse(data.body))
}
//mutable last_message = data
})
`
Insert cell
nonlocal = (parsed) => {
const state = {in_scope: [], nonlocal: []}
walk.recursive(parsed, state, {
// TODO verify that this list of expressions is exhaustive! Is there a cleaner way of writing this?
// e.g. generators?
Identifier(node, state, c) {
if (!state.in_scope.includes(node.name)) state.nonlocal.push(node.name)
},
VariableDeclarator(node, state, c) {
state.in_scope.push(node.id.name)
c(node.id, state)
node.init && c(node.init, state)
},
FunctionDeclaration(node, state, c) {
node.id && state.in_scope.push(node.id.name)
const old_scope = [...state.in_scope]
node.params.map(p => state.in_scope.push(p.name))
c(node.id, state)
node.params.map(p => c(p, state))
c(node.body, state)
state.in_scope = old_scope
},
FunctionExpression(node, state, c) { // anonymous function
const old_scope = [...state.in_scope]
node.params.map(p => state.in_scope.push(p.name))
node.params.map(p => c(p, state))
c(node.body, state)
state.in_scope = old_scope
},
ArrowFunctionExpression(node, state, c) {
const old_scope = [...state.in_scope]
node.params.map(p => state.in_scope.push(p.name))
node.params.map(p => c(p, state))
c(node.body, state)
state.in_scope = old_scope
},
ForStatement(node, state, c) {
const old_scope = [...state.in_scope]
c(node.init, state)
c(node.test, state)
c(node.update, state)
c(node.body, state)
state.in_scope = old_scope
},
ForInStatement(node, state, c) {
const old_scope = [...state.in_scope]
c(node.left, state)
c(node.right, state)
c(node.body, state)
state.in_scope = old_scope
},
ForOfStatement(node, state, c) {
const old_scope = [...state.in_scope]
c(node.left, state)
c(node.right, state)
c(node.body, state)
state.in_scope = old_scope
},
BlockStatement(node, state, c) {
const old_scope = [...state.in_scope]
node.body.map(e => c(e, state))
state.in_scope = old_scope
},
// TODO handle by following https://github.com/jquery/esprima/issues/1045
// AssignmentPattern(node, state, c) {
// const old_scope = [...state.in_scope]
// node.body.map(e => c(e, state))
// state.in_scope = old_scope
// }
})
return state.nonlocal
}
Insert cell
parsed = acorn.parse(code)
Insert cell
nonlocal_naive = {
const identifiers = []
const declarations = []
walk.simple(parsed, {
Identifier(node) {
identifiers.push(node.name)
},
VariableDeclarator(node) {
declarations.push(node.id.name)
}
})
return [... new Set(identifiers.filter(i => !declarations.includes(i)))]
}
Insert cell
default_code = `(a, b) => [c, d, a, b];
let anon = function(a1) {a1, a2}
let a2 = 42
function boop (a=1, b=c) {
let hi = heyo
function sloop () {
let heyo = 3
let bar = function (sloop) {
let hum = to
huh
}
}
}
hi
let x, y = a;
const z = 2;
async function *gen (hah) {yield await yo}
for (let foo = 1; foo < 100; foo ++) {
test;
let too
};
too
bloop`
Insert cell
acorn = require('acorn')
Insert cell
walk = require('acorn-walk')
Insert cell
import {textarea} from "@jashkenas/inputs"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more