Skip to content

Latest commit

 

History

History
175 lines (129 loc) · 4.31 KB

lisp.md

File metadata and controls

175 lines (129 loc) · 4.31 KB

./lisp.mjs

The furver lisp is an essential part of the furver experience. Firstly it enables the bulk and parallel handling of requests when using the user friendly client. Secondly it allows for more advanced usage by using the furver lisp directly.

This docs shows what the lisp features are. It also shows that you can use the lisp module directly by importing it.

Import

To use the Lisp interpreter, you first need to import the exec function from the module:

import exec from './lisp.mjs'

Once you have imported the exec function, you can call it with an environment object and an expression to evaluate:

Basic usage

const env = { 'add': (a, b) => a + b }
const expression = ['add', 1, 2]

exec(env, expression).then(console.log)
3

The exec function takes two arguments:

  1. The environment object in which the expression is evaluated. This is an object that maps operator names to functions that implement those operators. In the example above, the environment object contains a add operator that adds two numbers.

  2. The expression to evaluate. This is an array that represents the expression in the Lisp syntax. The first element of the array is the operator name, and the rest of the elements are the arguments to the operator. In the example above, the expression is ['add', 1, 2], which means "add 1 and 2".

Async

The lisp is will wait for a promise returned by a call before calling the next function.

const env = { 'add': (a, b) => a + b, pIdentity: (x) => Promise.resolve(x) }

exec(env, ['add', ['pIdentity', 1], 2]).then(console.log)
3

Define a promiseAll helper on the env if you are working with arrays of promises.

Let Expressions

The Lisp interpreter supports let expressions, which allow you to define local variables in an expression. A let expression has the form:

[let [[name1 value1] [name2 value2] ...] body]

The let operator takes two arguments:

  1. An array of bindings, where each binding is a pair of a variable name and its value.

  2. The body expression, which is evaluated in a new environment that includes the variable bindings.

Here's an example of using a let expression:

const env = { 'add': (a, b) => a + b, always: (x) => () => x }
const expression = ['let',
  [
    ['x', ['always', 2]],
    ['y', ['always', 3]],
  ],
  ['add', ['x'], ['y']]]

exec(env, expression).then(console.log)
5

This expression defines two lexically scoped variables x and y with values 2 and 3, respectively, and then adds them together.

Fn Expressions

The Lisp interpreter also supports fn expressions, which allow you to define anonymous functions. An fn expression has the form:

[fn body]

The fn operator takes only the body argument. It does not support arguments as the environment already allows passing aliased values to the body. If you wish to define your own values you should use the let statement.

Here's an example of using an fn expression:

const env = { '+': (a, b) => a + b, x: () => 2}
const expression = ['fn', ['+', 1, ['x']]]

exec(env, expression)
  .then(async fn => console.log(await fn(), fn))
3 [Function (anonymous)]

This expression defines an anonymous function that adds 1 to a single argument x.

You can also overwrite the env value for x by passing an object to the function object.

const env = { '+': (a, b) => a + b }
const expression = ['fn', ['+', 1, ['ref', 'x']]]

exec(env, expression)
  .then(fn => fn({ x: 2 }))
  .then(console.log)
3

Ref Keyword

You might see a ref in the previous example. The ref allows you to get the reference/value of a specific alias on the environment. This removes the need for wrapping a value in a function that returns itself.

[ref, name]
exec({ 'hello': 'world' }, ['ref', 'hello']).then(console.log)
world

In conclusion, the lisp.mjs module provides an asynchronous implementation of a Lisp interpreter that supports basic expressions, let expressions, ref keyword and fn expressions.