@thi.ng/lispy 0.1.0 → 0.3.0
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/CHANGELOG.md +13 -1
- package/README.md +223 -78
- package/index.d.ts +3 -3
- package/index.js +103 -24
- package/kernel.d.ts +1 -1
- package/kernel.js +4 -2
- package/package.json +28 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2025-05-
|
|
3
|
+
- **Last updated**: 2025-05-28T12:02:40Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -11,6 +11,18 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
11
11
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
12
12
|
and/or version bumps of transitive dependencies.
|
|
13
13
|
|
|
14
|
+
## [0.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/lispy@0.3.0) (2025-05-28)
|
|
15
|
+
|
|
16
|
+
#### 🚀 Features
|
|
17
|
+
|
|
18
|
+
- add/update builtins ([ab7cdec](https://github.com/thi-ng/umbrella/commit/ab7cdec))
|
|
19
|
+
|
|
20
|
+
## [0.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/lispy@0.2.0) (2025-05-21)
|
|
21
|
+
|
|
22
|
+
#### 🚀 Features
|
|
23
|
+
|
|
24
|
+
- add more builtins & control flow ([a3b559c](https://github.com/thi-ng/umbrella/commit/a3b559c))
|
|
25
|
+
|
|
14
26
|
## [0.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/lispy@0.1.0) (2025-05-20)
|
|
15
27
|
|
|
16
28
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -15,6 +15,22 @@
|
|
|
15
15
|
> GitHub](https://github.com/sponsors/postspectacular). Thank you! ❤️
|
|
16
16
|
|
|
17
17
|
- [About](#about)
|
|
18
|
+
- [Core language features](#core-language-features)
|
|
19
|
+
- [Constants](#constants)
|
|
20
|
+
- [Predicates](#predicates)
|
|
21
|
+
- [Basic math ops](#basic-math-ops)
|
|
22
|
+
- [Boolean logic operators](#boolean-logic-operators)
|
|
23
|
+
- [Bitwise operators](#bitwise-operators)
|
|
24
|
+
- [Comparison operators](#comparison-operators)
|
|
25
|
+
- [Constants & functions from JS-native `Math`](#constants--functions-from-js-native-math)
|
|
26
|
+
- [Selected utilities from thi.ng/math package](#selected-utilities-from-thingmath-package)
|
|
27
|
+
- [Functional programming](#functional-programming)
|
|
28
|
+
- [Control flow](#control-flow)
|
|
29
|
+
- [List/array/object processing](#listarrayobject-processing)
|
|
30
|
+
- [String processing](#string-processing)
|
|
31
|
+
- [Regexp](#regexp)
|
|
32
|
+
- [Misc utilities](#misc-utilities)
|
|
33
|
+
- [Extensibility](#extensibility)
|
|
18
34
|
- [Status](#status)
|
|
19
35
|
- [Installation](#installation)
|
|
20
36
|
- [Dependencies](#dependencies)
|
|
@@ -27,76 +43,155 @@
|
|
|
27
43
|
|
|
28
44
|
Lightweight, extensible, interpreted Lisp-style DSL for embedding in other projects.
|
|
29
45
|
|
|
30
|
-
> [!NOTE]
|
|
46
|
+
> [!NOTE]
|
|
47
|
+
> This DSL implementation has been extracted as standalone package from
|
|
31
48
|
> existing work/examples bundled in the thi.ng/umbrella monorepo (i.e.
|
|
32
49
|
> [lispy-repl](https://github.com/thi-ng/umbrella/tree/develop/examples/lispy-repl))
|
|
33
50
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
51
|
+
## Core language features
|
|
52
|
+
|
|
53
|
+
The core language is intentionally kept minimal, aimed at data transformations,
|
|
54
|
+
configuration, user code snippets/expressions, and currently only contains the
|
|
55
|
+
following:
|
|
56
|
+
|
|
57
|
+
### Constants
|
|
58
|
+
- `T`: true
|
|
59
|
+
- `F`: false
|
|
60
|
+
- `null`: null
|
|
61
|
+
- `PI`
|
|
62
|
+
- `HALF_PI`
|
|
63
|
+
- `TAU`
|
|
64
|
+
- `INF`: ∞
|
|
65
|
+
- `-INF`: -∞
|
|
66
|
+
|
|
67
|
+
### Predicates
|
|
68
|
+
|
|
69
|
+
- `(zero? x)`: zero check
|
|
70
|
+
- `(nan? x)`: NaN check
|
|
71
|
+
- `(neg? x)`: < 0 check
|
|
72
|
+
- `(null? x)`: Null(ish) check
|
|
73
|
+
- `(pos? x)`: > 0 check
|
|
74
|
+
|
|
75
|
+
### Basic math ops
|
|
76
|
+
|
|
77
|
+
- `(+ x y...)`
|
|
78
|
+
- `(- x y...)`
|
|
79
|
+
- `(* x y...)`
|
|
80
|
+
- `(/ x y...)`
|
|
81
|
+
- `(inc x)`: increment (+1)
|
|
82
|
+
- `(dec x)`: decrement (-1)
|
|
83
|
+
|
|
84
|
+
### Boolean logic operators
|
|
85
|
+
|
|
86
|
+
- `(and x y ...)`
|
|
87
|
+
- `(or x y ...)`
|
|
88
|
+
- `(not x)`
|
|
89
|
+
|
|
90
|
+
### Bitwise operators
|
|
91
|
+
|
|
92
|
+
- `(<< x y)`
|
|
93
|
+
- `(>> x y)`
|
|
94
|
+
- `(>>> x y)`
|
|
95
|
+
- `(bit-and x y)`
|
|
96
|
+
- `(bit-or x y)`
|
|
97
|
+
- `(bit-xor x y)`
|
|
98
|
+
- `(bit-not x)`
|
|
99
|
+
|
|
100
|
+
### Comparison operators
|
|
101
|
+
|
|
102
|
+
- `(< x y)`
|
|
103
|
+
- `(<= x y)`
|
|
104
|
+
- `(> x y)`
|
|
105
|
+
- `(>= x y)`
|
|
106
|
+
- `(== x y)`
|
|
107
|
+
- `(!= x y)`
|
|
108
|
+
|
|
109
|
+
### Constants & functions from JS-native `Math`
|
|
110
|
+
|
|
111
|
+
All included...
|
|
112
|
+
|
|
113
|
+
### Selected utilities from thi.ng/math package
|
|
114
|
+
|
|
115
|
+
- `(clamp x min max)`: clamp value to interval
|
|
116
|
+
- `(deg x)`: convert radians to degrees
|
|
117
|
+
- `(fit x a b c d)`: fit value from interval `[a..b]` into `[c..d]`
|
|
118
|
+
- `(mix a b t)`: linear interpolation (same as GLSL `mix()`)
|
|
119
|
+
- `(rad x)`: convert degrees to radians
|
|
120
|
+
- `(smoothstep e1 e2 x)`: same as GLSL `smoothstep()`
|
|
121
|
+
- `(step edge x)`: same as GLSL `step()`
|
|
122
|
+
|
|
123
|
+
### Functional programming
|
|
124
|
+
|
|
125
|
+
- `(always ...)`: function which always returns true
|
|
126
|
+
- `(comp f g)`: functional composition
|
|
127
|
+
- `(def name val)`: define global symbol
|
|
128
|
+
- `(defn name (...args) body)`: define global function
|
|
129
|
+
- `(fnull? x fn)`: function which returns `(fn)` if x is nullish
|
|
130
|
+
- `(identity x)`: function which always returns its arg
|
|
131
|
+
- `(never ...)`: function which always returns false
|
|
132
|
+
- `(partial fn arg)`: partial application (currying)
|
|
133
|
+
|
|
134
|
+
### Control flow
|
|
135
|
+
|
|
136
|
+
- `(env! (sym val ...))`: modify bindings in current env
|
|
137
|
+
- `(if test truthy falsey?)`: conditional with optional false branch
|
|
138
|
+
- `(let (sym val ...) body)`: locally scoped var bindings/expression
|
|
139
|
+
- `(while test body...)`: loop while test is truthy
|
|
140
|
+
- `(-> ...)`: Clojure-style thread-first S-expression re-writing
|
|
141
|
+
- `(-> a (+ b) (* c))` → `(* (+ a b) c)`
|
|
142
|
+
- `(->> ...)`: Clojure-style thread-last S-expression re-writing
|
|
143
|
+
- `(->> a (+ b) (* c))` → `(* c (+ b a))`
|
|
144
|
+
|
|
145
|
+
### List/array/object processing
|
|
146
|
+
|
|
147
|
+
- `(get arr index)`: get array/object value at index/key
|
|
148
|
+
- `(set! arr index value)`: set array/object value for index/key
|
|
149
|
+
- `(concat x y ...)`: same as `Array.prototype.concat(...)`
|
|
150
|
+
- `(count x)`: number of items (also for strings)
|
|
151
|
+
- `(filter fn list)`: list/array filtering
|
|
152
|
+
- `(first x)`: first item of array
|
|
153
|
+
- `(map fn list)`: list/array transformation
|
|
154
|
+
- `(next x)`: remaining array items (after first)
|
|
155
|
+
- `(push arr x...)`: same as `Array.prototype.push(...)`
|
|
156
|
+
- `(reduce fn init list)`: list/array reduction
|
|
157
|
+
|
|
158
|
+
### String processing
|
|
159
|
+
|
|
160
|
+
- `(capitalize x)`
|
|
161
|
+
- `(float x)`: same as `parseFloat(x)`
|
|
162
|
+
- `(int x radix?)`: same as `parseInt(x, radix)`
|
|
163
|
+
- `(join sep arr)`: same as `Array.prototype.join(sep)`
|
|
164
|
+
- `(lower x)`
|
|
165
|
+
- `(pad-left x width fill)`
|
|
166
|
+
- `(pad-right x width fill)`
|
|
167
|
+
- `(str x...)`: coerce values to string, concat results
|
|
168
|
+
- `(substr x from to?)`
|
|
169
|
+
- `(trim x)`
|
|
170
|
+
- `(upper x)`
|
|
171
|
+
|
|
172
|
+
### Regexp
|
|
173
|
+
|
|
174
|
+
- `(re-match re x)`
|
|
175
|
+
- `(re-test re x)`
|
|
176
|
+
- `(regexp str flags?)`
|
|
177
|
+
- `(replace str regexp replacement)`
|
|
178
|
+
|
|
179
|
+
### Misc utilities
|
|
180
|
+
|
|
181
|
+
- `(env)`: JSON stringified version of current env
|
|
182
|
+
- `(syms)`: Array of symbol names in current env
|
|
183
|
+
- `(print x...)`: aka `console.log(...)`
|
|
184
|
+
|
|
185
|
+
## Extensibility
|
|
186
|
+
|
|
187
|
+
The core language can be easily customized/extended by defining new symbols (or
|
|
188
|
+
redefining existing ones) in the root environment `ENV` (see example below) or
|
|
189
|
+
passing a custom environment to
|
|
190
|
+
[`evalSource()`](https://docs.thi.ng/umbrella/lispy/functions/evalSource.html).
|
|
96
191
|
|
|
97
192
|
## Status
|
|
98
193
|
|
|
99
|
-
**
|
|
194
|
+
**BETA** - possibly breaking changes forthcoming
|
|
100
195
|
|
|
101
196
|
[Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Blispy%5D+in%3Atitle)
|
|
102
197
|
|
|
@@ -126,7 +221,7 @@ For Node.js REPL:
|
|
|
126
221
|
const lispy = await import("@thi.ng/lispy");
|
|
127
222
|
```
|
|
128
223
|
|
|
129
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 1.
|
|
224
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 1.96 KB
|
|
130
225
|
|
|
131
226
|
## Dependencies
|
|
132
227
|
|
|
@@ -156,12 +251,22 @@ directory is using this package:
|
|
|
156
251
|
|
|
157
252
|
[Generated API docs](https://docs.thi.ng/umbrella/lispy/)
|
|
158
253
|
|
|
254
|
+
> [!NOTE]
|
|
255
|
+
> Please also see
|
|
256
|
+
> [/tests](https://github.com/thi-ng/umbrella/blob/develop/packages/lispy/test)
|
|
257
|
+
> for more small code examples..
|
|
258
|
+
|
|
159
259
|
```ts tangle:export/readme-1.ts
|
|
160
260
|
import { evalSource, ENV } from "@thi.ng/lispy";
|
|
161
261
|
|
|
162
|
-
// define custom
|
|
163
|
-
|
|
164
|
-
ENV
|
|
262
|
+
// define custom root environment
|
|
263
|
+
const CUSTOM_ENV = {
|
|
264
|
+
...ENV,
|
|
265
|
+
// re-define print fn (actually the same as default)
|
|
266
|
+
print: console.log,
|
|
267
|
+
// pre-define new global variable
|
|
268
|
+
name: "lispy"
|
|
269
|
+
};
|
|
165
270
|
|
|
166
271
|
const SRC = `
|
|
167
272
|
(print (+ 1 2 3 4))
|
|
@@ -175,32 +280,63 @@ const SRC = `
|
|
|
175
280
|
;; here, a curried version of the built-in print fn
|
|
176
281
|
(def greetings! (partial print "hello,"))
|
|
177
282
|
|
|
178
|
-
;; print greeting (name
|
|
283
|
+
;; print greeting ('name' symbol provided via custom env)
|
|
179
284
|
(greetings! name)
|
|
180
285
|
;; hello, lispy!
|
|
181
286
|
|
|
287
|
+
;; basic loop w/ local var
|
|
288
|
+
(let (i 0)
|
|
289
|
+
(while (< i 5)
|
|
290
|
+
(print i)
|
|
291
|
+
(env! (i (inc i)))))
|
|
292
|
+
;; 0
|
|
293
|
+
;; 1
|
|
294
|
+
;; 2
|
|
295
|
+
;; 3
|
|
296
|
+
;; 4
|
|
297
|
+
|
|
298
|
+
;; threading/rewriting operators
|
|
299
|
+
(->> name (str "hello, ") (print "result:"))
|
|
300
|
+
;; result: hello, lispy
|
|
301
|
+
|
|
182
302
|
;; print contents of default environment
|
|
183
303
|
(print (env))
|
|
184
304
|
`;
|
|
185
305
|
|
|
186
|
-
|
|
306
|
+
// execute with customized environment
|
|
307
|
+
evalSource(SRC, CUSTOM_ENV);
|
|
187
308
|
|
|
188
309
|
// output:
|
|
189
310
|
// 10
|
|
190
311
|
// 65
|
|
191
|
-
// hello, lispy
|
|
192
|
-
//
|
|
312
|
+
// hello, lispy
|
|
313
|
+
// 0
|
|
314
|
+
// 1
|
|
315
|
+
// 2
|
|
316
|
+
// 3
|
|
317
|
+
// 4
|
|
318
|
+
// result: hello, lispy
|
|
193
319
|
// {
|
|
194
320
|
// "+": "<function>",
|
|
195
321
|
// "*": "<function>",
|
|
196
322
|
// "-": "<function>",
|
|
197
323
|
// "/": "<function>",
|
|
324
|
+
// "inc": "<function>",
|
|
325
|
+
// "dec": "<function>",
|
|
326
|
+
// "null?": "<function>",
|
|
327
|
+
// "zero?": "<function>",
|
|
328
|
+
// "neg?": "<function>",
|
|
329
|
+
// "pos?": "<function>",
|
|
330
|
+
// "nan?": "<function>",
|
|
198
331
|
// "=": "<function>",
|
|
199
332
|
// "!=": "<function>",
|
|
200
333
|
// "<": "<function>",
|
|
201
334
|
// "<=": "<function>",
|
|
202
335
|
// ">=": "<function>",
|
|
203
336
|
// ">": "<function>",
|
|
337
|
+
// "T": true,
|
|
338
|
+
// "F": false,
|
|
339
|
+
// "null": null,
|
|
204
340
|
// "and": "<function>",
|
|
205
341
|
// "or": "<function>",
|
|
206
342
|
// "not": "<function>",
|
|
@@ -263,13 +399,15 @@ evalSource(SRC, {...ENV, name: "lispy"});
|
|
|
263
399
|
// "rad": "<function>",
|
|
264
400
|
// "step": "<function>",
|
|
265
401
|
// "smoothstep": "<function>",
|
|
266
|
-
// "
|
|
267
|
-
// "
|
|
402
|
+
// "get": "<function>",
|
|
403
|
+
// "set!": "<function>",
|
|
268
404
|
// "push": "<function>",
|
|
269
405
|
// "concat": "<function>",
|
|
270
406
|
// "count": "<function>",
|
|
271
407
|
// "first": "<function>",
|
|
272
408
|
// "next": "<function>",
|
|
409
|
+
// "str": "<function>",
|
|
410
|
+
// "join": "<function>",
|
|
273
411
|
// "lower": "<function>",
|
|
274
412
|
// "upper": "<function>",
|
|
275
413
|
// "capitalize": "<function>",
|
|
@@ -281,15 +419,22 @@ evalSource(SRC, {...ENV, name: "lispy"});
|
|
|
281
419
|
// "re-test": "<function>",
|
|
282
420
|
// "re-match": "<function>",
|
|
283
421
|
// "replace": "<function>",
|
|
422
|
+
// "identity": "<function>",
|
|
423
|
+
// "always": "<function>",
|
|
424
|
+
// "never": "<function>",
|
|
425
|
+
// "int": "<function>",
|
|
426
|
+
// "float": "<function>",
|
|
284
427
|
// "print": "<function>",
|
|
285
|
-
// "env": "<function>",
|
|
286
428
|
// "partial": "<function>",
|
|
287
429
|
// "partial2": "<function>",
|
|
288
430
|
// "comp": "<function>",
|
|
289
431
|
// "comp2": "<function>",
|
|
432
|
+
// "fnull?": "<function>",
|
|
290
433
|
// "reduce": "<function>",
|
|
291
434
|
// "map": "<function>",
|
|
292
|
-
// "filter": "<function>"
|
|
435
|
+
// "filter": "<function>",
|
|
436
|
+
// "name": "lispy",
|
|
437
|
+
// "greetings!": "<function>"
|
|
293
438
|
// }
|
|
294
439
|
```
|
|
295
440
|
|
|
@@ -304,10 +449,10 @@ If this project contributes to an academic publication, please cite it as:
|
|
|
304
449
|
title = "@thi.ng/lispy",
|
|
305
450
|
author = "Karsten Schmidt",
|
|
306
451
|
note = "https://thi.ng/lispy",
|
|
307
|
-
year =
|
|
452
|
+
year = 2023
|
|
308
453
|
}
|
|
309
454
|
```
|
|
310
455
|
|
|
311
456
|
## License
|
|
312
457
|
|
|
313
|
-
© 2025 Karsten Schmidt // Apache License 2.0
|
|
458
|
+
© 2023 - 2025 Karsten Schmidt // Apache License 2.0
|
package/index.d.ts
CHANGED
|
@@ -7,9 +7,9 @@ export type Env = Record<string, any>;
|
|
|
7
7
|
*/
|
|
8
8
|
export declare const BUILTINS: import("@thi.ng/defmulti").MultiFn2<ASTNode[], Env, any>;
|
|
9
9
|
/**
|
|
10
|
-
* Root environment bindings. This is the default env used by
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Root environment bindings. This is the default env used by {@link evalSource}
|
|
11
|
+
* and can be used to define extensions of the core language. Functions defined
|
|
12
|
+
* here can receive any number of arguments.
|
|
13
13
|
*/
|
|
14
14
|
export declare const ENV: Env;
|
|
15
15
|
/**
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { identity, always, never } from "@thi.ng/api/fn";
|
|
1
2
|
import { isFunction } from "@thi.ng/checks/is-function";
|
|
2
3
|
import { OPERATORS } from "@thi.ng/compare/ops";
|
|
3
4
|
import { DEFAULT, defmulti } from "@thi.ng/defmulti";
|
|
@@ -33,6 +34,36 @@ const __fnImpl = (args, body, env) => (...xs) => {
|
|
|
33
34
|
);
|
|
34
35
|
return body.reduce((_, x) => interpret(x, $env), void 0);
|
|
35
36
|
};
|
|
37
|
+
const __threadOp = (fn) => ([_, ...body], env) => {
|
|
38
|
+
const n = body.length;
|
|
39
|
+
if (!n) return;
|
|
40
|
+
let res = body[0];
|
|
41
|
+
for (let i = 1; i < n; i++) {
|
|
42
|
+
const curr = { ...body[i] };
|
|
43
|
+
assert(
|
|
44
|
+
curr.type === "expr",
|
|
45
|
+
`expected expression, got ${curr.type}`
|
|
46
|
+
);
|
|
47
|
+
const $curr = curr;
|
|
48
|
+
$curr.children = fn(res, $curr.children);
|
|
49
|
+
res = $curr;
|
|
50
|
+
}
|
|
51
|
+
return interpret(res, env);
|
|
52
|
+
};
|
|
53
|
+
const __injectBindings = (args, env) => {
|
|
54
|
+
const pairs = args.children;
|
|
55
|
+
assert(
|
|
56
|
+
args.type === "expr" && !(pairs.length % 2),
|
|
57
|
+
`require pairs of key-val bindings`
|
|
58
|
+
);
|
|
59
|
+
for (let i = 0, n = pairs.length; i < n; i += 2) {
|
|
60
|
+
assert(
|
|
61
|
+
pairs[i].type === "sym",
|
|
62
|
+
`expected symbol, got: ${pairs[i].type}`
|
|
63
|
+
);
|
|
64
|
+
env[pairs[i].value] = interpret(pairs[i + 1], env);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
36
67
|
const BUILTINS = defmulti(
|
|
37
68
|
(x) => x[0].value,
|
|
38
69
|
{},
|
|
@@ -52,22 +83,43 @@ const BUILTINS = defmulti(
|
|
|
52
83
|
// create local symbol/variable bindings, e.g.
|
|
53
84
|
// `(let (a 1 b 2 c (+ a b)) (print a b c))`
|
|
54
85
|
let: ([_, args, ...body], env) => {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
args.type === "expr" && !(pairs.length % 2),
|
|
58
|
-
`require pairs of key-val bindings`
|
|
59
|
-
);
|
|
60
|
-
let $env = { ...env };
|
|
61
|
-
for (let i = 0, n = pairs.length; i < n; i += 2) {
|
|
62
|
-
assert(
|
|
63
|
-
pairs[i].type === "sym",
|
|
64
|
-
`expected symbol, got: ${pairs[i].type}`
|
|
65
|
-
);
|
|
66
|
-
$env[pairs[i].value] = interpret(pairs[i + 1], $env);
|
|
67
|
-
}
|
|
86
|
+
const $env = { ...env };
|
|
87
|
+
__injectBindings(args, $env);
|
|
68
88
|
return body.reduce((_2, x) => interpret(x, $env), void 0);
|
|
69
89
|
},
|
|
90
|
+
while: ([, test, ...body], env) => {
|
|
91
|
+
const n = body.length;
|
|
92
|
+
while (interpret(test, env)) {
|
|
93
|
+
for (let i = 0; i < n; i++) interpret(body[i], env);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"env!": ([_, args], env) => __injectBindings(args, env),
|
|
70
97
|
list: ([_, ...body], env) => evalArgs(body, env),
|
|
98
|
+
obj: ([_, ...body], env) => {
|
|
99
|
+
assert(!(body.length % 2), `require pairs of key-val bindings`);
|
|
100
|
+
const obj = {};
|
|
101
|
+
for (let i = 0, n = body.length; i < n; i += 2) {
|
|
102
|
+
const key = body[i].type === "expr" ? interpret(body[i], env) : body[i].value;
|
|
103
|
+
obj[String(key)] = interpret(body[i + 1], env);
|
|
104
|
+
}
|
|
105
|
+
return obj;
|
|
106
|
+
},
|
|
107
|
+
keys: ([_, x], env) => Object.keys(interpret(x, env)),
|
|
108
|
+
vals: ([_, x], env) => Object.values(interpret(x, env)),
|
|
109
|
+
pairs: ([_, x], env) => Object.entries(interpret(x, env)),
|
|
110
|
+
// rewriting operators:
|
|
111
|
+
// iteratively threads first child as FIRST arg of next child
|
|
112
|
+
// (-> a (+ b) (* c)) = (* (+ a b) c)
|
|
113
|
+
"->": __threadOp((res, [f, ...children]) => [f, res, ...children]),
|
|
114
|
+
// iteratively threads first child as LAST arg of next child
|
|
115
|
+
// (->> a (+ b) (* c)) = (* c (+ b a))
|
|
116
|
+
"->>": __threadOp((res, children) => [...children, res]),
|
|
117
|
+
env: (_, env) => JSON.stringify(
|
|
118
|
+
env,
|
|
119
|
+
(_2, val) => isFunction(val) ? "<function>" : val,
|
|
120
|
+
2
|
|
121
|
+
),
|
|
122
|
+
syms: (_, env) => Object.keys(env),
|
|
71
123
|
// add default/fallback implementation to allow calling functions defined in
|
|
72
124
|
// the environment (either externally or via `defn`)
|
|
73
125
|
[DEFAULT]: ([x, ...args], env) => {
|
|
@@ -88,11 +140,35 @@ const ENV = {
|
|
|
88
140
|
"-": __mathOp((acc, x) => acc - x, (x) => -x),
|
|
89
141
|
// prettier-ignore
|
|
90
142
|
"/": __mathOp((acc, x) => acc / x, (x) => 1 / x),
|
|
143
|
+
inc: (x) => x + 1,
|
|
144
|
+
dec: (x) => x - 1,
|
|
145
|
+
// predicates
|
|
146
|
+
"null?": (x) => x == null,
|
|
147
|
+
"zero?": (x) => x === 0,
|
|
148
|
+
"neg?": (x) => x < 0,
|
|
149
|
+
"pos?": (x) => x > 0,
|
|
150
|
+
"nan?": (x) => isNaN(x),
|
|
91
151
|
// comparisons
|
|
92
152
|
...OPERATORS,
|
|
93
153
|
// boolean logic
|
|
94
|
-
|
|
95
|
-
|
|
154
|
+
T: true,
|
|
155
|
+
F: false,
|
|
156
|
+
null: null,
|
|
157
|
+
INF: Infinity,
|
|
158
|
+
"-INF": -Infinity,
|
|
159
|
+
and: (...args) => {
|
|
160
|
+
let x;
|
|
161
|
+
for (x of args) {
|
|
162
|
+
if (!x) return false;
|
|
163
|
+
}
|
|
164
|
+
return x;
|
|
165
|
+
},
|
|
166
|
+
or: (...args) => {
|
|
167
|
+
for (let x of args) {
|
|
168
|
+
if (x) return x;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
},
|
|
96
172
|
not: (x) => !x,
|
|
97
173
|
// binary
|
|
98
174
|
"<<": (x, y) => x << y,
|
|
@@ -157,9 +233,9 @@ const ENV = {
|
|
|
157
233
|
rad,
|
|
158
234
|
step,
|
|
159
235
|
smoothstep: smoothStep,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
push: (list, ...x) => list.push(...x),
|
|
236
|
+
get: (arr, i) => arr[i],
|
|
237
|
+
"set!": (arr, i, x) => (arr[i] = x, arr),
|
|
238
|
+
push: (list, ...x) => (list.push(...x), list),
|
|
163
239
|
concat: (list, ...x) => list.concat(...x),
|
|
164
240
|
// returns length of first argument (presumably a list or string)
|
|
165
241
|
// (e.g. `(count "abc")` => 3)
|
|
@@ -170,6 +246,8 @@ const ENV = {
|
|
|
170
246
|
// there're no further items
|
|
171
247
|
next: (arg) => arg.length > 1 ? arg.slice(1) : void 0,
|
|
172
248
|
// strings
|
|
249
|
+
str: (...args) => args.join(""),
|
|
250
|
+
join: (sep, args) => args.join(sep),
|
|
173
251
|
lower,
|
|
174
252
|
upper,
|
|
175
253
|
capitalize,
|
|
@@ -181,13 +259,14 @@ const ENV = {
|
|
|
181
259
|
"re-test": (regexp, x) => regexp.test(x),
|
|
182
260
|
"re-match": (regexp, x) => regexp.exec(x),
|
|
183
261
|
replace: (x, regexp, replacement) => x.replace(regexp, replacement),
|
|
262
|
+
// functions
|
|
263
|
+
identity,
|
|
264
|
+
always,
|
|
265
|
+
never,
|
|
266
|
+
int: parseInt,
|
|
267
|
+
float: parseFloat,
|
|
184
268
|
// misc
|
|
185
|
-
print: console.log
|
|
186
|
-
env: () => JSON.stringify(
|
|
187
|
-
ENV,
|
|
188
|
-
(_, val) => isFunction(val) ? "<function>" : val,
|
|
189
|
-
2
|
|
190
|
-
)
|
|
269
|
+
print: console.log
|
|
191
270
|
};
|
|
192
271
|
const evalSource = (src, env = ENV) => parse(src).children.reduce((_, x) => interpret(x, env), void 0);
|
|
193
272
|
const evalArgs = (nodes, env = ENV) => nodes.map((a) => interpret(a, env));
|
package/kernel.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const KERNEL = "\n; partial functional application (currying for 1 arg)\n(defn partial (f x) (fn (y) (f x y)))\n\n; partial functional application (currying for 2 args)\n(defn partial2 (f x y) (fn (z) (f x y z)))\n\n; functional composition for 1-arg fns\n(defn comp (f g) (fn (x) (f (g x))))\n\n
|
|
1
|
+
export declare const KERNEL = "\n; partial functional application (currying for 1 arg)\n(defn partial (f x) (fn (y) (f x y)))\n\n; partial functional application (currying for 2 args)\n(defn partial2 (f x y) (fn (z) (f x y z)))\n\n; functional composition for 1-arg fns\n(defn comp (f g) (fn (x) (f (g x))))\n\n(defn complement (f) (fn (x) (not (f x))))\n\n; calls f if x is nullish\n(defn fnull? (x f) (if (null? x) (f) x))\n\n; list reduction\n(defn reduce (f acc xs) (if xs (reduce f (f acc (first xs)) (next xs)) acc))\n\n; list transformation (expressed as reduction)\n(defn map (f xs) (reduce (fn (acc x) (concat acc (f x))) (list) xs))\n\n; filter list with predicate function\n(defn filter (f xs) (reduce (fn (acc x) (if (f x) (concat acc x) acc)) (list) xs))\n";
|
|
2
2
|
//# sourceMappingURL=kernel.d.ts.map
|
package/kernel.js
CHANGED
|
@@ -8,8 +8,10 @@ const KERNEL = `
|
|
|
8
8
|
; functional composition for 1-arg fns
|
|
9
9
|
(defn comp (f g) (fn (x) (f (g x))))
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
(defn complement (f) (fn (x) (not (f x))))
|
|
12
|
+
|
|
13
|
+
; calls f if x is nullish
|
|
14
|
+
(defn fnull? (x f) (if (null? x) (f) x))
|
|
13
15
|
|
|
14
16
|
; list reduction
|
|
15
17
|
(defn reduce (f acc xs) (if xs (reduce f (f acc (first xs)) (next xs)) acc))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/lispy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Lightweight, extensible, interpreted Lisp-style DSL for embedding in other projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -39,22 +39,35 @@
|
|
|
39
39
|
"tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@thi.ng/api": "^8.11.
|
|
43
|
-
"@thi.ng/checks": "^3.7.
|
|
44
|
-
"@thi.ng/compare": "^2.4.
|
|
45
|
-
"@thi.ng/defmulti": "^3.0.
|
|
46
|
-
"@thi.ng/errors": "^2.5.
|
|
47
|
-
"@thi.ng/math": "^5.11.
|
|
48
|
-
"@thi.ng/object-utils": "^1.1.
|
|
49
|
-
"@thi.ng/sexpr": "^1.0.
|
|
50
|
-
"@thi.ng/strings": "^3.9.
|
|
42
|
+
"@thi.ng/api": "^8.11.28",
|
|
43
|
+
"@thi.ng/checks": "^3.7.8",
|
|
44
|
+
"@thi.ng/compare": "^2.4.20",
|
|
45
|
+
"@thi.ng/defmulti": "^3.0.68",
|
|
46
|
+
"@thi.ng/errors": "^2.5.34",
|
|
47
|
+
"@thi.ng/math": "^5.11.28",
|
|
48
|
+
"@thi.ng/object-utils": "^1.1.24",
|
|
49
|
+
"@thi.ng/sexpr": "^1.0.18",
|
|
50
|
+
"@thi.ng/strings": "^3.9.13"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"esbuild": "^0.25.
|
|
54
|
-
"typedoc": "^0.28.
|
|
53
|
+
"esbuild": "^0.25.5",
|
|
54
|
+
"typedoc": "^0.28.5",
|
|
55
55
|
"typescript": "^5.8.3"
|
|
56
56
|
},
|
|
57
57
|
"keywords": [
|
|
58
|
+
"array",
|
|
59
|
+
"clojure",
|
|
60
|
+
"composition",
|
|
61
|
+
"dsl",
|
|
62
|
+
"functional",
|
|
63
|
+
"interpreter",
|
|
64
|
+
"language",
|
|
65
|
+
"lisp",
|
|
66
|
+
"parser",
|
|
67
|
+
"regexp",
|
|
68
|
+
"runtime",
|
|
69
|
+
"s-expression",
|
|
70
|
+
"string",
|
|
58
71
|
"typescript"
|
|
59
72
|
],
|
|
60
73
|
"publishConfig": {
|
|
@@ -80,8 +93,8 @@
|
|
|
80
93
|
}
|
|
81
94
|
},
|
|
82
95
|
"thi.ng": {
|
|
83
|
-
"status": "
|
|
84
|
-
"year":
|
|
96
|
+
"status": "beta",
|
|
97
|
+
"year": 2023
|
|
85
98
|
},
|
|
86
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "61c3833b7ef7d044621454b5ea4af885d39f065e\n"
|
|
87
100
|
}
|