[!NOTE] This is one of 210 standalone projects, maintained as part of the @thi.ng/umbrella monorepo and anti-framework.
🚀 Please help me to work full-time on these projects by sponsoring me on GitHub. Thank you! ❤️
- About
- Core language features
- Extensibility
- Status
- Installation
- Dependencies
- Usage examples
- API
- Authors
- License
Lightweight, extensible, interpreted Lisp-style DSL for embedding in other projects.
[!NOTE] This DSL implementation has been extracted as standalone package from existing work/examples bundled in the thi.ng/umbrella monorepo (i.e. lispy-repl)
The core language is intentionally kept minimal, aimed at data transformations, configuration, user code snippets/expressions, and currently only contains the following:
-
T: true -
F: false -
null: null PIHALF_PITAU-
INF: ∞ -
-INF: -∞
-
(zero? x): zero check -
(nan? x): NaN check -
(neg? x): < 0 check -
(null? x): Null(ish) check -
(pos? x): > 0 check
(+ x y...)(- x y...)(* x y...)(/ x y...)-
(inc x): increment (+1) -
(dec x): decrement (-1)
(and x y ...)(or x y ...)(not x)
(<< x y)(>> x y)(>>> x y)(bit-and x y)(bit-or x y)(bit-xor x y)(bit-not x)
(< x y)(<= x y)(> x y)(>= x y)(== x y)(!= x y)
All included...
-
(clamp x min max): clamp value to interval -
(deg x): convert radians to degrees -
(fit x a b c d): fit value from interval[a..b]into[c..d] -
(mix a b t): linear interpolation (same as GLSLmix()) -
(rad x): convert degrees to radians -
(smoothstep e1 e2 x): same as GLSLsmoothstep() -
(step edge x): same as GLSLstep()
-
(always ...): function which always returns true -
(comp f g): functional composition -
(def name val): define global symbol -
(defn name (...args) body): define global function -
(fnull? x fn): function which returns(fn)if x is nullish -
(identity x): function which always returns its arg -
(never ...): function which always returns false -
(partial fn arg): partial application (currying)
-
(env! (sym val ...)): modify bindings in current env -
(if test truthy falsey?): conditional with optional false branch -
(let (sym val ...) body): locally scoped var bindings/expression -
(while test body...): loop while test is truthy -
(-> ...): Clojure-style thread-first S-expression re-writing-
(-> a (+ b) (* c))→(* (+ a b) c)
-
-
(->> ...): Clojure-style thread-last S-expression re-writing-
(->> a (+ b) (* c))→(* c (+ b a))
-
-
(get arr index): get array/object value at index/key -
(set! arr index value): set array/object value for index/key -
(concat x y ...): same asArray.prototype.concat(...) -
(count x): number of items (also for strings) -
(filter fn list): list/array filtering -
(first x): first item of array -
(map fn list): list/array transformation -
(next x): remaining array items (after first) -
(push arr x...): same asArray.prototype.push(...) -
(reduce fn init list): list/array reduction
(capitalize x)-
(float x): same asparseFloat(x) -
(int x radix?): same asparseInt(x, radix) -
(join sep arr): same asArray.prototype.join(sep) (lower x)(pad-left x width fill)(pad-right x width fill)-
(str x...): coerce values to string, concat results (substr x from to?)(trim x)(upper x)
(re-match re x)(re-test re x)(regexp str flags?)(replace str regexp replacement)
-
(env): JSON stringified version of current env -
(syms): Array of symbol names in current env -
(print x...): akaconsole.log(...)
The core language can be easily customized/extended by defining new symbols (or
redefining existing ones) in the root environment ENV (see example below) or
passing a custom environment to
evalSource().
BETA - possibly breaking changes forthcoming
Search or submit any issues for this package
yarn add @thi.ng/lispyESM import:
import * as lispy from "@thi.ng/lispy";Browser ESM import:
<script type="module" src="https://esm.run/@thi.ng/lispy"></script>For Node.js REPL:
const lispy = await import("@thi.ng/lispy");Package sizes (brotli'd, pre-treeshake): ESM: 1.99 KB
- @thi.ng/api
- @thi.ng/checks
- @thi.ng/compare
- @thi.ng/defmulti
- @thi.ng/errors
- @thi.ng/math
- @thi.ng/object-utils
- @thi.ng/sexpr
- @thi.ng/strings
Note: @thi.ng/api is in most cases a type-only import (not used at runtime)
One project in this repo's /examples directory is using this package:
| Screenshot | Description | Live demo | Source |
|---|---|---|---|
| Browser REPL for a Lispy S-expression based mini language | Demo | Source |
[!NOTE] Please also see /tests for more small code examples..
import { evalSource, ENV } from "@thi.ng/lispy";
// define custom root environment
const CUSTOM_ENV = {
...ENV,
// re-define print fn (actually the same as default)
print: console.log,
// pre-define new global variable
name: "lispy"
};
const SRC = `
(print (+ 1 2 3 4))
;; 10
;; local variables
(let (a 23 b 42) (print (+ a b)))
;; 65
;; define global var/fn
;; here, a curried version of the built-in print fn
(def greetings! (partial print "hello,"))
;; print greeting ('name' symbol provided via custom env)
(greetings! name)
;; hello, lispy!
;; basic loop w/ local var
(let (i 0)
(while (< i 5)
(print i)
(env! (i (inc i)))))
;; 0
;; 1
;; 2
;; 3
;; 4
;; threading/rewriting operators
(->> name (str "hello, ") (print "result:"))
;; result: hello, lispy
;; print contents of default environment
(print (env))
`;
// execute with customized environment
evalSource(SRC, CUSTOM_ENV);
// output:
// 10
// 65
// hello, lispy
// 0
// 1
// 2
// 3
// 4
// result: hello, lispy
// {
// "+": "<function>",
// "*": "<function>",
// "-": "<function>",
// "/": "<function>",
// "inc": "<function>",
// "dec": "<function>",
// "null?": "<function>",
// "zero?": "<function>",
// "neg?": "<function>",
// "pos?": "<function>",
// "nan?": "<function>",
// "=": "<function>",
// "!=": "<function>",
// "<": "<function>",
// "<=": "<function>",
// ">=": "<function>",
// ">": "<function>",
// "T": true,
// "F": false,
// "null": null,
// "and": "<function>",
// "or": "<function>",
// "not": "<function>",
// "<<": "<function>",
// ">>": "<function>",
// ">>>": "<function>",
// "bit-and": "<function>",
// "bit-or": "<function>",
// "bit-xor": "<function>",
// "bit-not": "<function>",
// "E": 2.718281828459045,
// "LN10": 2.302585092994046,
// "LN2": 0.6931471805599453,
// "LOG10E": 0.4342944819032518,
// "LOG2E": 1.4426950408889634,
// "PI": 3.141592653589793,
// "SQRT1_2": 0.7071067811865476,
// "SQRT2": 1.4142135623730951,
// "abs": "<function>",
// "acos": "<function>",
// "acosh": "<function>",
// "asin": "<function>",
// "asinh": "<function>",
// "atan": "<function>",
// "atan2": "<function>",
// "atanh": "<function>",
// "cbrt": "<function>",
// "ceil": "<function>",
// "clz32": "<function>",
// "cos": "<function>",
// "cosh": "<function>",
// "exp": "<function>",
// "expm1": "<function>",
// "floor": "<function>",
// "fround": "<function>",
// "hypot": "<function>",
// "imul": "<function>",
// "log": "<function>",
// "log10": "<function>",
// "log1p": "<function>",
// "log2": "<function>",
// "max": "<function>",
// "min": "<function>",
// "pow": "<function>",
// "random": "<function>",
// "round": "<function>",
// "sign": "<function>",
// "sin": "<function>",
// "sinh": "<function>",
// "sqrt": "<function>",
// "tan": "<function>",
// "tanh": "<function>",
// "trunc": "<function>",
// "HALF_PI": 1.5707963267948966,
// "TAU": 6.283185307179586,
// "clamp": "<function>",
// "deg": "<function>",
// "fit": "<function>",
// "mix": "<function>",
// "rad": "<function>",
// "step": "<function>",
// "smoothstep": "<function>",
// "get": "<function>",
// "set!": "<function>",
// "push": "<function>",
// "concat": "<function>",
// "count": "<function>",
// "first": "<function>",
// "next": "<function>",
// "str": "<function>",
// "join": "<function>",
// "lower": "<function>",
// "upper": "<function>",
// "capitalize": "<function>",
// "pad-left": "<function>",
// "pad-right": "<function>",
// "substr": "<function>",
// "trim": "<function>",
// "regexp": "<function>",
// "re-test": "<function>",
// "re-match": "<function>",
// "replace": "<function>",
// "identity": "<function>",
// "always": "<function>",
// "never": "<function>",
// "int": "<function>",
// "float": "<function>",
// "print": "<function>",
// "partial": "<function>",
// "partial2": "<function>",
// "comp": "<function>",
// "comp2": "<function>",
// "fnull?": "<function>",
// "reduce": "<function>",
// "map": "<function>",
// "filter": "<function>",
// "name": "lispy",
// "greetings!": "<function>"
// }If this project contributes to an academic publication, please cite it as:
@misc{thing-lispy,
title = "@thi.ng/lispy",
author = "Karsten Schmidt",
note = "https://thi.ng/lispy",
year = 2023
}© 2023 - 2025 Karsten Schmidt // Apache License 2.0