@lionlang/core
v0.1.1
Published
Lion is a JSON-based Lisp.
Downloads
20
Readme
Lion
Lion is a JSON-based Lisp. Every Lion program is valid JSON, which makes it easy to serialize, store, inspect, and generate from other systems.
Lion evaluates:
- JSON primitives directly
- strings as environment references when a binding exists
- arrays as special forms or function-call expressions
- objects by recursively evaluating their values
The result is a small homoiconic language that can describe both computation and data interchange without a separate parser.
Packages
packages/
core/ evaluator, special forms, stdlib
repl/ interactive web REPL
agent/ real-world Lion program driving an OpenTUI + AI agentBasic Usage
import { Effect } from "effect";
import { run } from "@lionlang/core/evaluation/evaluate";
import { stdlib } from "@lionlang/core/modules";
const result = await Effect.runPromise(
run(["number/add", 1, 2], stdlib)
);
// => 3Custom Environment
Any string can resolve against the evaluation environment. That includes values, objects, classes, and functions.
import { Effect } from "effect";
import { run } from "@lionlang/core/evaluation/evaluate";
import { stdlib } from "@lionlang/core/modules";
const env = {
...stdlib,
price: 100,
taxRate: 0.08,
clamp: (min: number, max: number, value: number) =>
Math.min(max, Math.max(min, value)),
};
await Effect.runPromise(
run(["number/add", "price", ["number/multiply", "price", "taxRate"]], env)
);
// => 108
await Effect.runPromise(run(["clamp", 0, 100, 150], env));
// => 100Expression Model
Primitives
Numbers, booleans, and null evaluate to themselves. Strings are treated as references first; if no binding exists, the original string is returned.
42
true
null
"x"With { "x": 10 } in the environment, "x" evaluates to 10. Otherwise it evaluates to "x".
Arrays
Arrays are always treated as executable expressions:
- special forms such as
["define", "x", 1] - function calls when the first element evaluates to a function
["number/add", 1, 2]Evaluates to 3.
Objects
Objects preserve their keys and evaluate each value.
{
"sum": ["number/add", 1, 2],
"ok": true
}Evaluates to { "sum": 3, "ok": true }.
Special Forms
quote
Returns its argument without evaluating it.
["quote", ["number/add", 1, 2]]eval
Evaluates its argument, then evaluates the result.
["eval", ["quote", ["number/add", 1, 2]]]begin
Evaluates expressions in order and returns the last result.
["begin", ["define", "x", 1], ["define", "x", 2], "x"]define
Evaluates a value, stores it in the global environment, and returns the stored value.
["define", "answer", 42]lambda
Creates a Lion function with lexical scope.
["lambda", ["x"], ["number/add", "x", 1]]Lambdas close over surrounding scope, and they observe updated global bindings when invoked later.
cond
Evaluates condition/result pairs in order and returns the first matching result. An else branch is optional.
["cond",
[["number/greaterThan", "score", 90], "great"],
[["number/greaterThan", "score", 70], "pass"],
["else", "retry"]]If no branch matches and no else is provided, cond returns null.
match
Pattern matches a value against predicate/function pairs, then applies the first matching branch. The last argument is a fallback function.
["match",
"value",
["value/number?", ["lambda", ["x"], ["number/add", "x", 1]]],
["value/string?", ["lambda", ["x"], ["string/concat", "x", "!"]]],
["lambda", ["x"], "x"]]match also supports structural predicates:
["match",
{ "type": "user", "profile": { "name": "Ada" } },
[
{
"type": ["func/partial", "string/equals?", "user"],
"profile": { "name": "value/string?" }
},
"value/identity"
],
["lambda", ["x"], null]]Standard Library
The default environment exported as stdlib namespaces entries as <module>/<name>.
number
["number/add", 1, 2]
["number/subtract", 10, 3]
["number/multiply", 5, 4]
["number/divide", 20, 4]
["number/equals?", 5, 5]
["number/lessThan", 3, 5]
["number/greaterThan", 5, 3]
["number/lessThanOrEqualTo", 3, 5]
["number/greaterThanOrEqualTo", 5, 5]boolean
["boolean/not", true]
["boolean/and", true, false]
["boolean/or", false, true]
["boolean/xor", true, false]
["boolean/nand", true, true]
["boolean/nor", false, false]
["boolean/equals?", true, true]array
["array/make", 1, 2, 3]
["array/head", ["array/make", 1, 2, 3]]
["array/tail", ["array/make", 1, 2, 3]]
["array/length", ["array/make", 1, 2, 3]]
["array/concat", ["array/make", 1, 2], ["array/make", 3, 4]]
["array/includes?", ["array/make", 1, 2, 3], 2]
["array/map", ["array/make", 1, 2, 3], ["lambda", ["x"], ["number/add", "x", 1]]]
["array/flat-map", ["array/make", 1, 2], ["lambda", ["x"], ["array/make", "x", "x"]]]
["array/reduce", ["array/make", 1, 2, 3], 0, ["lambda", ["acc", "x"], ["number/add", "acc", "x"]]]string
["string/equals?", "a", "a"]
["string/length", "lion"]
["string/concat", "lion", "ized"]
["string/startsWith", "lion", "li"]
["string/endsWith", "lion", "on"]
["string/includes", "lion", "io"]
["string/indexOf", "lion", "o"]object
["object/get", {"a": 1}, "a"]
["object/get-path", {"a": {"b": {"c": 42}}}, "a.b.c"]
["object/keys", {"a": 1, "b": 2}]
["object/values", {"a": 1, "b": 2}]
["object/json-stringify", {"a": 1}, null, 2]
["object/get-method", "obj", "methodName"]
["object/call-method", "obj", "methodName", 1, 2]
["object/get-method-path", "obj", "nested.methodName"]
["object/call-method-path", "obj", "nested.methodName", 1, 2]
["object/new", "SomeClass", "arg1", "arg2"]object/get-path understands dot paths and numeric array segments such as "users.1.name".
func
["func/bind", "fn", "obj"]
["func/apply", "fn", "obj", 1, 2]
["func/callback", ["lambda", ["x"], ["number/add", "x", 1]]]
["func/partial", "fn", 1, 2]func/callback is especially useful for turning Lion lambdas into JavaScript callbacks while preserving access to the Lion environment.
value
["value/function?", "x"]
["value/number?", 1]
["value/string?", "lion"]
["value/boolean?", true]
["value/object?", {"a": 1}]
["value/array?", ["array/make", 1, 2]]
["value/null?", null]
["value/identity", 42]value/undefined? and value/symbol? are mainly useful when Lion is evaluating host values supplied through the environment.
console
["console/log", "hello"]
["console/log-json", {"hello": "world"}]Host Interop
Lion works well as a configuration and orchestration layer over ordinary JavaScript objects and functions.
The packages/agent package is the clearest example. It injects:
OpenTUIclasses and enums- AI SDK helpers
- a model instance
- Node
fs - Zod constructors
- a renderer instance
Then Lion code in packages/agent/src/agent.json uses:
object/get-pathto traverse host APIsobject/newto construct classesobject/call-methodvia aliases such as"()"func/callbackto hand Lion lambdas to promise and event APIsmatch,cond,begin, anddefineto orchestrate application flow
That lets Lion drive a non-trivial terminal UI agent entirely from JSON.
Development
From the repo root:
bun install
bun run prepare
bun test
bun run buildTo work on a specific package:
turbo test --filter=@lionlang/core
turbo dev --filter=@lionlang/repl