0.0.6 • Published 9 days ago

j-expression v0.0.6

Weekly downloads
-
License
MIT
Repository
github
Last release
9 days ago

j-expression

S-expression but in JSON

S-expression

S-expression is also known as 'symbolic expressions' are a type of notation used to represent structured data, which is commonly used in LISP.

Typical forms of S-expression are like below:

;; Strings can be added
(append "Hello " "world!") ; => "Hello world!"

;; If you want to create a literal list of data, use ' to stop it from
;; being evaluated
'(+ 1 2) ; => (quote (+ 1 2)) => (+ 1 2)

;; Now, some arithmetic operations
(+ 1 1)  ; => 2
(- 8 1)  ; => 7

;; `list' is a convenience variadic constructor for lists
(list 1 2 3) ; => '(1 2 3)

As we can see, S-expression is all about list. LISP interpreter evals the list to get the result.

JSON

What's the alternative of list? JSON! We can use JSON to represent S-expression.

// String startsWith "$" like "$append" is a symbol live in the Environment, otherwise "Hello " is a string.
// String startsWith "$$" will escape this rule.
["$append", "Hello ", "world!"] // => (append "Hello " "world!") => "Hello world!"
["$append", "$$Hello ", "world!"] // => (append "$Hello " "world!") => "$Hello world!"

["$quote", ["$add", 1, 2]] // => (quote (+ 1 2)) => '(+ 1 2)

["$add", 1, 1] // =>  (+ 1 1)   ["$add", 1, 1] makes more sense than ["$+", 1, 1]
["$subtract", 8, 1] // => (- 8 1)

["$list", 1, 2, 3] // => (list 1 2 3) => '(1 2 3)

And j-expression is a simple interpreter to eval these expressions.

Scenario

This kind of expression can be used for dynamic rules.

We can provide a UI/Editor to generate such expression, then eval it to get the return value.

Please check the demo(https://j-expression-editor.vercel.app) and the source code(https://github.com/littlehaker/j-expression-editor) for a simple editor.

screenshot

Installation

npm i j-expression

Usage

import JExpression from "j-expression";
const expr = new JExpression();

expr.eval(["$add", 1, 2]); // => 3

// Custom Environment
expr.define("answer", 42);
expr.eval("$answer"); // => 42

Environment

Environment is where the interpreter accesses the symbols.

  • Comparison: $gt / $lt / $eq
["$gt", 2, 1] // => true
["$lt", 2, 1] // => false
["$eq", 2, 1] // => false
  • Computation: $add / $subtract / $multiply / $divide
["$add", 2, 1] // => 3
["$subtract", 2, 1] // => 1
["$multiply", 2, 2] // => 4
["$divide", 2, 1] // => 2
  • Condition: $if / $cond
["$if", true, "foo", "bar"] // => "foo"
["$cond",
  [["$gt", 2, 1], "foo"],
  [["$lt", 2, 3], "bar"],
  [true, "baz"]] // => if 2 > 1 then "foo" else if 2 < 3 then "bar" else baz => "foo"
  • Boolean: $and / $or
["$and", true, false, true] // => false
["$or", true, false, true] // => true
  • List: $list
["$list", 1, 2, 3] // => [1, 2, 3]
  • Eval: $quote / $eval
["$quote", ["$add", 1, 2]] // => ["$add", 1, 2]
["$eval", ["$quote", ["$add", 1, 2]]] // => 3

Asynchronous

You can use evalAsync to do asynchronous evaluation.

expr.define("addAsync", async (a, b) => a + b);
expr.define("deferredValue", Promise.resolve(42));

await expr.evalAsync(["$addAsync", 1, "$deferredValue"]) // => 43

Limitation

  • No lexical scope
  • Dynamic scope only
  • Poor default environment but easy to extend

License

MIT

0.0.6

9 days ago

0.0.5

10 days ago

0.0.4

12 days ago

0.0.3

1 month ago

0.0.2

1 month ago

0.0.1

1 month ago