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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more