conjure-js 0.0.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/conjure +0 -0
- package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/assets/editor.worker-CdQrwHl8.js +26 -0
- package/dist/assets/main-A7ZMId9A.css +1 -0
- package/dist/assets/main-CmI-7epE.js +3137 -0
- package/dist/index.html +195 -0
- package/dist/vite.svg +1 -0
- package/package.json +68 -0
- package/src/bin/__fixtures__/smoke/app/lib.clj +4 -0
- package/src/bin/__fixtures__/smoke/app/main.clj +4 -0
- package/src/bin/__fixtures__/smoke/repl-smoke.ts +12 -0
- package/src/bin/bencode.ts +205 -0
- package/src/bin/cli.ts +250 -0
- package/src/bin/nrepl-utils.ts +59 -0
- package/src/bin/nrepl.ts +393 -0
- package/src/bin/version.ts +4 -0
- package/src/clojure/core.clj +620 -0
- package/src/clojure/core.clj.d.ts +189 -0
- package/src/clojure/demo/math.clj +16 -0
- package/src/clojure/demo/math.clj.d.ts +4 -0
- package/src/clojure/demo.clj +42 -0
- package/src/clojure/demo.clj.d.ts +0 -0
- package/src/clojure/generated/builtin-namespace-registry.ts +14 -0
- package/src/clojure/generated/clojure-core-source.ts +623 -0
- package/src/clojure/generated/clojure-string-source.ts +196 -0
- package/src/clojure/string.clj +192 -0
- package/src/clojure/string.clj.d.ts +25 -0
- package/src/core/assertions.ts +134 -0
- package/src/core/conversions.ts +108 -0
- package/src/core/core-env.ts +58 -0
- package/src/core/env.ts +78 -0
- package/src/core/errors.ts +39 -0
- package/src/core/evaluator/apply.ts +114 -0
- package/src/core/evaluator/arity.ts +174 -0
- package/src/core/evaluator/collections.ts +25 -0
- package/src/core/evaluator/destructure.ts +247 -0
- package/src/core/evaluator/dispatch.ts +73 -0
- package/src/core/evaluator/evaluate.ts +100 -0
- package/src/core/evaluator/expand.ts +79 -0
- package/src/core/evaluator/index.ts +72 -0
- package/src/core/evaluator/quasiquote.ts +87 -0
- package/src/core/evaluator/recur-check.ts +109 -0
- package/src/core/evaluator/special-forms.ts +517 -0
- package/src/core/factories.ts +155 -0
- package/src/core/gensym.ts +9 -0
- package/src/core/index.ts +76 -0
- package/src/core/positions.ts +38 -0
- package/src/core/printer.ts +86 -0
- package/src/core/reader.ts +559 -0
- package/src/core/scanners.ts +93 -0
- package/src/core/session.ts +610 -0
- package/src/core/stdlib/arithmetic.ts +361 -0
- package/src/core/stdlib/atoms.ts +88 -0
- package/src/core/stdlib/collections.ts +784 -0
- package/src/core/stdlib/errors.ts +81 -0
- package/src/core/stdlib/hof.ts +307 -0
- package/src/core/stdlib/meta.ts +48 -0
- package/src/core/stdlib/predicates.ts +240 -0
- package/src/core/stdlib/regex.ts +238 -0
- package/src/core/stdlib/strings.ts +311 -0
- package/src/core/stdlib/transducers.ts +256 -0
- package/src/core/stdlib/utils.ts +287 -0
- package/src/core/tokenizer.ts +437 -0
- package/src/core/transformations.ts +75 -0
- package/src/core/types.ts +258 -0
- package/src/main.ts +1 -0
- package/src/monaco-esm.d.ts +7 -0
- package/src/playground/clojure-tokens.ts +67 -0
- package/src/playground/editor.worker.ts +5 -0
- package/src/playground/find-form.ts +138 -0
- package/src/playground/playground.ts +342 -0
- package/src/playground/samples/00-welcome.clj +385 -0
- package/src/playground/samples/01-collections.clj +191 -0
- package/src/playground/samples/02-higher-order-functions.clj +215 -0
- package/src/playground/samples/03-destructuring.clj +194 -0
- package/src/playground/samples/04-strings-and-regex.clj +202 -0
- package/src/playground/samples/05-error-handling.clj +212 -0
- package/src/repl/repl.ts +116 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Auto-generated from src/clojure/string.clj — do not edit directly.
|
|
2
|
+
// Re-generate with: npm run gen:core-source
|
|
3
|
+
export const clojure_stringSource = `\
|
|
4
|
+
(ns clojure.string)
|
|
5
|
+
|
|
6
|
+
;; Runtime-injected native helpers. Declared here so clojure-lsp can resolve
|
|
7
|
+
;; them; the interpreter treats bare (def name) as a no-op and leaves the
|
|
8
|
+
;; native binding from coreEnv intact.
|
|
9
|
+
(def str-split*)
|
|
10
|
+
(def str-upper-case*)
|
|
11
|
+
(def str-lower-case*)
|
|
12
|
+
(def str-trim*)
|
|
13
|
+
(def str-triml*)
|
|
14
|
+
(def str-trimr*)
|
|
15
|
+
(def str-reverse*)
|
|
16
|
+
(def str-starts-with*)
|
|
17
|
+
(def str-ends-with*)
|
|
18
|
+
(def str-includes*)
|
|
19
|
+
(def str-index-of*)
|
|
20
|
+
(def str-last-index-of*)
|
|
21
|
+
(def str-replace*)
|
|
22
|
+
(def str-replace-first*)
|
|
23
|
+
|
|
24
|
+
;; ---------------------------------------------------------------------------
|
|
25
|
+
;; Joining / splitting
|
|
26
|
+
;; ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
(defn join
|
|
29
|
+
"Returns a string of all elements in coll, as returned by (str), separated
|
|
30
|
+
by an optional separator."
|
|
31
|
+
([coll] (join "" coll))
|
|
32
|
+
([separator coll]
|
|
33
|
+
(if (nil? coll)
|
|
34
|
+
""
|
|
35
|
+
(reduce
|
|
36
|
+
(fn [acc x]
|
|
37
|
+
(if (= acc "")
|
|
38
|
+
(str x)
|
|
39
|
+
(str acc separator x)))
|
|
40
|
+
""
|
|
41
|
+
coll))))
|
|
42
|
+
|
|
43
|
+
(defn split
|
|
44
|
+
"Splits string on a regular expression. Optional limit is the maximum number
|
|
45
|
+
of parts returned. Trailing empty strings are not returned by default; pass
|
|
46
|
+
a limit of -1 to return all."
|
|
47
|
+
([s sep] (str-split* s sep))
|
|
48
|
+
([s sep limit] (str-split* s sep limit)))
|
|
49
|
+
|
|
50
|
+
(defn split-lines
|
|
51
|
+
"Splits s on \\\\n or \\\\r\\\\n. Trailing empty lines are not returned."
|
|
52
|
+
[s]
|
|
53
|
+
(split s #"\\r?\\n"))
|
|
54
|
+
|
|
55
|
+
;; ---------------------------------------------------------------------------
|
|
56
|
+
;; Case conversion
|
|
57
|
+
;; ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
(defn upper-case
|
|
60
|
+
"Converts string to all upper-case."
|
|
61
|
+
[s]
|
|
62
|
+
(str-upper-case* s))
|
|
63
|
+
|
|
64
|
+
(defn lower-case
|
|
65
|
+
"Converts string to all lower-case."
|
|
66
|
+
[s]
|
|
67
|
+
(str-lower-case* s))
|
|
68
|
+
|
|
69
|
+
(defn capitalize
|
|
70
|
+
"Converts first character of the string to upper-case, all other
|
|
71
|
+
characters to lower-case."
|
|
72
|
+
[s]
|
|
73
|
+
(if (< (count s) 2)
|
|
74
|
+
(upper-case s)
|
|
75
|
+
(str (upper-case (subs s 0 1)) (lower-case (subs s 1)))))
|
|
76
|
+
|
|
77
|
+
;; ---------------------------------------------------------------------------
|
|
78
|
+
;; Trimming
|
|
79
|
+
;; ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
(defn trim
|
|
82
|
+
"Removes whitespace from both ends of string."
|
|
83
|
+
[s]
|
|
84
|
+
(str-trim* s))
|
|
85
|
+
|
|
86
|
+
(defn triml
|
|
87
|
+
"Removes whitespace from the left side of string."
|
|
88
|
+
[s]
|
|
89
|
+
(str-triml* s))
|
|
90
|
+
|
|
91
|
+
(defn trimr
|
|
92
|
+
"Removes whitespace from the right side of string."
|
|
93
|
+
[s]
|
|
94
|
+
(str-trimr* s))
|
|
95
|
+
|
|
96
|
+
(defn trim-newline
|
|
97
|
+
"Removes all trailing newline \\\\n or return \\\\r characters from string.
|
|
98
|
+
Similar to Perl's chomp."
|
|
99
|
+
[s]
|
|
100
|
+
(replace s #"[\\r\\n]+$" ""))
|
|
101
|
+
|
|
102
|
+
;; ---------------------------------------------------------------------------
|
|
103
|
+
;; Predicates
|
|
104
|
+
;; ---------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
(defn blank?
|
|
107
|
+
"True if s is nil, empty, or contains only whitespace."
|
|
108
|
+
[s]
|
|
109
|
+
(or (nil? s) (not (nil? (re-matches #"\\s*" s)))))
|
|
110
|
+
|
|
111
|
+
(defn starts-with?
|
|
112
|
+
"True if s starts with substr."
|
|
113
|
+
[s substr]
|
|
114
|
+
(str-starts-with* s substr))
|
|
115
|
+
|
|
116
|
+
(defn ends-with?
|
|
117
|
+
"True if s ends with substr."
|
|
118
|
+
[s substr]
|
|
119
|
+
(str-ends-with* s substr))
|
|
120
|
+
|
|
121
|
+
(defn includes?
|
|
122
|
+
"True if s includes substr."
|
|
123
|
+
[s substr]
|
|
124
|
+
(str-includes* s substr))
|
|
125
|
+
|
|
126
|
+
;; ---------------------------------------------------------------------------
|
|
127
|
+
;; Search
|
|
128
|
+
;; ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
(defn index-of
|
|
131
|
+
"Return index of value (string) in s, optionally searching forward from
|
|
132
|
+
from-index. Return nil if value not found."
|
|
133
|
+
([s value] (str-index-of* s value))
|
|
134
|
+
([s value from-index] (str-index-of* s value from-index)))
|
|
135
|
+
|
|
136
|
+
(defn last-index-of
|
|
137
|
+
"Return last index of value (string) in s, optionally searching backward
|
|
138
|
+
from from-index. Return nil if value not found."
|
|
139
|
+
([s value] (str-last-index-of* s value))
|
|
140
|
+
([s value from-index] (str-last-index-of* s value from-index)))
|
|
141
|
+
|
|
142
|
+
;; ---------------------------------------------------------------------------
|
|
143
|
+
;; Replacement
|
|
144
|
+
;; ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
(defn replace
|
|
147
|
+
"Replaces all instances of match with replacement in s.
|
|
148
|
+
|
|
149
|
+
match/replacement can be:
|
|
150
|
+
string / string — literal match, literal replacement
|
|
151
|
+
pattern / string — regex match; $1, $2, etc. substituted from groups
|
|
152
|
+
pattern / fn — regex match; fn called with match (string or vector
|
|
153
|
+
of [whole g1 g2 ...]), return value used as replacement.
|
|
154
|
+
|
|
155
|
+
See also replace-first."
|
|
156
|
+
[s match replacement]
|
|
157
|
+
(str-replace* s match replacement))
|
|
158
|
+
|
|
159
|
+
(defn replace-first
|
|
160
|
+
"Replaces the first instance of match with replacement in s.
|
|
161
|
+
Same match/replacement semantics as replace."
|
|
162
|
+
[s match replacement]
|
|
163
|
+
(str-replace-first* s match replacement))
|
|
164
|
+
|
|
165
|
+
(defn re-quote-replacement
|
|
166
|
+
"Given a replacement string that you wish to be a literal replacement for a
|
|
167
|
+
pattern match in replace or replace-first, escape any special replacement
|
|
168
|
+
characters ($ signs) so they are treated literally."
|
|
169
|
+
[s]
|
|
170
|
+
(replace s #"\\$" "$$$$"))
|
|
171
|
+
|
|
172
|
+
;; ---------------------------------------------------------------------------
|
|
173
|
+
;; Miscellaneous
|
|
174
|
+
;; ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
(defn reverse
|
|
177
|
+
"Returns s with its characters reversed."
|
|
178
|
+
[s]
|
|
179
|
+
(str-reverse* s))
|
|
180
|
+
|
|
181
|
+
(defn escape
|
|
182
|
+
"Return a new string, using cmap to escape each character ch from s as
|
|
183
|
+
follows: if (cmap ch) is nil, append ch to the new string; otherwise append
|
|
184
|
+
(str (cmap ch)).
|
|
185
|
+
|
|
186
|
+
cmap may be a map or a function. Maps are callable directly (IFn semantics).
|
|
187
|
+
|
|
188
|
+
Note: Clojure uses char literal keys (e.g. {\\\\< \\"<\\"}). This interpreter
|
|
189
|
+
has no char type, so map keys must be single-character strings instead
|
|
190
|
+
(e.g. {\\"<\\" \\"<\\"})."
|
|
191
|
+
[s cmap]
|
|
192
|
+
(apply str (map (fn [c]
|
|
193
|
+
(let [r (cmap c)]
|
|
194
|
+
(if (nil? r) c (str r))))
|
|
195
|
+
(split s #""))))
|
|
196
|
+
`
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
(ns clojure.string)
|
|
2
|
+
|
|
3
|
+
;; Runtime-injected native helpers. Declared here so clojure-lsp can resolve
|
|
4
|
+
;; them; the interpreter treats bare (def name) as a no-op and leaves the
|
|
5
|
+
;; native binding from coreEnv intact.
|
|
6
|
+
(def str-split*)
|
|
7
|
+
(def str-upper-case*)
|
|
8
|
+
(def str-lower-case*)
|
|
9
|
+
(def str-trim*)
|
|
10
|
+
(def str-triml*)
|
|
11
|
+
(def str-trimr*)
|
|
12
|
+
(def str-reverse*)
|
|
13
|
+
(def str-starts-with*)
|
|
14
|
+
(def str-ends-with*)
|
|
15
|
+
(def str-includes*)
|
|
16
|
+
(def str-index-of*)
|
|
17
|
+
(def str-last-index-of*)
|
|
18
|
+
(def str-replace*)
|
|
19
|
+
(def str-replace-first*)
|
|
20
|
+
|
|
21
|
+
;; ---------------------------------------------------------------------------
|
|
22
|
+
;; Joining / splitting
|
|
23
|
+
;; ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
(defn join
|
|
26
|
+
"Returns a string of all elements in coll, as returned by (str), separated
|
|
27
|
+
by an optional separator."
|
|
28
|
+
([coll] (join "" coll))
|
|
29
|
+
([separator coll]
|
|
30
|
+
(if (nil? coll)
|
|
31
|
+
""
|
|
32
|
+
(reduce
|
|
33
|
+
(fn [acc x]
|
|
34
|
+
(if (= acc "")
|
|
35
|
+
(str x)
|
|
36
|
+
(str acc separator x)))
|
|
37
|
+
""
|
|
38
|
+
coll))))
|
|
39
|
+
|
|
40
|
+
(defn split
|
|
41
|
+
"Splits string on a regular expression. Optional limit is the maximum number
|
|
42
|
+
of parts returned. Trailing empty strings are not returned by default; pass
|
|
43
|
+
a limit of -1 to return all."
|
|
44
|
+
([s sep] (str-split* s sep))
|
|
45
|
+
([s sep limit] (str-split* s sep limit)))
|
|
46
|
+
|
|
47
|
+
(defn split-lines
|
|
48
|
+
"Splits s on \\n or \\r\\n. Trailing empty lines are not returned."
|
|
49
|
+
[s]
|
|
50
|
+
(split s #"\r?\n"))
|
|
51
|
+
|
|
52
|
+
;; ---------------------------------------------------------------------------
|
|
53
|
+
;; Case conversion
|
|
54
|
+
;; ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
(defn upper-case
|
|
57
|
+
"Converts string to all upper-case."
|
|
58
|
+
[s]
|
|
59
|
+
(str-upper-case* s))
|
|
60
|
+
|
|
61
|
+
(defn lower-case
|
|
62
|
+
"Converts string to all lower-case."
|
|
63
|
+
[s]
|
|
64
|
+
(str-lower-case* s))
|
|
65
|
+
|
|
66
|
+
(defn capitalize
|
|
67
|
+
"Converts first character of the string to upper-case, all other
|
|
68
|
+
characters to lower-case."
|
|
69
|
+
[s]
|
|
70
|
+
(if (< (count s) 2)
|
|
71
|
+
(upper-case s)
|
|
72
|
+
(str (upper-case (subs s 0 1)) (lower-case (subs s 1)))))
|
|
73
|
+
|
|
74
|
+
;; ---------------------------------------------------------------------------
|
|
75
|
+
;; Trimming
|
|
76
|
+
;; ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
(defn trim
|
|
79
|
+
"Removes whitespace from both ends of string."
|
|
80
|
+
[s]
|
|
81
|
+
(str-trim* s))
|
|
82
|
+
|
|
83
|
+
(defn triml
|
|
84
|
+
"Removes whitespace from the left side of string."
|
|
85
|
+
[s]
|
|
86
|
+
(str-triml* s))
|
|
87
|
+
|
|
88
|
+
(defn trimr
|
|
89
|
+
"Removes whitespace from the right side of string."
|
|
90
|
+
[s]
|
|
91
|
+
(str-trimr* s))
|
|
92
|
+
|
|
93
|
+
(defn trim-newline
|
|
94
|
+
"Removes all trailing newline \\n or return \\r characters from string.
|
|
95
|
+
Similar to Perl's chomp."
|
|
96
|
+
[s]
|
|
97
|
+
(replace s #"[\r\n]+$" ""))
|
|
98
|
+
|
|
99
|
+
;; ---------------------------------------------------------------------------
|
|
100
|
+
;; Predicates
|
|
101
|
+
;; ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
(defn blank?
|
|
104
|
+
"True if s is nil, empty, or contains only whitespace."
|
|
105
|
+
[s]
|
|
106
|
+
(or (nil? s) (not (nil? (re-matches #"\s*" s)))))
|
|
107
|
+
|
|
108
|
+
(defn starts-with?
|
|
109
|
+
"True if s starts with substr."
|
|
110
|
+
[s substr]
|
|
111
|
+
(str-starts-with* s substr))
|
|
112
|
+
|
|
113
|
+
(defn ends-with?
|
|
114
|
+
"True if s ends with substr."
|
|
115
|
+
[s substr]
|
|
116
|
+
(str-ends-with* s substr))
|
|
117
|
+
|
|
118
|
+
(defn includes?
|
|
119
|
+
"True if s includes substr."
|
|
120
|
+
[s substr]
|
|
121
|
+
(str-includes* s substr))
|
|
122
|
+
|
|
123
|
+
;; ---------------------------------------------------------------------------
|
|
124
|
+
;; Search
|
|
125
|
+
;; ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
(defn index-of
|
|
128
|
+
"Return index of value (string) in s, optionally searching forward from
|
|
129
|
+
from-index. Return nil if value not found."
|
|
130
|
+
([s value] (str-index-of* s value))
|
|
131
|
+
([s value from-index] (str-index-of* s value from-index)))
|
|
132
|
+
|
|
133
|
+
(defn last-index-of
|
|
134
|
+
"Return last index of value (string) in s, optionally searching backward
|
|
135
|
+
from from-index. Return nil if value not found."
|
|
136
|
+
([s value] (str-last-index-of* s value))
|
|
137
|
+
([s value from-index] (str-last-index-of* s value from-index)))
|
|
138
|
+
|
|
139
|
+
;; ---------------------------------------------------------------------------
|
|
140
|
+
;; Replacement
|
|
141
|
+
;; ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
(defn replace
|
|
144
|
+
"Replaces all instances of match with replacement in s.
|
|
145
|
+
|
|
146
|
+
match/replacement can be:
|
|
147
|
+
string / string — literal match, literal replacement
|
|
148
|
+
pattern / string — regex match; $1, $2, etc. substituted from groups
|
|
149
|
+
pattern / fn — regex match; fn called with match (string or vector
|
|
150
|
+
of [whole g1 g2 ...]), return value used as replacement.
|
|
151
|
+
|
|
152
|
+
See also replace-first."
|
|
153
|
+
[s match replacement]
|
|
154
|
+
(str-replace* s match replacement))
|
|
155
|
+
|
|
156
|
+
(defn replace-first
|
|
157
|
+
"Replaces the first instance of match with replacement in s.
|
|
158
|
+
Same match/replacement semantics as replace."
|
|
159
|
+
[s match replacement]
|
|
160
|
+
(str-replace-first* s match replacement))
|
|
161
|
+
|
|
162
|
+
(defn re-quote-replacement
|
|
163
|
+
"Given a replacement string that you wish to be a literal replacement for a
|
|
164
|
+
pattern match in replace or replace-first, escape any special replacement
|
|
165
|
+
characters ($ signs) so they are treated literally."
|
|
166
|
+
[s]
|
|
167
|
+
(replace s #"\$" "$$$$"))
|
|
168
|
+
|
|
169
|
+
;; ---------------------------------------------------------------------------
|
|
170
|
+
;; Miscellaneous
|
|
171
|
+
;; ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
(defn reverse
|
|
174
|
+
"Returns s with its characters reversed."
|
|
175
|
+
[s]
|
|
176
|
+
(str-reverse* s))
|
|
177
|
+
|
|
178
|
+
(defn escape
|
|
179
|
+
"Return a new string, using cmap to escape each character ch from s as
|
|
180
|
+
follows: if (cmap ch) is nil, append ch to the new string; otherwise append
|
|
181
|
+
(str (cmap ch)).
|
|
182
|
+
|
|
183
|
+
cmap may be a map or a function. Maps are callable directly (IFn semantics).
|
|
184
|
+
|
|
185
|
+
Note: Clojure uses char literal keys (e.g. {\\< \"<\"}). This interpreter
|
|
186
|
+
has no char type, so map keys must be single-character strings instead
|
|
187
|
+
(e.g. {\"<\" \"<\"})."
|
|
188
|
+
[s cmap]
|
|
189
|
+
(apply str (map (fn [c]
|
|
190
|
+
(let [r (cmap c)]
|
|
191
|
+
(if (nil? r) c (str r))))
|
|
192
|
+
(split s #""))))
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function join(coll: unknown): unknown;
|
|
2
|
+
export function join(separator: unknown, coll: unknown): unknown;
|
|
3
|
+
export function split(s: unknown, sep: unknown): unknown;
|
|
4
|
+
export function split(s: unknown, sep: unknown, limit: unknown): unknown;
|
|
5
|
+
export function split_lines(s: unknown): unknown;
|
|
6
|
+
export function upper_case(s: unknown): unknown;
|
|
7
|
+
export function lower_case(s: unknown): unknown;
|
|
8
|
+
export function capitalize(s: unknown): unknown;
|
|
9
|
+
export function trim(s: unknown): unknown;
|
|
10
|
+
export function triml(s: unknown): unknown;
|
|
11
|
+
export function trimr(s: unknown): unknown;
|
|
12
|
+
export function trim_newline(s: unknown): unknown;
|
|
13
|
+
export function blank_QMARK_(s: unknown): unknown;
|
|
14
|
+
export function starts_with_QMARK_(s: unknown, substr: unknown): unknown;
|
|
15
|
+
export function ends_with_QMARK_(s: unknown, substr: unknown): unknown;
|
|
16
|
+
export function includes_QMARK_(s: unknown, substr: unknown): unknown;
|
|
17
|
+
export function index_of(s: unknown, value: unknown): unknown;
|
|
18
|
+
export function index_of(s: unknown, value: unknown, from_index: unknown): unknown;
|
|
19
|
+
export function last_index_of(s: unknown, value: unknown): unknown;
|
|
20
|
+
export function last_index_of(s: unknown, value: unknown, from_index: unknown): unknown;
|
|
21
|
+
export function replace(s: unknown, match: unknown, replacement: unknown): unknown;
|
|
22
|
+
export function replace_first(s: unknown, match: unknown, replacement: unknown): unknown;
|
|
23
|
+
export function re_quote_replacement(s: unknown): unknown;
|
|
24
|
+
export function reverse(s: unknown): unknown;
|
|
25
|
+
export function escape(s: unknown, cmap: unknown): unknown;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
valueKeywords,
|
|
3
|
+
type CljAtom,
|
|
4
|
+
type CljBoolean,
|
|
5
|
+
type CljFunction,
|
|
6
|
+
type CljKeyword,
|
|
7
|
+
type CljList,
|
|
8
|
+
type CljMacro,
|
|
9
|
+
type CljMap,
|
|
10
|
+
type CljMultiMethod,
|
|
11
|
+
type CljNativeFunction,
|
|
12
|
+
type CljNumber,
|
|
13
|
+
type CljReduced,
|
|
14
|
+
type CljRegex,
|
|
15
|
+
type CljString,
|
|
16
|
+
type CljSymbol,
|
|
17
|
+
type CljValue,
|
|
18
|
+
type CljVector,
|
|
19
|
+
type CljVolatile,
|
|
20
|
+
} from './types.ts'
|
|
21
|
+
import { specialFormKeywords } from './evaluator/special-forms.ts'
|
|
22
|
+
|
|
23
|
+
export const isNil = (value: CljValue): boolean => value.kind === 'nil'
|
|
24
|
+
export const isFalsy = (value: CljValue): boolean => {
|
|
25
|
+
if (value.kind === 'nil') return true
|
|
26
|
+
if (value.kind === 'boolean') return !value.value
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
export const isTruthy = (value: CljValue): boolean => {
|
|
30
|
+
return !isFalsy(value)
|
|
31
|
+
}
|
|
32
|
+
export const isSpecialForm = (
|
|
33
|
+
value: CljValue
|
|
34
|
+
): value is CljSymbol & { name: keyof typeof specialFormKeywords } =>
|
|
35
|
+
value.kind === 'symbol' && value.name in specialFormKeywords
|
|
36
|
+
export const isSymbol = (value: CljValue): value is CljSymbol =>
|
|
37
|
+
value.kind === 'symbol'
|
|
38
|
+
export const isVector = (value: CljValue): value is CljVector =>
|
|
39
|
+
value.kind === 'vector'
|
|
40
|
+
export const isList = (value: CljValue): value is CljList =>
|
|
41
|
+
value.kind === 'list'
|
|
42
|
+
export const isFunction = (value: CljValue): value is CljFunction =>
|
|
43
|
+
value.kind === 'function'
|
|
44
|
+
export const isNativeFunction = (value: CljValue): value is CljNativeFunction =>
|
|
45
|
+
value.kind === 'native-function'
|
|
46
|
+
export const isMacro = (value: CljValue): value is CljMacro =>
|
|
47
|
+
value.kind === 'macro'
|
|
48
|
+
export const isMap = (value: CljValue): value is CljMap => value.kind === 'map'
|
|
49
|
+
export const isKeyword = (value: CljValue): value is CljKeyword =>
|
|
50
|
+
value.kind === 'keyword'
|
|
51
|
+
export const isAFunction = (
|
|
52
|
+
value: CljValue
|
|
53
|
+
): value is CljFunction | CljNativeFunction =>
|
|
54
|
+
isFunction(value) || isNativeFunction(value)
|
|
55
|
+
|
|
56
|
+
/** True for any value that can be invoked like a function (IFn). */
|
|
57
|
+
export const isCallable = (value: CljValue): boolean =>
|
|
58
|
+
isAFunction(value) || isKeyword(value) || isMap(value)
|
|
59
|
+
export const isMultiMethod = (value: CljValue): value is CljMultiMethod =>
|
|
60
|
+
value.kind === 'multi-method'
|
|
61
|
+
export const isAtom = (value: CljValue): value is CljAtom =>
|
|
62
|
+
value.kind === 'atom'
|
|
63
|
+
export const isReduced = (value: CljValue): value is CljReduced =>
|
|
64
|
+
value.kind === 'reduced'
|
|
65
|
+
export const isVolatile = (value: CljValue): value is CljVolatile =>
|
|
66
|
+
value.kind === 'volatile'
|
|
67
|
+
export const isRegex = (value: CljValue): value is CljRegex =>
|
|
68
|
+
value.kind === 'regex'
|
|
69
|
+
export const isCollection = (
|
|
70
|
+
value: CljValue
|
|
71
|
+
): value is CljList | CljVector | CljMap =>
|
|
72
|
+
isVector(value) || isMap(value) || isList(value)
|
|
73
|
+
|
|
74
|
+
export const isSeqable = (
|
|
75
|
+
value: CljValue
|
|
76
|
+
): value is CljList | CljVector | CljMap | CljString =>
|
|
77
|
+
isCollection(value) || value.kind === 'string'
|
|
78
|
+
|
|
79
|
+
export const isCljValue = (value: any): value is CljValue => {
|
|
80
|
+
return (
|
|
81
|
+
typeof value === 'object' &&
|
|
82
|
+
value !== null &&
|
|
83
|
+
'kind' in value &&
|
|
84
|
+
value.kind in valueKeywords
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const equalityHandlers = {
|
|
89
|
+
[valueKeywords.number]: (a: CljNumber, b: CljNumber) => a.value === b.value,
|
|
90
|
+
[valueKeywords.string]: (a: CljString, b: CljString) => a.value === b.value,
|
|
91
|
+
[valueKeywords.boolean]: (a: CljBoolean, b: CljBoolean) =>
|
|
92
|
+
a.value === b.value,
|
|
93
|
+
[valueKeywords.nil]: () => true,
|
|
94
|
+
[valueKeywords.symbol]: (a: CljSymbol, b: CljSymbol) => a.name === b.name,
|
|
95
|
+
[valueKeywords.keyword]: (a: CljKeyword, b: CljKeyword) => a.name === b.name,
|
|
96
|
+
[valueKeywords.vector]: (a: CljVector, b: CljVector) => {
|
|
97
|
+
if (a.value.length !== b.value.length) return false
|
|
98
|
+
return a.value.every((value, index) => isEqual(value, b.value[index]))
|
|
99
|
+
},
|
|
100
|
+
[valueKeywords.map]: (a: CljMap, b: CljMap) => {
|
|
101
|
+
if (a.entries.length !== b.entries.length) return false
|
|
102
|
+
const uniqueKeys = new Set([
|
|
103
|
+
...a.entries.map(([key]) => key),
|
|
104
|
+
...b.entries.map(([key]) => key),
|
|
105
|
+
])
|
|
106
|
+
for (const key of uniqueKeys) {
|
|
107
|
+
const aEntry = a.entries.find(([k]) => isEqual(k, key))
|
|
108
|
+
if (!aEntry) return false
|
|
109
|
+
const bEntry = b.entries.find(([k]) => isEqual(k, key))
|
|
110
|
+
if (!bEntry) return false
|
|
111
|
+
if (!isEqual(aEntry[1], bEntry[1])) return false
|
|
112
|
+
}
|
|
113
|
+
return true
|
|
114
|
+
},
|
|
115
|
+
[valueKeywords.list]: (a: CljList, b: CljList) => {
|
|
116
|
+
if (a.value.length !== b.value.length) return false
|
|
117
|
+
return a.value.every((value, index) => isEqual(value, b.value[index]))
|
|
118
|
+
},
|
|
119
|
+
[valueKeywords.atom]: (a: CljAtom, b: CljAtom) => a === b,
|
|
120
|
+
[valueKeywords.reduced]: (a: CljReduced, b: CljReduced) =>
|
|
121
|
+
isEqual(a.value, b.value),
|
|
122
|
+
[valueKeywords.volatile]: (a: CljVolatile, b: CljVolatile) => a === b,
|
|
123
|
+
// Regex uses reference equality matching Clojure Pattern semantics:
|
|
124
|
+
// (= #"foo" #"foo") => false — each literal is a distinct object
|
|
125
|
+
[valueKeywords.regex]: (a: CljRegex, b: CljRegex) => a === b,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const isEqual = (a: CljValue, b: CljValue): boolean => {
|
|
129
|
+
if (a.kind !== b.kind) return false
|
|
130
|
+
|
|
131
|
+
const handler = equalityHandlers[a.kind as keyof typeof equalityHandlers]
|
|
132
|
+
if (!handler) return false
|
|
133
|
+
return handler(a as never, b as never)
|
|
134
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isCljValue } from './assertions'
|
|
2
|
+
import { applyFunction } from './evaluator'
|
|
3
|
+
import {
|
|
4
|
+
cljBoolean,
|
|
5
|
+
cljKeyword,
|
|
6
|
+
cljMap,
|
|
7
|
+
cljNativeFunction,
|
|
8
|
+
cljNil,
|
|
9
|
+
cljNumber,
|
|
10
|
+
cljString,
|
|
11
|
+
cljVector,
|
|
12
|
+
} from './factories'
|
|
13
|
+
import type { CljValue } from './types'
|
|
14
|
+
|
|
15
|
+
export class ConversionError extends Error {
|
|
16
|
+
context: unknown
|
|
17
|
+
constructor(message: string, context?: unknown) {
|
|
18
|
+
super(message)
|
|
19
|
+
this.name = 'ConversionError'
|
|
20
|
+
this.context = context
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const richKeyKinds = new Set(['list', 'vector', 'map'])
|
|
25
|
+
|
|
26
|
+
export function cljToJs(value: CljValue): unknown {
|
|
27
|
+
switch (value.kind) {
|
|
28
|
+
case 'number':
|
|
29
|
+
return value.value
|
|
30
|
+
case 'string':
|
|
31
|
+
return value.value
|
|
32
|
+
case 'boolean':
|
|
33
|
+
return value.value
|
|
34
|
+
case 'nil':
|
|
35
|
+
return null
|
|
36
|
+
case 'keyword':
|
|
37
|
+
return value.name.startsWith(':') ? value.name.slice(1) : value.name
|
|
38
|
+
case 'symbol':
|
|
39
|
+
return value.name
|
|
40
|
+
case 'list':
|
|
41
|
+
case 'vector':
|
|
42
|
+
return value.value.map(cljToJs)
|
|
43
|
+
case 'map': {
|
|
44
|
+
const obj: Record<string, unknown> = {}
|
|
45
|
+
for (const [k, v] of value.entries) {
|
|
46
|
+
if (richKeyKinds.has(k.kind)) {
|
|
47
|
+
throw new ConversionError(
|
|
48
|
+
`Rich key types (${k.kind}) are not supported in JS object conversion. Restructure your map to use string, keyword, or number keys.`,
|
|
49
|
+
{ key: k, value: v }
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
const jsKey = String(cljToJs(k))
|
|
53
|
+
obj[jsKey] = cljToJs(v)
|
|
54
|
+
}
|
|
55
|
+
return obj
|
|
56
|
+
}
|
|
57
|
+
case 'function':
|
|
58
|
+
case 'native-function': {
|
|
59
|
+
const fn = value
|
|
60
|
+
return (...jsArgs: unknown[]) => {
|
|
61
|
+
const cljArgs = jsArgs.map(jsToClj)
|
|
62
|
+
const result = applyFunction(fn, cljArgs)
|
|
63
|
+
return cljToJs(result)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
case 'macro':
|
|
67
|
+
throw new ConversionError(
|
|
68
|
+
'Macros cannot be exported to JavaScript. Macros are compile-time constructs.',
|
|
69
|
+
{ macro: value }
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function jsToClj(value: unknown): CljValue {
|
|
75
|
+
if (value === null || value === undefined) return cljNil()
|
|
76
|
+
if (isCljValue(value)) return value
|
|
77
|
+
|
|
78
|
+
switch (typeof value) {
|
|
79
|
+
case 'number':
|
|
80
|
+
return cljNumber(value)
|
|
81
|
+
case 'string':
|
|
82
|
+
return cljString(value)
|
|
83
|
+
case 'boolean':
|
|
84
|
+
return cljBoolean(value)
|
|
85
|
+
case 'function': {
|
|
86
|
+
const jsFn = value as (...args: unknown[]) => unknown
|
|
87
|
+
return cljNativeFunction('js-fn', (...cljArgs: CljValue[]) => {
|
|
88
|
+
const jsArgs = cljArgs.map(cljToJs)
|
|
89
|
+
const result = jsFn(...jsArgs)
|
|
90
|
+
return jsToClj(result)
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
case 'object': {
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
return cljVector(value.map(jsToClj))
|
|
96
|
+
}
|
|
97
|
+
const entries: [CljValue, CljValue][] = Object.entries(
|
|
98
|
+
value as Record<string, unknown>
|
|
99
|
+
).map(([k, v]) => [cljKeyword(`:${k}`), jsToClj(v)])
|
|
100
|
+
return cljMap(entries)
|
|
101
|
+
}
|
|
102
|
+
default:
|
|
103
|
+
throw new ConversionError(
|
|
104
|
+
`Cannot convert JS value of type ${typeof value} to CljValue`,
|
|
105
|
+
{ value }
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
}
|