Concatenative JavaScript
const vars = {}
const as = s => v => vars[s] = v
const def = s => v => {
vars[s] = v
}
const set = (last, prop, val) => {
last[prop] = val
}
const put = (last, prop, val) => {
last[prop] = val
return last
}
const of = k => run.last[k]
const $ = k => () => vars[k]
const run = (prog, log) => {
run.stack = []
let curr
for (let i = 0; i < prog.length; i++) {
let val = prog[i]
if (val != null && val === $) {
run.stack.push(val())
} else if (typeof val === 'function') {
let ctx = null
if (document.body[val.name]) {
ctx = document.body
val = val.bind(ctx)
} else if (document[val.name]) {
ctx = document
val = val.bind(ctx)
}
if (log) console.log('exec',
val.name == 'bound ' ? '\u03BB' : val.name, run.stack)
curr = val.apply(ctx, run.stack)
if (curr != run.stack) {
run.stack = curr != null ? [curr] : []
if (typeof curr === 'object') {
run.last = curr
}
}
} else {
if (val != null) run.stack.push(val)
}
}
}
const showStack = () => {
console.log('- stack:', run.stack)
console.log(' -> last object', run.last)
return run.stack
}
const _ = new Proxy({}, {
get: (o, key) => (() => (...a) =>
run.last[key].apply(run.last, a)
)()
})
// try it out
run(
[ 'div',
document.createElement,
document.body.appendChild,
as`myDiv`,
'innerHTML', 'mini <b>concatenative</b>', set,
'style', of, 'color', 'green', set,
'just logging the element', $`myDiv`, console.log,
'canvas', document.createElement,
document.body.appendChild,
'width', 500, put,
'height', 500, put,
def`myCanvas`,
'2d',
_.getContext,
'fillStyle', 'red', set,
100, 30, _.translate,
0, 0, 10, 100, _.fillRect,
20, 0, 10, 100, _.fillRect,
.2, _.rotate,
-30, 25, 100, 10, _.fillRect,
-20, 55, 100, 10, _.fillRect,
{},
'name', 'zevan', put,
'age', 82, put,
JSON.stringify,
as`person`,
showStack
]
)
console.log(vars);
I recently spent some time reading about and playing with the FORTH language. I keep finding myself creating weird vaguely FORTH inspired snippets now… This one processes an array that’s in a concatenative format like this:
[ 'hello world', Date.now(), console.log ]
Arguments are collected on a stack and passed to a function when a function is hit in the array. There are some odd helper functions for setting properties, setting nested properties, running functions with a specific context etc…
Here is GPT4’s mostly correct explanation of what it does
This is a rather unusual piece of JavaScript code that creates a simple “programming language” based on the concept of a stack machine, and runs a “program” in this language. It resembles some sort of DSL (domain-specific language) or script-like construct.
Here are what the main components do:
vars
: This is a dictionary that stores variables defined in the program.as
: This is a function that takes a strings
and returns a function that takes a valuev
, storingv
invars
under the keys
.def
: Similar toas
, it defines a variable invars
.set
: This function is used to set the property of an object. It takes the objectlast
, the property nameprop
, and the valueval
, and assignsval
to the propertyprop
of the objectlast
.put
: Similar toset
, it sets the property of an object and returns the updated object.of
: This function gets the property with namek
of the last object returned by the program.$
: This function is a getter for variables invars
.run
: This is the main function that runs the program. The program is an array of actions, where an action can be a function, an object with aname
of ‘get’ (which pushes the result of calling the function onto the stack), or a value (which is pushed onto the stack).showStack
: This function logs the current state of the stack and the last object returned._
: This creates a new Proxy object that allows arbitrary methods to be called on the last object returned by the program.The
run
function is then called with a list of actions to perform. The actions include creating adiv
andcanvas
element, appending them to thebody
of the document, setting their properties, and drawing on thecanvas
. It also creates an object with aname
andage
, converts it to JSON, and stores it in a variable. Finally, it shows the state of the stack and logs all the variables.