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,212 @@
|
|
|
1
|
+
(ns user.errors)
|
|
2
|
+
|
|
3
|
+
;; Deep Dive: Error Handling
|
|
4
|
+
;;
|
|
5
|
+
;; Press ⌘+Enter on any form to evaluate it.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
;; try / catch / finally
|
|
9
|
+
|
|
10
|
+
(comment
|
|
11
|
+
;; No error — returns the value of the body
|
|
12
|
+
(try
|
|
13
|
+
(+ 1 2)) ;; => 3
|
|
14
|
+
|
|
15
|
+
;; catch with :default — catches anything
|
|
16
|
+
(try
|
|
17
|
+
(/ 1 0)
|
|
18
|
+
(catch :default e
|
|
19
|
+
(str "Caught: " (ex-message e))))
|
|
20
|
+
|
|
21
|
+
;; finally — always runs, does NOT change the return value
|
|
22
|
+
(try
|
|
23
|
+
(+ 1 2)
|
|
24
|
+
(finally
|
|
25
|
+
(println "always runs"))) ;; prints, returns 3
|
|
26
|
+
|
|
27
|
+
(try
|
|
28
|
+
(/ 1 0)
|
|
29
|
+
(catch :default e
|
|
30
|
+
(println "handling error")
|
|
31
|
+
:recovered)
|
|
32
|
+
(finally
|
|
33
|
+
(println "cleanup"))) ;; => :recovered
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
;; throw
|
|
38
|
+
;;
|
|
39
|
+
;; You can throw any value — not just error objects.
|
|
40
|
+
|
|
41
|
+
(comment
|
|
42
|
+
(try
|
|
43
|
+
(throw "something went wrong")
|
|
44
|
+
(catch string? e
|
|
45
|
+
(str "got a string: " e)))
|
|
46
|
+
|
|
47
|
+
(try
|
|
48
|
+
(throw :not-found)
|
|
49
|
+
(catch keyword? e
|
|
50
|
+
(str "got a keyword: " e)))
|
|
51
|
+
|
|
52
|
+
(try
|
|
53
|
+
(throw 42)
|
|
54
|
+
(catch number? e
|
|
55
|
+
(str "got a number: " (+ e 1))))
|
|
56
|
+
|
|
57
|
+
(try
|
|
58
|
+
(throw {:type :validation :field :email :msg "invalid"})
|
|
59
|
+
(catch map? e
|
|
60
|
+
(str "validation error on " (:field e))))
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
;; Catch Discriminators
|
|
65
|
+
;;
|
|
66
|
+
;; The catch clause tests the thrown value with a discriminator:
|
|
67
|
+
;;
|
|
68
|
+
;; :default — catches everything
|
|
69
|
+
;; :error/runtime — catches evaluator errors (type errors, etc.)
|
|
70
|
+
;; keyword — checks (= thrown discriminator)
|
|
71
|
+
;; predicate fn — checks (pred thrown-value)
|
|
72
|
+
|
|
73
|
+
(comment
|
|
74
|
+
(defn find-user [id]
|
|
75
|
+
(if (pos? id)
|
|
76
|
+
{:id id :name "Alice"}
|
|
77
|
+
(throw :user/not-found)))
|
|
78
|
+
|
|
79
|
+
(try
|
|
80
|
+
(find-user -1)
|
|
81
|
+
(catch :user/not-found _
|
|
82
|
+
"User not found — check the id"))
|
|
83
|
+
|
|
84
|
+
;; Multiple catch clauses — matched in order
|
|
85
|
+
(defn risky [x]
|
|
86
|
+
(cond
|
|
87
|
+
(string? x) (throw :bad-type)
|
|
88
|
+
(neg? x) (throw :negative)
|
|
89
|
+
:else (/ 100 x)))
|
|
90
|
+
|
|
91
|
+
(try
|
|
92
|
+
(risky -5)
|
|
93
|
+
(catch :bad-type _ "wrong type")
|
|
94
|
+
(catch :negative _ "negative number")
|
|
95
|
+
(catch :default e (str "unexpected: " e)))
|
|
96
|
+
|
|
97
|
+
(try (risky "oops") (catch :bad-type _ "wrong type") (catch :negative _ "neg"))
|
|
98
|
+
(try (risky 0) (catch :default e (ex-message e)))
|
|
99
|
+
|
|
100
|
+
;; :error/runtime — catches interpreter-level errors
|
|
101
|
+
(try
|
|
102
|
+
(+ 1 "not a number")
|
|
103
|
+
(catch :error/runtime e
|
|
104
|
+
(str "type error caught: " (ex-message e))))
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
;; ex-info: Structured Errors
|
|
109
|
+
;;
|
|
110
|
+
;; `ex-info` creates an error with a message AND a data map.
|
|
111
|
+
|
|
112
|
+
(comment
|
|
113
|
+
(try
|
|
114
|
+
(throw (ex-info "User validation failed"
|
|
115
|
+
{:field :email
|
|
116
|
+
:value "not-an-email"
|
|
117
|
+
:code :invalid-format}))
|
|
118
|
+
(catch :default e
|
|
119
|
+
{:message (ex-message e)
|
|
120
|
+
:data (ex-data e)}))
|
|
121
|
+
|
|
122
|
+
;; ex-info with a cause (chained errors)
|
|
123
|
+
(try
|
|
124
|
+
(try
|
|
125
|
+
(/ 1 0)
|
|
126
|
+
(catch :default cause
|
|
127
|
+
(throw (ex-info "Database query failed"
|
|
128
|
+
{:query "SELECT *"}
|
|
129
|
+
cause))))
|
|
130
|
+
(catch :default e
|
|
131
|
+
{:message (ex-message e)
|
|
132
|
+
:data (ex-data e)
|
|
133
|
+
:cause (ex-message (ex-cause e))}))
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
;; Keyword-typed Errors with ex-info
|
|
138
|
+
;;
|
|
139
|
+
;; Attach a :type keyword to the ex-info map, then catch by that keyword.
|
|
140
|
+
|
|
141
|
+
(defn parse-age [x]
|
|
142
|
+
(cond
|
|
143
|
+
(not (number? x))
|
|
144
|
+
(throw (ex-info "Not a number" {:value x :type :error/parse}))
|
|
145
|
+
|
|
146
|
+
(neg? x)
|
|
147
|
+
(throw (ex-info "Age cannot be negative" {:value x :type :error/validation}))
|
|
148
|
+
|
|
149
|
+
:else x))
|
|
150
|
+
|
|
151
|
+
(comment
|
|
152
|
+
(try
|
|
153
|
+
(parse-age "hello")
|
|
154
|
+
(catch :error/parse e
|
|
155
|
+
(str "Parse error: " (ex-message e) " (got: " (:value (ex-data e)) ")"))
|
|
156
|
+
(catch :error/validation e
|
|
157
|
+
(str "Validation error: " (ex-message e))))
|
|
158
|
+
|
|
159
|
+
(try
|
|
160
|
+
(parse-age -5)
|
|
161
|
+
(catch :error/parse e (str "parse: " (ex-message e)))
|
|
162
|
+
(catch :error/validation e (str "validation: " (ex-message e))))
|
|
163
|
+
|
|
164
|
+
(parse-age 30) ;; => 30 (no error)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
;; Practical Patterns
|
|
169
|
+
|
|
170
|
+
(comment
|
|
171
|
+
;; Result map {ok? result/error}
|
|
172
|
+
(defn safe-divide [a b]
|
|
173
|
+
(try
|
|
174
|
+
{:ok? true :result (/ a b)}
|
|
175
|
+
(catch :default e
|
|
176
|
+
{:ok? false :error (ex-message e)})))
|
|
177
|
+
|
|
178
|
+
(safe-divide 10 2) ;; => {:ok? true :result 5}
|
|
179
|
+
(safe-divide 10 0) ;; => {:ok? false :error "..."}
|
|
180
|
+
|
|
181
|
+
;; Validate before computing
|
|
182
|
+
(defn sqrt [n]
|
|
183
|
+
(when (neg? n)
|
|
184
|
+
(throw (ex-info "Cannot take sqrt of negative number"
|
|
185
|
+
{:value n :type :error/domain})))
|
|
186
|
+
(loop [x (* 0.5 (+ 1.0 n))]
|
|
187
|
+
(let [next-x (* 0.5 (+ x (/ n x)))
|
|
188
|
+
diff (max (- next-x x) (- x next-x))]
|
|
189
|
+
(if (< diff 1e-9)
|
|
190
|
+
next-x
|
|
191
|
+
(recur next-x)))))
|
|
192
|
+
|
|
193
|
+
(try (sqrt 9) (catch :default e (ex-message e))) ;; => 3.0
|
|
194
|
+
(try (sqrt -1) (catch :error/domain e (ex-message e)))
|
|
195
|
+
|
|
196
|
+
;; Wrapping external errors with context
|
|
197
|
+
(defn load-user [id]
|
|
198
|
+
(try
|
|
199
|
+
(if (= id 42)
|
|
200
|
+
{:id 42 :name "Alice"}
|
|
201
|
+
(throw (ex-info "User not found" {:id id :type :error/not-found})))
|
|
202
|
+
(catch :error/not-found e
|
|
203
|
+
(throw (ex-info (str "Failed to load profile for id=" id)
|
|
204
|
+
{:id id :type :error/load-failed}
|
|
205
|
+
e)))))
|
|
206
|
+
|
|
207
|
+
(try
|
|
208
|
+
(load-user 99)
|
|
209
|
+
(catch :error/load-failed e
|
|
210
|
+
{:msg (ex-message e)
|
|
211
|
+
:cause (ex-message (ex-cause e))}))
|
|
212
|
+
)
|
package/src/repl/repl.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { EvaluationError } from '../core/errors'
|
|
2
|
+
import { printString } from '../core/printer'
|
|
3
|
+
import { createSession } from '../core/session'
|
|
4
|
+
import type { Session } from '../core/session'
|
|
5
|
+
|
|
6
|
+
export type ReplEntrySource = {
|
|
7
|
+
kind: 'source'
|
|
8
|
+
text: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ReplEntryResult = {
|
|
12
|
+
kind: 'result'
|
|
13
|
+
output: string
|
|
14
|
+
durationMs: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ReplEntryError = {
|
|
18
|
+
kind: 'error'
|
|
19
|
+
source: string
|
|
20
|
+
message: string
|
|
21
|
+
durationMs: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type ReplEntryOutput = {
|
|
25
|
+
kind: 'output'
|
|
26
|
+
text: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type ReplEntry =
|
|
30
|
+
| ReplEntrySource
|
|
31
|
+
| ReplEntryResult
|
|
32
|
+
| ReplEntryError
|
|
33
|
+
| ReplEntryOutput
|
|
34
|
+
|
|
35
|
+
export interface ReplState {
|
|
36
|
+
session: Session
|
|
37
|
+
/** Submitted expression history for Up/Down navigation */
|
|
38
|
+
history: string[]
|
|
39
|
+
/** Rendered output entries */
|
|
40
|
+
entries: ReplEntry[]
|
|
41
|
+
/** Output collector for current evaluation */
|
|
42
|
+
outputs: string[]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function makeSession(addOutput: (text: string) => void): Session {
|
|
46
|
+
return createSession({ output: addOutput })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function makeRepl(): ReplState {
|
|
50
|
+
const state: ReplState = {
|
|
51
|
+
session: undefined as unknown as Session,
|
|
52
|
+
history: [],
|
|
53
|
+
entries: [],
|
|
54
|
+
outputs: [],
|
|
55
|
+
}
|
|
56
|
+
state.session = makeSession((text) => state.outputs.push(text))
|
|
57
|
+
return state
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function evalSource(state: ReplState, source: string): ReplEntry[] {
|
|
61
|
+
const trimmed = source.trim()
|
|
62
|
+
if (!trimmed) return []
|
|
63
|
+
|
|
64
|
+
state.history.push(trimmed)
|
|
65
|
+
|
|
66
|
+
// Clear outputs from previous evaluation
|
|
67
|
+
state.outputs = []
|
|
68
|
+
const start = performance.now()
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const result = state.session.evaluate(trimmed)
|
|
72
|
+
const end = performance.now()
|
|
73
|
+
|
|
74
|
+
// Build entries in correct order: source, outputs, result
|
|
75
|
+
const entries: ReplEntry[] = []
|
|
76
|
+
|
|
77
|
+
entries.push({ kind: 'source', text: trimmed })
|
|
78
|
+
|
|
79
|
+
for (const text of state.outputs) {
|
|
80
|
+
entries.push({ kind: 'output', text })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
entries.push({
|
|
84
|
+
kind: 'result',
|
|
85
|
+
output: printString(result),
|
|
86
|
+
durationMs: end - start,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
state.entries.push(...entries)
|
|
90
|
+
return entries
|
|
91
|
+
} catch (e) {
|
|
92
|
+
const end = performance.now()
|
|
93
|
+
const entry = makeErrorEntry(trimmed, e, end - start)
|
|
94
|
+
state.entries.push(entry)
|
|
95
|
+
return [entry]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function makeErrorEntry(
|
|
100
|
+
source: string,
|
|
101
|
+
e: unknown,
|
|
102
|
+
durationMs: number
|
|
103
|
+
): ReplEntryError {
|
|
104
|
+
const message =
|
|
105
|
+
e instanceof EvaluationError || e instanceof Error ? e.message : String(e)
|
|
106
|
+
return { kind: 'error', source, message, durationMs }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function resetEnv(state: ReplState): void {
|
|
110
|
+
state.outputs = []
|
|
111
|
+
state.session = makeSession((text) => state.outputs.push(text))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getAllForms(state: ReplState): string {
|
|
115
|
+
return state.history.join('\n')
|
|
116
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"types": [
|
|
8
|
+
"vite/client",
|
|
9
|
+
"node",
|
|
10
|
+
"bun"
|
|
11
|
+
],
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
|
|
14
|
+
/* Bundler mode */
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"allowImportingTsExtensions": true,
|
|
17
|
+
"allowArbitraryExtensions": true,
|
|
18
|
+
"verbatimModuleSyntax": true,
|
|
19
|
+
"moduleDetection": "force",
|
|
20
|
+
"noEmit": true,
|
|
21
|
+
|
|
22
|
+
/* Linting */
|
|
23
|
+
"strict": true,
|
|
24
|
+
"noUnusedLocals": true,
|
|
25
|
+
"noUnusedParameters": true,
|
|
26
|
+
"erasableSyntaxOnly": true,
|
|
27
|
+
"noFallthroughCasesInSwitch": true,
|
|
28
|
+
"noUncheckedSideEffectImports": true
|
|
29
|
+
},
|
|
30
|
+
"include": ["src"]
|
|
31
|
+
}
|