@lionlang/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +324 -6
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,15 +1,333 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Lion
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
Lion evaluates:
|
|
6
|
+
|
|
7
|
+
- JSON primitives directly
|
|
8
|
+
- strings as environment references when a binding exists
|
|
9
|
+
- arrays as special forms or function-call expressions
|
|
10
|
+
- objects by recursively evaluating their values
|
|
11
|
+
|
|
12
|
+
The result is a small homoiconic language that can describe both computation and data interchange without a separate parser.
|
|
13
|
+
|
|
14
|
+
## Packages
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
packages/
|
|
18
|
+
core/ evaluator, special forms, stdlib
|
|
19
|
+
repl/ interactive web REPL
|
|
20
|
+
agent/ real-world Lion program driving an OpenTUI + AI agent
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Basic Usage
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Effect } from "effect";
|
|
27
|
+
import { run } from "@lionlang/core/evaluation/evaluate";
|
|
28
|
+
import { stdlib } from "@lionlang/core/modules";
|
|
29
|
+
|
|
30
|
+
const result = await Effect.runPromise(
|
|
31
|
+
run(["number/add", 1, 2], stdlib)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// => 3
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Custom Environment
|
|
38
|
+
|
|
39
|
+
Any string can resolve against the evaluation environment. That includes values, objects, classes, and functions.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { Effect } from "effect";
|
|
43
|
+
import { run } from "@lionlang/core/evaluation/evaluate";
|
|
44
|
+
import { stdlib } from "@lionlang/core/modules";
|
|
45
|
+
|
|
46
|
+
const env = {
|
|
47
|
+
...stdlib,
|
|
48
|
+
price: 100,
|
|
49
|
+
taxRate: 0.08,
|
|
50
|
+
clamp: (min: number, max: number, value: number) =>
|
|
51
|
+
Math.min(max, Math.max(min, value)),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
await Effect.runPromise(
|
|
55
|
+
run(["number/add", "price", ["number/multiply", "price", "taxRate"]], env)
|
|
56
|
+
);
|
|
57
|
+
// => 108
|
|
58
|
+
|
|
59
|
+
await Effect.runPromise(run(["clamp", 0, 100, 150], env));
|
|
60
|
+
// => 100
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Expression Model
|
|
64
|
+
|
|
65
|
+
### Primitives
|
|
66
|
+
|
|
67
|
+
Numbers, booleans, and `null` evaluate to themselves. Strings are treated as references first; if no binding exists, the original string is returned.
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
42
|
|
71
|
+
true
|
|
72
|
+
null
|
|
73
|
+
"x"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
With `{ "x": 10 }` in the environment, `"x"` evaluates to `10`. Otherwise it evaluates to `"x"`.
|
|
77
|
+
|
|
78
|
+
### Arrays
|
|
79
|
+
|
|
80
|
+
Arrays are always treated as executable expressions:
|
|
81
|
+
|
|
82
|
+
1. special forms such as `["define", "x", 1]`
|
|
83
|
+
2. function calls when the first element evaluates to a function
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
["number/add", 1, 2]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Evaluates to `3`.
|
|
90
|
+
|
|
91
|
+
### Objects
|
|
92
|
+
|
|
93
|
+
Objects preserve their keys and evaluate each value.
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"sum": ["number/add", 1, 2],
|
|
98
|
+
"ok": true
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Evaluates to `{ "sum": 3, "ok": true }`.
|
|
103
|
+
|
|
104
|
+
## Special Forms
|
|
105
|
+
|
|
106
|
+
### `quote`
|
|
107
|
+
|
|
108
|
+
Returns its argument without evaluating it.
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
["quote", ["number/add", 1, 2]]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `eval`
|
|
115
|
+
|
|
116
|
+
Evaluates its argument, then evaluates the result.
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
["eval", ["quote", ["number/add", 1, 2]]]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `begin`
|
|
123
|
+
|
|
124
|
+
Evaluates expressions in order and returns the last result.
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
["begin", ["define", "x", 1], ["define", "x", 2], "x"]
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `define`
|
|
131
|
+
|
|
132
|
+
Evaluates a value, stores it in the global environment, and returns the stored value.
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
["define", "answer", 42]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `lambda`
|
|
139
|
+
|
|
140
|
+
Creates a Lion function with lexical scope.
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
["lambda", ["x"], ["number/add", "x", 1]]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Lambdas close over surrounding scope, and they observe updated global bindings when invoked later.
|
|
147
|
+
|
|
148
|
+
### `cond`
|
|
149
|
+
|
|
150
|
+
Evaluates condition/result pairs in order and returns the first matching result. An `else` branch is optional.
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
["cond",
|
|
154
|
+
[["number/greaterThan", "score", 90], "great"],
|
|
155
|
+
[["number/greaterThan", "score", 70], "pass"],
|
|
156
|
+
["else", "retry"]]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
If no branch matches and no `else` is provided, `cond` returns `null`.
|
|
160
|
+
|
|
161
|
+
### `match`
|
|
162
|
+
|
|
163
|
+
Pattern matches a value against predicate/function pairs, then applies the first matching branch. The last argument is a fallback function.
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
["match",
|
|
167
|
+
"value",
|
|
168
|
+
["value/number?", ["lambda", ["x"], ["number/add", "x", 1]]],
|
|
169
|
+
["value/string?", ["lambda", ["x"], ["string/concat", "x", "!"]]],
|
|
170
|
+
["lambda", ["x"], "x"]]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`match` also supports structural predicates:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
["match",
|
|
177
|
+
{ "type": "user", "profile": { "name": "Ada" } },
|
|
178
|
+
[
|
|
179
|
+
{
|
|
180
|
+
"type": ["func/partial", "string/equals?", "user"],
|
|
181
|
+
"profile": { "name": "value/string?" }
|
|
182
|
+
},
|
|
183
|
+
"value/identity"
|
|
184
|
+
],
|
|
185
|
+
["lambda", ["x"], null]]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Standard Library
|
|
189
|
+
|
|
190
|
+
The default environment exported as `stdlib` namespaces entries as `<module>/<name>`.
|
|
191
|
+
|
|
192
|
+
### `number`
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
["number/add", 1, 2]
|
|
196
|
+
["number/subtract", 10, 3]
|
|
197
|
+
["number/multiply", 5, 4]
|
|
198
|
+
["number/divide", 20, 4]
|
|
199
|
+
["number/equals?", 5, 5]
|
|
200
|
+
["number/lessThan", 3, 5]
|
|
201
|
+
["number/greaterThan", 5, 3]
|
|
202
|
+
["number/lessThanOrEqualTo", 3, 5]
|
|
203
|
+
["number/greaterThanOrEqualTo", 5, 5]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### `boolean`
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
["boolean/not", true]
|
|
210
|
+
["boolean/and", true, false]
|
|
211
|
+
["boolean/or", false, true]
|
|
212
|
+
["boolean/xor", true, false]
|
|
213
|
+
["boolean/nand", true, true]
|
|
214
|
+
["boolean/nor", false, false]
|
|
215
|
+
["boolean/equals?", true, true]
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### `array`
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
["array/make", 1, 2, 3]
|
|
222
|
+
["array/head", ["array/make", 1, 2, 3]]
|
|
223
|
+
["array/tail", ["array/make", 1, 2, 3]]
|
|
224
|
+
["array/length", ["array/make", 1, 2, 3]]
|
|
225
|
+
["array/concat", ["array/make", 1, 2], ["array/make", 3, 4]]
|
|
226
|
+
["array/includes?", ["array/make", 1, 2, 3], 2]
|
|
227
|
+
["array/map", ["array/make", 1, 2, 3], ["lambda", ["x"], ["number/add", "x", 1]]]
|
|
228
|
+
["array/flat-map", ["array/make", 1, 2], ["lambda", ["x"], ["array/make", "x", "x"]]]
|
|
229
|
+
["array/reduce", ["array/make", 1, 2, 3], 0, ["lambda", ["acc", "x"], ["number/add", "acc", "x"]]]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### `string`
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
["string/equals?", "a", "a"]
|
|
236
|
+
["string/length", "lion"]
|
|
237
|
+
["string/concat", "lion", "ized"]
|
|
238
|
+
["string/startsWith", "lion", "li"]
|
|
239
|
+
["string/endsWith", "lion", "on"]
|
|
240
|
+
["string/includes", "lion", "io"]
|
|
241
|
+
["string/indexOf", "lion", "o"]
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### `object`
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
["object/get", {"a": 1}, "a"]
|
|
248
|
+
["object/get-path", {"a": {"b": {"c": 42}}}, "a.b.c"]
|
|
249
|
+
["object/keys", {"a": 1, "b": 2}]
|
|
250
|
+
["object/values", {"a": 1, "b": 2}]
|
|
251
|
+
["object/json-stringify", {"a": 1}, null, 2]
|
|
252
|
+
["object/get-method", "obj", "methodName"]
|
|
253
|
+
["object/call-method", "obj", "methodName", 1, 2]
|
|
254
|
+
["object/get-method-path", "obj", "nested.methodName"]
|
|
255
|
+
["object/call-method-path", "obj", "nested.methodName", 1, 2]
|
|
256
|
+
["object/new", "SomeClass", "arg1", "arg2"]
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`object/get-path` understands dot paths and numeric array segments such as `"users.1.name"`.
|
|
260
|
+
|
|
261
|
+
### `func`
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
["func/bind", "fn", "obj"]
|
|
265
|
+
["func/apply", "fn", "obj", 1, 2]
|
|
266
|
+
["func/callback", ["lambda", ["x"], ["number/add", "x", 1]]]
|
|
267
|
+
["func/partial", "fn", 1, 2]
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`func/callback` is especially useful for turning Lion lambdas into JavaScript callbacks while preserving access to the Lion environment.
|
|
271
|
+
|
|
272
|
+
### `value`
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
["value/function?", "x"]
|
|
276
|
+
["value/number?", 1]
|
|
277
|
+
["value/string?", "lion"]
|
|
278
|
+
["value/boolean?", true]
|
|
279
|
+
["value/object?", {"a": 1}]
|
|
280
|
+
["value/array?", ["array/make", 1, 2]]
|
|
281
|
+
["value/null?", null]
|
|
282
|
+
["value/identity", 42]
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
`value/undefined?` and `value/symbol?` are mainly useful when Lion is evaluating host values supplied through the environment.
|
|
286
|
+
|
|
287
|
+
### `console`
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
["console/log", "hello"]
|
|
291
|
+
["console/log-json", {"hello": "world"}]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Host Interop
|
|
295
|
+
|
|
296
|
+
Lion works well as a configuration and orchestration layer over ordinary JavaScript objects and functions.
|
|
297
|
+
|
|
298
|
+
The `packages/agent` package is the clearest example. It injects:
|
|
299
|
+
|
|
300
|
+
- `OpenTUI` classes and enums
|
|
301
|
+
- AI SDK helpers
|
|
302
|
+
- a model instance
|
|
303
|
+
- Node `fs`
|
|
304
|
+
- Zod constructors
|
|
305
|
+
- a renderer instance
|
|
306
|
+
|
|
307
|
+
Then Lion code in [`packages/agent/src/agent.json`](/Users/andrueanderson/Workspace/TypeScript/lion/packages/agent/src/agent.json) uses:
|
|
308
|
+
|
|
309
|
+
- `object/get-path` to traverse host APIs
|
|
310
|
+
- `object/new` to construct classes
|
|
311
|
+
- `object/call-method` via aliases such as `"()"`
|
|
312
|
+
- `func/callback` to hand Lion lambdas to promise and event APIs
|
|
313
|
+
- `match`, `cond`, `begin`, and `define` to orchestrate application flow
|
|
314
|
+
|
|
315
|
+
That lets Lion drive a non-trivial terminal UI agent entirely from JSON.
|
|
316
|
+
|
|
317
|
+
## Development
|
|
318
|
+
|
|
319
|
+
From the repo root:
|
|
4
320
|
|
|
5
321
|
```bash
|
|
6
322
|
bun install
|
|
323
|
+
bun run prepare
|
|
324
|
+
bun test
|
|
325
|
+
bun run build
|
|
7
326
|
```
|
|
8
327
|
|
|
9
|
-
To
|
|
328
|
+
To work on a specific package:
|
|
10
329
|
|
|
11
330
|
```bash
|
|
12
|
-
|
|
331
|
+
turbo test --filter=@lionlang/core
|
|
332
|
+
turbo dev --filter=@lionlang/repl
|
|
13
333
|
```
|
|
14
|
-
|
|
15
|
-
This project was created using `bun init` in bun v1.3.1. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lionlang/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lion is a JSON-based Lisp.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Andrue Anderson <andrue.anderson.cs@gmail.com>",
|
|
8
|
+
"homepage": "https://lionlang.dev",
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
10
11
|
"url": "git+https://github.com/andrueandersoncs/lion.git",
|