@thi.ng/lispy 0.1.0 → 0.2.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 +7 -1
- package/README.md +203 -72
- package/index.d.ts +3 -3
- package/index.js +98 -24
- package/kernel.d.ts +1 -1
- package/kernel.js +3 -0
- package/package.json +18 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2025-05-
|
|
3
|
+
- **Last updated**: 2025-05-21T10:56:03Z
|
|
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,12 @@ 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.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/lispy@0.2.0) (2025-05-21)
|
|
15
|
+
|
|
16
|
+
#### 🚀 Features
|
|
17
|
+
|
|
18
|
+
- add more builtins & control flow ([a3b559c](https://github.com/thi-ng/umbrella/commit/a3b559c))
|
|
19
|
+
|
|
14
20
|
## [0.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/lispy@0.1.0) (2025-05-20)
|
|
15
21
|
|
|
16
22
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://mastodon.thi.ng/@toxi)
|
|
8
8
|
|
|
9
9
|
> [!NOTE]
|
|
10
|
-
> This is one of
|
|
10
|
+
> This is one of 207 standalone projects, maintained as part
|
|
11
11
|
> of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
|
|
12
12
|
> and anti-framework.
|
|
13
13
|
>
|
|
@@ -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,151 @@
|
|
|
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
|
-
|
|
51
|
+
## Core language features
|
|
52
|
+
|
|
53
|
+
The core language is intentionally kept minimal, aimed at data transformations,
|
|
54
|
+
and currently only contains the following:
|
|
55
|
+
|
|
56
|
+
### Constants
|
|
57
|
+
- `T`: true
|
|
58
|
+
- `F`: false
|
|
59
|
+
- `null`: null
|
|
60
|
+
- `PI`
|
|
61
|
+
- `HALF_PI`
|
|
62
|
+
- `TAU`
|
|
63
|
+
|
|
64
|
+
### Predicates
|
|
65
|
+
|
|
66
|
+
- `(zero? x)`: zero check
|
|
67
|
+
- `(nan? x)`: NaN check
|
|
68
|
+
- `(neg? x)`: < 0 check
|
|
69
|
+
- `(null? x)`: Null(ish) check
|
|
70
|
+
- `(pos? x)`: > 0 check
|
|
71
|
+
|
|
72
|
+
### Basic math ops
|
|
73
|
+
|
|
74
|
+
- `(+ x y...)`
|
|
75
|
+
- `(- x y...)`
|
|
76
|
+
- `(* x y...)`
|
|
77
|
+
- `(/ x y...)`
|
|
78
|
+
- `(inc x)`: increment (+1)
|
|
79
|
+
- `(dec x)`: decrement (-1)
|
|
80
|
+
|
|
81
|
+
### Boolean logic operators
|
|
82
|
+
|
|
83
|
+
- `(and x y ...)`
|
|
84
|
+
- `(or x y ...)`
|
|
85
|
+
- `(not x)`
|
|
86
|
+
|
|
87
|
+
### Bitwise operators
|
|
88
|
+
|
|
89
|
+
- `(<< x y)`
|
|
90
|
+
- `(>> x y)`
|
|
91
|
+
- `(>>> x y)`
|
|
92
|
+
- `(bit-and x y)`
|
|
93
|
+
- `(bit-or x y)`
|
|
94
|
+
- `(bit-xor x y)`
|
|
95
|
+
- `(bit-not x)`
|
|
96
|
+
|
|
97
|
+
### Comparison operators
|
|
98
|
+
|
|
99
|
+
- `(< x y)`
|
|
100
|
+
- `(<= x y)`
|
|
101
|
+
- `(> x y)`
|
|
102
|
+
- `(>= x y)`
|
|
103
|
+
- `(== x y)`
|
|
104
|
+
- `(!= x y)`
|
|
105
|
+
|
|
106
|
+
### Constants & functions from JS-native `Math`
|
|
107
|
+
|
|
108
|
+
All included...
|
|
109
|
+
|
|
110
|
+
### Selected utilities from thi.ng/math package
|
|
111
|
+
|
|
112
|
+
- `(clamp x min max)`: clamp value to interval
|
|
113
|
+
- `(deg x)`: convert radians to degrees
|
|
114
|
+
- `(fit x a b c d)`: fit value from interval `[a..b]` into `[c..d]`
|
|
115
|
+
- `(mix a b t)`: linear interpolation (same as GLSL `mix()`)
|
|
116
|
+
- `(rad x)`: convert degrees to radians
|
|
117
|
+
- `(smoothstep e1 e2 x)`: same as GLSL `smoothstep()`
|
|
118
|
+
- `(step edge x)`: same as GLSL `step()`
|
|
119
|
+
|
|
120
|
+
### Functional programming
|
|
121
|
+
|
|
122
|
+
- `(always ...)`: function which always returns true
|
|
123
|
+
- `(comp f g)`: functional composition
|
|
124
|
+
- `(def name val)`: define global symbol
|
|
125
|
+
- `(defn name (...args) body)`: define global function
|
|
126
|
+
- `(fnull? x fn)`: function which returns `(fn)` if x is nullish
|
|
127
|
+
- `(identity x)`: function which always returns its arg
|
|
128
|
+
- `(never ...)`: function which always returns false
|
|
129
|
+
- `(partial fn arg)`: partial application (currying)
|
|
130
|
+
|
|
131
|
+
### Control flow
|
|
132
|
+
|
|
133
|
+
- `(env! (sym val ...))`: modify bindings in current env
|
|
134
|
+
- `(if test truthy falsey?)`: conditional with optional false branch
|
|
135
|
+
- `(let (sym val ...) body)`: locally scoped var bindings/expression
|
|
136
|
+
- `(while test body... (recur (...))?)`: loop while test is truthy
|
|
137
|
+
- `(-> ...)`: Clojure-style thread-first S-expression re-writing
|
|
138
|
+
- `(-> a (+ b) (* c))` → `(* (+ a b) c)`
|
|
139
|
+
- `(->> ...)`: Clojure-style thread-last S-expression re-writing
|
|
140
|
+
- `(->> a (+ b) (* c))` → `(* c (+ b a))`
|
|
141
|
+
|
|
142
|
+
### List/array/object processing
|
|
143
|
+
|
|
144
|
+
- `(get arr index)`: get array/object value at index/key
|
|
145
|
+
- `(set! arr index value)`: set array/object value for index/key
|
|
146
|
+
- `(concat x y ...)`: same as `Array.prototype.concat(...)`
|
|
147
|
+
- `(count x)`: number of items (also for strings)
|
|
148
|
+
- `(filter fn list)`: list/array filtering
|
|
149
|
+
- `(first x)`: first item of array
|
|
150
|
+
- `(map fn list)`: list/array transformation
|
|
151
|
+
- `(next x)`: remaining array items (after first)
|
|
152
|
+
- `(push arr x...)`: same as `Array.prototype.push(...)`
|
|
153
|
+
- `(reduce fn init list)`: list/array reduction
|
|
154
|
+
|
|
155
|
+
### String processing
|
|
156
|
+
|
|
157
|
+
- `(capitalize x)`
|
|
158
|
+
- `(float x)`: same as `parseFloat(x)`
|
|
159
|
+
- `(int x radix?)`: same as `parseInt(x, radix)`
|
|
160
|
+
- `(join sep arr)`: same as `Array.prototype.join(sep)`
|
|
161
|
+
- `(lower x)`
|
|
162
|
+
- `(pad-left x width fill)`
|
|
163
|
+
- `(pad-right x width fill)`
|
|
164
|
+
- `(str x...)`: coerce values to string, concat results
|
|
165
|
+
- `(substr x from to?)`
|
|
166
|
+
- `(trim x)`
|
|
167
|
+
- `(upper x)`
|
|
168
|
+
|
|
169
|
+
### Regexp
|
|
170
|
+
|
|
171
|
+
- `(re-match re x)`
|
|
172
|
+
- `(re-test re x)`
|
|
173
|
+
- `(regexp str flags?)`
|
|
174
|
+
- `(replace str regexp replacement)`
|
|
175
|
+
|
|
176
|
+
### Misc utilities
|
|
177
|
+
|
|
178
|
+
- `(env)`: JSON stringified version of current env
|
|
179
|
+
- `(syms)`: Array of symbol names in current env
|
|
180
|
+
- `(print x...)`: aka `console.log(...)`
|
|
181
|
+
|
|
182
|
+
## Extensibility
|
|
93
183
|
|
|
94
184
|
The core language can be easily customized/extended by defining new items in the
|
|
95
|
-
root environment `ENV` (see example below)
|
|
185
|
+
root environment `ENV` (see example below) or passing a custom environment to
|
|
186
|
+
[`evalSource()`](https://docs.thi.ng/umbrella/lispyfunctions/evalSource.html).
|
|
96
187
|
|
|
97
188
|
## Status
|
|
98
189
|
|
|
99
|
-
**
|
|
190
|
+
**BETA** - possibly breaking changes forthcoming
|
|
100
191
|
|
|
101
192
|
[Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Blispy%5D+in%3Atitle)
|
|
102
193
|
|
|
@@ -126,7 +217,7 @@ For Node.js REPL:
|
|
|
126
217
|
const lispy = await import("@thi.ng/lispy");
|
|
127
218
|
```
|
|
128
219
|
|
|
129
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 1.
|
|
220
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 1.92 KB
|
|
130
221
|
|
|
131
222
|
## Dependencies
|
|
132
223
|
|
|
@@ -179,28 +270,59 @@ const SRC = `
|
|
|
179
270
|
(greetings! name)
|
|
180
271
|
;; hello, lispy!
|
|
181
272
|
|
|
273
|
+
;; basic loop w/ local var
|
|
274
|
+
(let (i 0)
|
|
275
|
+
(while (< i 5)
|
|
276
|
+
(print i)
|
|
277
|
+
(env! (i (inc i)))))
|
|
278
|
+
;; 0
|
|
279
|
+
;; 1
|
|
280
|
+
;; 2
|
|
281
|
+
;; 3
|
|
282
|
+
;; 4
|
|
283
|
+
|
|
284
|
+
;; threading/rewriting operators
|
|
285
|
+
(->> name (str "hello, ") (print "result:"))
|
|
286
|
+
;; result: hello, lispy
|
|
287
|
+
|
|
182
288
|
;; print contents of default environment
|
|
183
289
|
(print (env))
|
|
184
290
|
`;
|
|
185
291
|
|
|
292
|
+
// execute with customized environment
|
|
186
293
|
evalSource(SRC, {...ENV, name: "lispy"});
|
|
187
294
|
|
|
188
295
|
// output:
|
|
189
296
|
// 10
|
|
190
297
|
// 65
|
|
191
|
-
// hello, lispy
|
|
192
|
-
//
|
|
298
|
+
// hello, lispy
|
|
299
|
+
// 0
|
|
300
|
+
// 1
|
|
301
|
+
// 2
|
|
302
|
+
// 3
|
|
303
|
+
// 4
|
|
304
|
+
// result: hello, lispy
|
|
193
305
|
// {
|
|
194
306
|
// "+": "<function>",
|
|
195
307
|
// "*": "<function>",
|
|
196
308
|
// "-": "<function>",
|
|
197
309
|
// "/": "<function>",
|
|
310
|
+
// "inc": "<function>",
|
|
311
|
+
// "dec": "<function>",
|
|
312
|
+
// "null?": "<function>",
|
|
313
|
+
// "zero?": "<function>",
|
|
314
|
+
// "neg?": "<function>",
|
|
315
|
+
// "pos?": "<function>",
|
|
316
|
+
// "nan?": "<function>",
|
|
198
317
|
// "=": "<function>",
|
|
199
318
|
// "!=": "<function>",
|
|
200
319
|
// "<": "<function>",
|
|
201
320
|
// "<=": "<function>",
|
|
202
321
|
// ">=": "<function>",
|
|
203
322
|
// ">": "<function>",
|
|
323
|
+
// "T": true,
|
|
324
|
+
// "F": false,
|
|
325
|
+
// "null": null,
|
|
204
326
|
// "and": "<function>",
|
|
205
327
|
// "or": "<function>",
|
|
206
328
|
// "not": "<function>",
|
|
@@ -263,13 +385,15 @@ evalSource(SRC, {...ENV, name: "lispy"});
|
|
|
263
385
|
// "rad": "<function>",
|
|
264
386
|
// "step": "<function>",
|
|
265
387
|
// "smoothstep": "<function>",
|
|
266
|
-
// "
|
|
267
|
-
// "
|
|
388
|
+
// "get": "<function>",
|
|
389
|
+
// "set!": "<function>",
|
|
268
390
|
// "push": "<function>",
|
|
269
391
|
// "concat": "<function>",
|
|
270
392
|
// "count": "<function>",
|
|
271
393
|
// "first": "<function>",
|
|
272
394
|
// "next": "<function>",
|
|
395
|
+
// "str": "<function>",
|
|
396
|
+
// "join": "<function>",
|
|
273
397
|
// "lower": "<function>",
|
|
274
398
|
// "upper": "<function>",
|
|
275
399
|
// "capitalize": "<function>",
|
|
@@ -281,15 +405,22 @@ evalSource(SRC, {...ENV, name: "lispy"});
|
|
|
281
405
|
// "re-test": "<function>",
|
|
282
406
|
// "re-match": "<function>",
|
|
283
407
|
// "replace": "<function>",
|
|
408
|
+
// "identity": "<function>",
|
|
409
|
+
// "always": "<function>",
|
|
410
|
+
// "never": "<function>",
|
|
411
|
+
// "int": "<function>",
|
|
412
|
+
// "float": "<function>",
|
|
284
413
|
// "print": "<function>",
|
|
285
|
-
// "env": "<function>",
|
|
286
414
|
// "partial": "<function>",
|
|
287
415
|
// "partial2": "<function>",
|
|
288
416
|
// "comp": "<function>",
|
|
289
417
|
// "comp2": "<function>",
|
|
418
|
+
// "fnull?": "<function>",
|
|
290
419
|
// "reduce": "<function>",
|
|
291
420
|
// "map": "<function>",
|
|
292
|
-
// "filter": "<function>"
|
|
421
|
+
// "filter": "<function>",
|
|
422
|
+
// "name": "lispy",
|
|
423
|
+
// "greetings!": "<function>"
|
|
293
424
|
// }
|
|
294
425
|
```
|
|
295
426
|
|
|
@@ -304,10 +435,10 @@ If this project contributes to an academic publication, please cite it as:
|
|
|
304
435
|
title = "@thi.ng/lispy",
|
|
305
436
|
author = "Karsten Schmidt",
|
|
306
437
|
note = "https://thi.ng/lispy",
|
|
307
|
-
year =
|
|
438
|
+
year = 2023
|
|
308
439
|
}
|
|
309
440
|
```
|
|
310
441
|
|
|
311
442
|
## License
|
|
312
443
|
|
|
313
|
-
© 2025 Karsten Schmidt // Apache License 2.0
|
|
444
|
+
© 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,40 @@ 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
|
+
// rewriting operators:
|
|
108
|
+
// iteratively threads first child as FIRST arg of next child
|
|
109
|
+
// (-> a (+ b) (* c)) = (* (+ a b) c)
|
|
110
|
+
"->": __threadOp((res, [f, ...children]) => [f, res, ...children]),
|
|
111
|
+
// iteratively threads first child as LAST arg of next child
|
|
112
|
+
// (->> a (+ b) (* c)) = (* c (+ b a))
|
|
113
|
+
"->>": __threadOp((res, children) => [...children, res]),
|
|
114
|
+
env: (_, env) => JSON.stringify(
|
|
115
|
+
env,
|
|
116
|
+
(_2, val) => isFunction(val) ? "<function>" : val,
|
|
117
|
+
2
|
|
118
|
+
),
|
|
119
|
+
syms: (_, env) => Object.keys(env),
|
|
71
120
|
// add default/fallback implementation to allow calling functions defined in
|
|
72
121
|
// the environment (either externally or via `defn`)
|
|
73
122
|
[DEFAULT]: ([x, ...args], env) => {
|
|
@@ -88,11 +137,33 @@ const ENV = {
|
|
|
88
137
|
"-": __mathOp((acc, x) => acc - x, (x) => -x),
|
|
89
138
|
// prettier-ignore
|
|
90
139
|
"/": __mathOp((acc, x) => acc / x, (x) => 1 / x),
|
|
140
|
+
inc: (x) => x + 1,
|
|
141
|
+
dec: (x) => x - 1,
|
|
142
|
+
// predicates
|
|
143
|
+
"null?": (x) => x == null,
|
|
144
|
+
"zero?": (x) => x === 0,
|
|
145
|
+
"neg?": (x) => x < 0,
|
|
146
|
+
"pos?": (x) => x > 0,
|
|
147
|
+
"nan?": (x) => isNaN(x),
|
|
91
148
|
// comparisons
|
|
92
149
|
...OPERATORS,
|
|
93
150
|
// boolean logic
|
|
94
|
-
|
|
95
|
-
|
|
151
|
+
T: true,
|
|
152
|
+
F: false,
|
|
153
|
+
null: null,
|
|
154
|
+
and: (...args) => {
|
|
155
|
+
let x;
|
|
156
|
+
for (x of args) {
|
|
157
|
+
if (!x) return false;
|
|
158
|
+
}
|
|
159
|
+
return x;
|
|
160
|
+
},
|
|
161
|
+
or: (...args) => {
|
|
162
|
+
for (let x of args) {
|
|
163
|
+
if (x) return x;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
},
|
|
96
167
|
not: (x) => !x,
|
|
97
168
|
// binary
|
|
98
169
|
"<<": (x, y) => x << y,
|
|
@@ -157,9 +228,9 @@ const ENV = {
|
|
|
157
228
|
rad,
|
|
158
229
|
step,
|
|
159
230
|
smoothstep: smoothStep,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
push: (list, ...x) => list.push(...x),
|
|
231
|
+
get: (arr, i) => arr[i],
|
|
232
|
+
"set!": (arr, i, x) => arr[i] = x,
|
|
233
|
+
push: (list, ...x) => (list.push(...x), list),
|
|
163
234
|
concat: (list, ...x) => list.concat(...x),
|
|
164
235
|
// returns length of first argument (presumably a list or string)
|
|
165
236
|
// (e.g. `(count "abc")` => 3)
|
|
@@ -170,6 +241,8 @@ const ENV = {
|
|
|
170
241
|
// there're no further items
|
|
171
242
|
next: (arg) => arg.length > 1 ? arg.slice(1) : void 0,
|
|
172
243
|
// strings
|
|
244
|
+
str: (...args) => args.join(""),
|
|
245
|
+
join: (sep, args) => args.join(sep),
|
|
173
246
|
lower,
|
|
174
247
|
upper,
|
|
175
248
|
capitalize,
|
|
@@ -181,13 +254,14 @@ const ENV = {
|
|
|
181
254
|
"re-test": (regexp, x) => regexp.test(x),
|
|
182
255
|
"re-match": (regexp, x) => regexp.exec(x),
|
|
183
256
|
replace: (x, regexp, replacement) => x.replace(regexp, replacement),
|
|
257
|
+
// functions
|
|
258
|
+
identity,
|
|
259
|
+
always,
|
|
260
|
+
never,
|
|
261
|
+
int: parseInt,
|
|
262
|
+
float: parseFloat,
|
|
184
263
|
// misc
|
|
185
|
-
print: console.log
|
|
186
|
-
env: () => JSON.stringify(
|
|
187
|
-
ENV,
|
|
188
|
-
(_, val) => isFunction(val) ? "<function>" : val,
|
|
189
|
-
2
|
|
190
|
-
)
|
|
264
|
+
print: console.log
|
|
191
265
|
};
|
|
192
266
|
const evalSource = (src, env = ENV) => parse(src).children.reduce((_, x) => interpret(x, env), void 0);
|
|
193
267
|
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; functional composition for 2-arg fns\n(defn comp2 (f g) (fn (x y z) (f (g x y) z)))\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";
|
|
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; functional composition for 2-arg fns\n(defn comp2 (f g) (fn (x y z) (f (g x y) z)))\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
|
@@ -11,6 +11,9 @@ const KERNEL = `
|
|
|
11
11
|
; functional composition for 2-arg fns
|
|
12
12
|
(defn comp2 (f g) (fn (x y z) (f (g x y) z)))
|
|
13
13
|
|
|
14
|
+
; calls f if x is nullish
|
|
15
|
+
(defn fnull? (x f) (if (null? x) (f) x))
|
|
16
|
+
|
|
14
17
|
; list reduction
|
|
15
18
|
(defn reduce (f acc xs) (if xs (reduce f (f acc (first xs)) (next xs)) acc))
|
|
16
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/lispy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Lightweight, extensible, interpreted Lisp-style DSL for embedding in other projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@thi.ng/errors": "^2.5.33",
|
|
47
47
|
"@thi.ng/math": "^5.11.27",
|
|
48
48
|
"@thi.ng/object-utils": "^1.1.23",
|
|
49
|
-
"@thi.ng/sexpr": "^1.0.
|
|
49
|
+
"@thi.ng/sexpr": "^1.0.17",
|
|
50
50
|
"@thi.ng/strings": "^3.9.12"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
@@ -55,6 +55,19 @@
|
|
|
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": "0108278642f57ca9fc7bed0edc967991c20cd594\n"
|
|
87
100
|
}
|