@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-05-20T19:06:30Z
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] This DSL implementation has been extracted as standalone package from
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
- The core language is intentionally kept extremely minimal and currently only
35
- contains the following:
36
-
37
- - basic math ops (multi-arity)
38
- - logic operators (multi-arity)
39
- - `and`
40
- - `or`
41
- - `not`
42
- - bitwise operators
43
- - `<<`
44
- - `>>` / `>>>`
45
- - `bit-and`
46
- - `bit-or`
47
- - `bit-xor`
48
- - `bit-not`
49
- - comparison operators
50
- - constants & functions from JS-native `Math`
51
- - selected utilities from [thi.ng/math](https://thi.ng/math) package
52
- - `HALF_PI`
53
- - `TAU`
54
- - `clamp`: clamp value to interval
55
- - `deg`: convert radians to degrees
56
- - `fit`: fit value from one interval into another
57
- - `mix`: linear interpolation (same as GLSL `mix()`)
58
- - `rad`: convert degrees to radians
59
- - `step`: same as GLSL `step()`
60
- - `smoothstep`: same as GLSL `smoothstep()`
61
- - basic FP programming constructs
62
- - `comp`: functional composition
63
- - `def`: define global symbol
64
- - `defn`: define global function
65
- - `let`: locally scope var bindings/expression
66
- - `partial`: partial application (currying)
67
- - basic list/array processing
68
- - `aget`: get array index
69
- - `aset`: set array index
70
- - `count`: number of items
71
- - `concat`: same as `Array.prototype.concat(...)`
72
- - `filter`: list/array filtering
73
- - `first`: first item of array
74
- - `map`: list/array transformation
75
- - `next`: remaining array items (after first)
76
- - `push`: same as `Array.prototype.push(...)`
77
- - `reduce`: list/array reduction
78
- - string/regexp processing
79
- - `capitalize`
80
- - `lower`
81
- - `upper`
82
- - `pad-left`
83
- - `pad-right`
84
- - `substr`
85
- - `trim`
86
- - `regexp`
87
- - `re-match`
88
- - `re-test`
89
- - `replace`
90
- - misc utilities
91
- - `print`: aka `console.log(...)`
92
- - `env`: JSON stringified version of current root env
93
-
94
- The core language can be easily customized/extended by defining new items in the
95
- root environment `ENV` (see example below).
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
- **ALPHA** - bleeding edge / work-in-progress
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.50 KB
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 FFI in the root environment
163
- // (here it's actually the same as default print fn)
164
- ENV.print = console.log;
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 will be provided externally)
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
- evalSource(SRC, {...ENV, name: "lispy"});
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
- // "aget": "<function>",
267
- // "aset": "<function>",
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 = 2025
452
+ year = 2023
308
453
  }
309
454
  ```
310
455
 
311
456
  ## License
312
457
 
313
- &copy; 2025 Karsten Schmidt // Apache License 2.0
458
+ &copy; 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
- * {@link evalExpressions} and can be used to define extensions of the core
12
- * language. Functions defined here can receive any number of arguments.
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 pairs = args.children;
56
- assert(
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
- and: (...args) => args.every((x) => !!x),
95
- or: (...args) => args.some((x) => !!x),
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
- aget: (arr, i) => arr[i],
161
- aset: (arr, i, x) => arr[i] = x,
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; 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(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
- ; functional composition for 2-arg fns
12
- (defn comp2 (f g) (fn (x y z) (f (g x y) z)))
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.1.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.27",
43
- "@thi.ng/checks": "^3.7.7",
44
- "@thi.ng/compare": "^2.4.19",
45
- "@thi.ng/defmulti": "^3.0.67",
46
- "@thi.ng/errors": "^2.5.33",
47
- "@thi.ng/math": "^5.11.27",
48
- "@thi.ng/object-utils": "^1.1.23",
49
- "@thi.ng/sexpr": "^1.0.16",
50
- "@thi.ng/strings": "^3.9.12"
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.3",
54
- "typedoc": "^0.28.3",
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": "alpha",
84
- "year": 2025
96
+ "status": "beta",
97
+ "year": 2023
85
98
  },
86
- "gitHead": "90ae29324123738dde721193785a6743cdf4181e\n"
99
+ "gitHead": "61c3833b7ef7d044621454b5ea4af885d39f065e\n"
87
100
  }