devabhasha 1.0.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/README.md ADDED
@@ -0,0 +1,974 @@
1
+ # देवभाषा · Devabhāṣā
2
+
3
+ A small **Sanskrit programming language that transpiles to JavaScript**, with a
4
+ web/DOM layer and a zero-build browser playground. Source is written in
5
+ Devanagari; output is readable JavaScript.
6
+
7
+ ```
8
+ कार्य अभिवादनम् (नाम) { function abhivaadanam(naama) {
9
+ फलम् "नमस्ते, " + नाम। → return ("namaste, " + naama);
10
+ } }
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ node src/cli.js run examples/namaste.deva # compile + execute (Node)
17
+ node src/cli.js build examples/ganaka.deva # → examples/ganaka.js
18
+ node build-playground.js # → playground/index.html
19
+ node test/test.js # run the test suite
20
+ ```
21
+
22
+ Open `playground/index.html` in any browser — no server needed.
23
+
24
+ ## Language reference
25
+
26
+ | Sanskrit | Meaning | Compiles to |
27
+ |----------|---------|-------------|
28
+ | `चर` / `नियत` | varies / fixed | `let` / `const` |
29
+ | `कार्य` | work to be done | `function` (statement **or** expression) |
30
+ | `फलम्` | fruit / result | `return` |
31
+ | `यदि` / `अन्यथा` | if / otherwise | `if` / `else` |
32
+ | `यावत्` | as long as | `while` |
33
+ | `प्रत्येकम् (x : समूह)` | for each | `for (const x of …)` |
34
+ | `भङ्ग` / `अनुवृत्तम्` | break / continue | `break` / `continue` |
35
+ | `सत्यम्` / `असत्यम्` / `शून्यम्` | true / false / void | `true` / `false` / `null` |
36
+ | `दर्शय(…)` | cause to show | `console.log(…)` |
37
+ | `अङ्गम्(tag, …)` | limb / part | `document.createElement` + children |
38
+ | `योजय(node[, target])` | join / attach | append to DOM |
39
+ | `श्रोता(node, ev, fn)` | listener | `addEventListener` |
40
+
41
+ - Statements end with the **danda** `।` or `;` (both optional before `}`/EOF).
42
+ - Comments begin with `#`.
43
+ - Numbers may use Devanagari digits (`०१२…`) or ASCII.
44
+ - Identifiers may be Devanagari; they're transliterated to stable ASCII in the
45
+ output (`गणना` → `gannanaa`), so the JS is portable and debuggable.
46
+
47
+ ## कारक — Pāṇinian case-role construction
48
+
49
+ The signature feature. Sanskrit marks a word's grammatical role with its
50
+ **vibhakti (case ending)**, not its position. Devabhāṣā uses this for
51
+ **order-free DOM construction** via the verb `रचय` (racaya, "construct").
52
+
53
+ ```
54
+ रचय पटः वाक्यम् "वर्धय" स्पर्शाय करणेन वर्धक।
55
+ │ │ │ │
56
+ │ │ │ └ करण/instrumental → handler
57
+ │ │ └ सम्प्रदान/dative → event ("click")
58
+ │ └ कर्म/accusative → content
59
+ └ कर्तृ/nominative → tag (<button>)
60
+ ```
61
+
62
+ Each argument's **role comes from its ending**, so the arguments may be
63
+ written in **any order** and compile to identical JavaScript:
64
+
65
+ ```
66
+ __DB.construct({ tag: "button", content: "वर्धय", event: "click", handler: वर्धक })
67
+ ```
68
+
69
+ The seven recognized cases and their kāraka roles:
70
+
71
+ | Case (vibhakti) | Kāraka | DOM slot |
72
+ |-----------------|--------|----------|
73
+ | प्रथमा nominative `-ः` | कर्तृ agent | element tag |
74
+ | द्वितीया accusative `-म्` | कर्म patient | content |
75
+ | तृतीया instrumental `-ेन` | करण instrument | handler |
76
+ | चतुर्थी dative `-ाय` | सम्प्रदान recipient | event |
77
+ | पञ्चमी ablative `-ात्` | अपादान source | data source |
78
+ | षष्ठी genitive `-स्य` | सम्बन्ध relation | attribute |
79
+ | सप्तमी locative `-े` | अधिकरण locus | mount parent |
80
+
81
+ **Scope (honest):** the case engine (`src/vibhakti.js`) implements the
82
+ a-stem (अकारान्त) singular paradigm — the most common declension, covering
83
+ most coined technical vocabulary. Other stem-classes and genders extend it
84
+ by adding rows to the paradigm table. ASCII words and uninflected stems are
85
+ correctly *not* treated as case-marked, which is what keeps free word order
86
+ parseable.
87
+
88
+ Tag and event vocabulary lives in `src/karaka-web.js` (`पट`→button,
89
+ `शीर्ष`→h1, `स्पर्श`→click, …).
90
+
91
+ ## समास — compound composition (nested DOM trees)
92
+
93
+ Where kāraka handles *one* element's roles, **समास (samāsa, "compound")**
94
+ handles how elements **nest** — the structural counterpart. A block form
95
+
96
+ ```
97
+ रचय <tag-nominative> { ...children... }
98
+ ```
99
+
100
+ is a **तत्पुरुष** (container) whose body is a **द्वन्द्व** (sibling list).
101
+ It composes recursively, so DOM trees of any depth are just nested `रचय`
102
+ blocks. Container tags use the nominative (`मूलः`=div, `सूचीः`=ul); content
103
+ uses the accusative marker `वाक्यम्`:
104
+
105
+ ```
106
+ चर पृष्ठम् = रचय मूलः {
107
+ रचय शीर्षः वाक्यम् "देवभाषा"।
108
+ रचय वाक्यः वाक्यम् "संस्कृतेन रचितम्।"।
109
+ रचय सूचीः {
110
+ रचय पङ्क्तिः वाक्यम् "प्रथमम्"।
111
+ रचय पङ्क्तिः वाक्यम् "द्वितीयम्"।
112
+ }
113
+ }।
114
+ योजय(पृष्ठम्)।
115
+ ```
116
+
117
+ compiles to nested `__DB.construct({ tag, children: [...] })` and renders:
118
+
119
+ ```html
120
+ <div><h1>देवभाषा</h1><p>संस्कृतेन रचितम्।</p>
121
+ <ul><li>प्रथमम्</li><li>द्वितीयम्</li></ul></div>
122
+ ```
123
+
124
+ kāraka and समās compose: a child element can carry its own case-marked
125
+ slots (an event handler, attributes) while sitting in the tree. Children
126
+ attach by DOM `append`, which *moves* nodes, so the model stays correct
127
+ whether or not a child was auto-mounted.
128
+
129
+ ## रूप — CSS-in-Sanskrit styling
130
+
131
+ `रूप` (rūpa, "form/appearance") attaches styles to an element, with **both
132
+ property names and value words in Sanskrit**:
133
+
134
+ ```
135
+ रचय पटः वाक्यम् "स्पृश" रूप {
136
+ वर्णः: श्वेतः, # color: white
137
+ पृष्ठभूमिः: रक्तः, # backgroundColor: crimson
138
+ अन्तरालः: "10px 20px", # padding (quoted CSS passes through)
139
+ कोणवृत्तिः: "6px", # borderRadius
140
+ सीमा: अदृश्यम् # border: none
141
+ }।
142
+ ```
143
+
144
+ The vocabulary (`src/style.js`) covers color (`वर्णः`), background
145
+ (`पृष्ठभूमिः`), typography (`अक्षरमानम्`/font-size, `अक्षरभारः`/font-weight,
146
+ `संरेखणम्`/text-align), the box model (`अन्तरालः`/padding,
147
+ `बाह्यान्तरः`/margin, `कोणवृत्तिः`/border-radius), and fl/layout
148
+ (`प्रदर्शनम्`/display, `दिक्`/flex-direction, `न्यायः`/justify). Value words
149
+ include colors (`रक्तः`→crimson, `नीलः`→navy, `केसरः`→#F4C430) and keywords
150
+ (`केन्द्रम्`→center, `प्रवाहः`→flex, `गुरुः`→bold).
151
+
152
+ Resolution rules: a **known Sanskrit value word** becomes a CSS literal; a
153
+ **bare variable** stays a reference (`वर्णः: मुख्यवर्णः` reads the variable);
154
+ a **quoted string** (`"18px"`) and an **unknown CSS property** (`cursor`)
155
+ pass through unchanged; an **expression** (`अक्षरमानम्: n + "px"`) is
156
+ emitted as-is. रूप composes with समās — whole styled trees are nested `रचय`
157
+ blocks each carrying their own `रूप`.
158
+
159
+ ## सूची-दत्तांशः — data rendering & named styles
160
+
161
+ **List rendering.** A समās child can be any expression that yields DOM
162
+ nodes, and array children are flattened — so mapping a data array with
163
+ `.प्रतिचित्रय` (map) renders a list:
164
+
165
+ ```
166
+ ग्रन्थाः.प्रतिचित्रय(कार्य(ग) {
167
+ फलम् रचय पङ्क्तिः वाक्यम् ग.नाम। # one <li> per data item
168
+ })
169
+ ```
170
+
171
+ placed inside a `रचय सूचीः { … }` produces a `<ul>` of `<li>`s, one per row.
172
+
173
+ **Named styles (रूपनाम).** Declare a reusable style once and apply it by
174
+ name, with optional per-element overrides:
175
+
176
+ ```
177
+ रूपनाम कार्डः = रूप { पृष्ठभूमिः: श्वेतः, अन्तरालः: "12px", कोणवृत्तिः: "8px" }।
178
+
179
+ रचय मूलः रूप कार्डः { … } # apply the named style
180
+ रचय मूलः रूप कार्डः { पृष्ठभूमिः: नीलः } { … } # named base + override
181
+ ```
182
+
183
+ `रूपनाम X = रूप {…}` compiles to `const X = {…CSS…}`; a reference compiles
184
+ to `Object.assign({}, X)`, and a base-plus-override to
185
+ `Object.assign({}, X, {…})` so overrides win. A subtle parser point: after a
186
+ named reference, `{` is disambiguated by lookahead — `{ word: … }` is a
187
+ style override, anything else is a समās children block.
188
+
189
+ Together these make real data-driven UIs (`examples/suchi.deva`): a styled
190
+ container whose children are a heading plus a mapped list of styled cards.
191
+
192
+ ## भाव — reactive state
193
+
194
+ `भाव` (bhāva, "state/condition") declares a reactive state cell, and
195
+ `दृश्य` (dṛśya, "view") declares a region that **re-renders automatically**
196
+ when any state it reads changes. The model is subscribe-on-read /
197
+ re-run-on-write: a view subscribes to every `भाव` it reads while rendering,
198
+ and a later write re-runs just that view.
199
+
200
+ ```
201
+ भाव गणकः = ०। # a reactive counter cell
202
+
203
+ दृश्य { # a self-updating view
204
+ रचय मूलः {
205
+ रचय शीर्षः वाक्यम् "गणना: " + गणकः। # reads गणकः → subscribes
206
+ रचय पटः वाक्यम् "वर्धय"
207
+ स्पर्शाय करणेन कार्य(){ गणकः++। }। # writes गणकः → re-renders
208
+ }
209
+ }
210
+ ```
211
+
212
+ The syntax is transparent: a `भाव` name reads as its value and assigns
213
+ normally — under the hood `गणकः` compiles to `गणकः()` (read), `गणकः = v`
214
+ to `गणकः(v)` (write), and `गणकः++` / `गणकः += n` desugar through the cell.
215
+ `दृश्य { … }` mounts to the page root by default, or `दृश्य (container) { … }`
216
+ to a chosen element. A view's final expression is what gets rendered.
217
+
218
+ Because a view is just a समās tree, reactivity composes with everything:
219
+ styled, data-mapped, event-wired trees that rebuild on state change.
220
+ `examples/todo.deva` is a complete reactive todo app — `भाव` task list,
221
+ `.प्रतिचित्रय` rendering each task, buttons that add/remove and re-render.
222
+
223
+ ### प्रभाव — fine-grained reactivity
224
+
225
+ `दृश्य` is *coarse*: when any cell it read changes, it re-runs and rebuilds
226
+ its whole subtree. That's simple and fine for small views, but it's not how
227
+ modern frontend frameworks (Solid, Svelte) achieve speed — they update only
228
+ the exact DOM bound to the changed value. Devabhāṣā now has that model too, as
229
+ a layer alongside `दृश्य`:
230
+
231
+ - `प्रभाव(कार्य(){ … })` (prabhāva, "influence/effect") runs the function once,
232
+ **tracks exactly which `भाव` cells it reads**, and re-runs *only that
233
+ function* when one of those cells changes — nothing else.
234
+ - `बन्ध(कार्य(){ … })` (bandha, "binding") returns a text node whose content
235
+ is kept in sync by an effect; when its dependencies change, **only that one
236
+ node's text updates** — no rebuild, no lost focus or scroll.
237
+
238
+ ```
239
+ भाव गणकः = ०।
240
+ चर नोड = बन्ध(कार्य(){ फलम् पाठ"गणना: {गणकः}"। })। # a live text node
241
+ गणकः = गणकः + १। # ONLY नोड's text updates — the surrounding DOM is untouched
242
+ ```
243
+
244
+ The engine is a real signal/effect system: a subscriber stack tracks the
245
+ current effect, each cell records its subscribers, and an effect cleans up its
246
+ old dependencies before every re-run — so conditional reads (`यदि (ध्वजः) {
247
+ … ब … }`) correctly drop a dependency when the branch stops taking it (the
248
+ classic stale-subscription bug, handled). `दृश्य` and `प्रभाव` coexist: the
249
+ coarse view is still there for simple cases, fine-grained for performance.
250
+
251
+ **Fine-grained is also automatic.** A `रचय` whose content is *dynamic* (reads
252
+ a `भाव` cell) and is used **outside** a `दृश्य` compiles straight to a bound
253
+ text node — no `बन्ध` call needed:
254
+
255
+ ```
256
+ भाव गणकः = ०।
257
+ चर शीर्षम् = रचय शीर्षः वाक्यम् पाठ"गणना: {गणकः}"। # dynamic content
258
+ गणकः = गणकः + १। # ONLY this node's text updates, in place
259
+ ```
260
+
261
+ The compiler detects the `भाव` read and emits a fine-grained binding; static
262
+ content (no `भाव` read) stays a plain value with zero overhead, and content
263
+ *inside* a `दृश्य` stays coarse so the two models never double-update. So you
264
+ get fine-grained performance by default, just by writing dynamic content.
265
+
266
+ ### घटकाः — components & props
267
+
268
+ A **component** is just a `कार्य` that takes props and returns a `रचय` tree —
269
+ no new syntax, because functions and `रचय` already compose. You call it like
270
+ any function and reuse it freely:
271
+
272
+ ```
273
+ कार्य पत्रम् (शीर्षकम्, मूल्यम्) {
274
+ फलम् रचय मूलः { रचय शीर्षः वाक्यम् शीर्षकम्। रचय वाक्यः वाक्यम् मूल्यम्। }।
275
+ }
276
+ चर अ = पत्रम्("नाम", "रामः")। # reuse with different props
277
+ चर ब = पत्रम्("वयः", "तिंशत्")।
278
+ ```
279
+
280
+ The one thing that needs care is **reactive props** — a prop that should stay
281
+ *live* across the call boundary. Passing a `भाव` cell normally reads its value
282
+ at the call site (a dead snapshot). Instead, wrap it in `सूत्र(…)` (sūtra,
283
+ "thread"), which passes a *reactive reference* — the component renders it and
284
+ it updates fine-grained when the cell changes, while only *that* node updates:
285
+
286
+ ```
287
+ भाव गणकः = ०।
288
+ चर कार्डः = आँकडापत्रम्("क्लिक्", सूत्र(गणकः), "navy")। # live reactive prop
289
+ गणकः = गणकः + १। # the card's value updates in place; siblings untouched
290
+ ```
291
+
292
+ `सूत्र` is **explicit on purpose** — reactivity crossing a boundary is visible
293
+ at the call site rather than implicit, matching the language's preference for
294
+ predictable over clever. Static props (plain values) and callback props
295
+ (passing a `कार्य` for, e.g., a click handler) work as ordinary arguments.
296
+ `examples/घटकाः.deva` composes a reusable stat-card component (used three
297
+ times) with reactive, static, and callback props.
298
+
299
+ ### आवली — keyed list rendering
300
+
301
+ Rendering a list with `.प्रतिचित्रय` inside a `दृश्य` rebuilds *every* row when
302
+ the data changes — losing each row's DOM state (focus, scroll, input values).
303
+ `आवली` (āvalī, "row/series") is **keyed reconciliation**: it identifies rows by
304
+ a stable key and, on each change, **reuses** the DOM nodes of surviving keys
305
+ (moving them if reordered), **builds** only genuinely new keys, and **removes**
306
+ vanished ones — the model Solid and Svelte use.
307
+
308
+ ```
309
+ भाव वस्तूनि = [कोष{ कुं: १, … }, कोष{ कुं: २, … }, …]।
310
+ चर पटः = आवली(
311
+ सूत्र(वस्तूनि), # the reactive data
312
+ कार्य(व){ फलम् व.कुं। }, # key — a stable identity per item
313
+ कार्य(व){ फलम् रचय वाक्यः वाक्यम् व.नाम। } # render one row
314
+ )।
315
+ ```
316
+
317
+ Because nodes are reused by key, **a row's DOM state moves with it** across a
318
+ reorder — the defining benefit of keyed rendering. The reconciler runs inside
319
+ an effect, so it re-runs only when the data signal changes, and it touches the
320
+ DOM minimally (no rebuild of unchanged rows). `examples/आवली.deva` is a
321
+ shuffling colored list where each row keeps its identity through reordering.
322
+
323
+ ### Reactive styles & cleanup
324
+
325
+ Fine-grained binding extends to **style properties**: a `रूप { }` value that
326
+ reads a `भाव` cell (outside a `दृश्य`) updates *just that one CSS property* in
327
+ place when the cell changes — no rebuild, the rest of the element untouched.
328
+
329
+ ```
330
+ भाव वर्णः = "tomato"।
331
+ चर पटः = रचय मूलः रूप { पृष्ठभूमिः: वर्णः, अन्तरालः: "40px" }। # bg is reactive
332
+ वर्णः = "steelblue"। # ONLY background-color updates
333
+ ```
334
+
335
+ Each dynamic property gets its own effect; static properties (and styles inside
336
+ a `दृश्य`) stay plain with zero overhead. The same applies to a `सक्रियः ?
337
+ रक्तः : धूसरः` ternary — the property re-evaluates fine-grained.
338
+
339
+ For teardown, `सफाई(कार्य(){ … })` (saphāī, "cleanup") registers a hook inside
340
+ a `प्रभाव` that runs **before the effect's next re-run** — the place to clear a
341
+ timer, remove a listener, or cancel a subscription so reactive code doesn't
342
+ leak:
343
+
344
+ ```
345
+ प्रभाव(कार्य(){
346
+ चर घटी = कालचक्र(कार्य(){ … }, १०००)।
347
+ सफाई(कार्य(){ कालनाशः(घटी)। })। # tear down the old timer before re-running
348
+ })।
349
+ ```
350
+
351
+ Refs need no special form — `रचय` already returns the DOM node (`चर न = रचय
352
+ …`), so you hold the handle directly and can read or mutate it.
353
+
354
+ ### आलस्यचित्रम् — lazy-loaded images
355
+
356
+ `आलस्यचित्रम्(src, opts)` (ālasya-citra, "lazy image") builds an `img` that
357
+ loads its real source only when it scrolls into view, via `IntersectionObserver`
358
+ — the standard technique for fast image-heavy pages. It shows `opts.placeholder`
359
+ (or nothing) until then, keeps the real URL in `data-src`, sets the native
360
+ `loading="lazy"` hint, and falls back to eager loading where the observer is
361
+ unavailable:
362
+
363
+ ```
364
+ चर चित्रम् = आलस्यचित्रम्(लेख.चित्रम्, कोष{
365
+ alt: लेख.शीर्षकम्,
366
+ placeholder: "data:image/svg+xml,…" # a tiny inline placeholder
367
+ })।
368
+ ```
369
+
370
+ `examples/ब्लॉग.deva` is a complete **blog website**: a post list whose cards are
371
+ components with lazy-loaded cover images, reactive view-switching between the
372
+ list and a full-post view (a `प्रभाव` effect rebuilding the content area when
373
+ the selected post changes), and a back button — composing components, effects,
374
+ and lazy images into a real app.
375
+
376
+ `examples/vyaya.deva` goes further — a reactive **expense tracker** with a
377
+ live computed total, category filter buttons (whose active colour comes from
378
+ a `सक्रियः ? रक्तः : धूसरः` ternary, color words resolved in-expression),
379
+ `.गालय` filtering, and add-entry interaction: several `भाव` cells and derived
380
+ views composing into one app.
381
+
382
+ `examples/flappy.deva` (विहगः) is a real-time **Flappy-Bird-style game** — a
383
+ different stress test entirely: an animation loop, continuous state mutation,
384
+ collision detection, and keyboard input. It uses three general-purpose host
385
+ bindings added for interactive programs: `कालचक्र(fn, ms)` (kālacakra, "wheel
386
+ of time" → a repeating timer / game loop), `कालनाशः(id)` (stop a timer), and
387
+ `कुञ्जिश्रोता(fn)` (kuñjiśrotā, "key-listener" → a keyboard handler receiving
388
+ the pressed key). The whole game state lives in one `भाव` cell that the loop
389
+ mutates ~30×/sec; absolute positioning uses the `शीर्षात्`/`वामतः`/`अधस्तात्`/
390
+ `दक्षिणतः` (top/left/bottom/right) and `स्तरः` (z-index) style properties.
391
+
392
+ Building it surfaced a real fix to the reactive model: a `भाव` cell skipped
393
+ re-rendering when written the same *reference* — fine for immutable updates,
394
+ but a game mutates its state object in place, so the view never refreshed. The
395
+ guard now skips only unchanged **primitives**; object/array state always
396
+ re-renders, which is the behavior mutable game loops (and most reactive code)
397
+ expect.
398
+
399
+ ## गणित — the math module
400
+
401
+ A comprehensive mathematics layer. Elementary and transcendental functions
402
+ come from a native bridge to JavaScript's `Math` (exact, full precision);
403
+ the statistics and number-theory layer (`examples/ganita.deva`) is written
404
+ *in Devabhāṣā* on top of them — the self-hosting payoff applied to a
405
+ standard library.
406
+
407
+ **Native bridge** — `गणित.<fn>(…)`:
408
+ `वर्गमूलम्` (sqrt), `घनमूलम्` (cbrt), `घातः` (pow), `निरपेक्षम्` (abs),
409
+ `अधःपातः`/`ऊर्ध्वपातः` (floor/ceil), `सन्निकर्षः` (round), `धनर्णचिह्नम्` (sign),
410
+ `ज्या`/`कोटिज्या`/`स्पर्शज्या` (sin/cos/tan), `विलोमज्या` etc. (asin…),
411
+ `घातीयम्` (exp), `लघुगणकः` (log), `अधिकतमः`/`न्यूनतमः` (max/min),
412
+ `यादृच्छिकम्` (random). Constants: `गणित.पाई` (π), `गणित.यूलरांकः` (e),
413
+ `गणित.मूलद्वि` (√2).
414
+
415
+ A nice piece of history: **ज्या (jyā) and कोटिज्या (koṭijyā) are the actual
416
+ classical Sanskrit names for sine and cosine** — the words that, via Arabic
417
+ *jiba → jayb*, became the Latin *sinus* and our "sine."
418
+
419
+ **Statistics & number theory** (written in Devabhāṣā, `examples/ganita.deva`):
420
+ `माध्यम्` (mean), `मध्यमा` (median), `बहुलकः` (mode), `शतमक` (percentile),
421
+ `प्रसरणम्`/`मानविचलनम्` (variance/stddev), `न्यूनतमम्`/`अधिकतमम्`/`परिसरः`
422
+ (min/max/range), `योगफलम्`/`गुणनफलम्` (sum/product), `क्रमणम्` (sort),
423
+ `महत्तमसमापवर्तकः`/`लघुत्तमसमापवर्त्यः` (gcd/lcm), `क्रमगुणितम्` (factorial),
424
+ `अभाज्यः` (is-prime), plus angle conversion (`अंशेभ्यःरेडियनम्`), `सीमन`
425
+ (clamp), `रैखिकान्तर्वेशनम्` (lerp), and `परिवृत्तिः` (round to N places).
426
+ Verified against Python's `statistics`/`numpy` to full precision.
427
+
428
+ **Transcendentals from first principles.** To show the language isn't merely
429
+ forwarding to `Math`, `घातीयश्रेणी` (exp) and `ज्याश्रेणी` (sin) are computed
430
+ in pure Devabhāṣā via their Taylor series — and agree with the native bridge
431
+ to ~15 digits.
432
+
433
+ ```
434
+ चर दत्तांशः = [४, ८, १५, १६, २३, ४२]।
435
+ दर्शय("माध्यम्:", माध्यम्(दत्तांशः))। # 18
436
+ दर्शय("मानविचलनम्:", मानविचलनम्(दत्तांशः))। # 12.3153…
437
+ दर्शय("e via Taylor:", घातीयश्रेणी(१))। # 2.71828… (no Math used)
438
+ दर्शय("ज्या(π/६):", गणित.ज्या(गणित.पाई / ६))। # 0.5
439
+ ```
440
+
441
+ ## अन्तर्न्यासः — string interpolation
442
+
443
+ `पाठ"…"` (pāṭha, "text") marks an interpolated string: any `{expr}` inside is
444
+ evaluated and spliced in, compiling to a JS template literal.
445
+
446
+ ```
447
+ दर्शय(पाठ"{नाम} वर्षाणि {वयः}")। # variables
448
+ रचय वाक्यः वाक्यम् पाठ"योगः: {योगम्(व्ययाः)} रूप्यकाणि"। # any expression
449
+ दर्शय(पाठ"\{ब्रेस\} रक्षितम्")। # \{ \} for literal braces
450
+ ```
451
+
452
+ It is **opt-in via the पाठ marker** for a deliberate reason: making every
453
+ `"…"` interpolate would mis-read the literal braces in existing strings —
454
+ including the JS that the self-hosted compiler emits as string literals — so
455
+ plain `"…"` stays completely literal, and only `पाठ"…"` interpolates. The
456
+ marker must touch the quote, so `पाठ` remains usable as an ordinary
457
+ identifier. Interpolation holds full expressions (calls, member access,
458
+ arithmetic, even nested strings), and is the idiomatic way to build labels
459
+ and views — `examples/vyaya.deva` uses it throughout.
460
+
461
+ ## मधुरचिह्नानि — operator sugar
462
+
463
+ Convenience operators that desugar to the core language:
464
+
465
+ - **compound assignment** — `x += y`, `-=`, `*=`, `/=`, `%=` (works on
466
+ variables, object fields, and array elements); `x += y` becomes `x = x + y`
467
+ - **ternary** — `परीक्षा ? तदा : अन्यथा`, chainable for `else-if` ladders
468
+ - **null-coalescing** — `a ?? b` (yields `b` only when `a` is null/undefined)
469
+ - **increment / decrement** — `i++`, `i--`
470
+
471
+ ```
472
+ चर कुल = ०।
473
+ प्रत्येकम् (x : [१, २, ३, ४]) {
474
+ कुल += x > २ ? x : ०। # add x only when x > 2
475
+ }
476
+ दर्शय(कुल)। # 7
477
+
478
+ चर नाम = दत्त ?? "अज्ञातः"। # default when दत्त is null
479
+ ```
480
+
481
+ These live in the JavaScript-hosted compiler; the self-hosted bootstrap
482
+ sources deliberately don't use them, so the fixpoint is unaffected.
483
+
484
+ ## आयात / निर्यात — the module system
485
+
486
+ Modules use **compile-time resolution and linking** (the Rust/Python
487
+ lineage, not C-style textual inclusion): each `.deva` file is compiled
488
+ independently, exports are explicit, and a bundler resolves the import graph
489
+ and links everything into one self-contained program.
490
+
491
+ **Exporting** — prefix any declaration with `निर्यात` (niryāta, "sending
492
+ out"). Anything not marked is private to its module:
493
+
494
+ ```
495
+ # गणितागारम्.deva
496
+ निर्यात नियत पाई = ३।
497
+ निर्यात कार्य द्वि (न) { फलम् न * २। }
498
+ कार्य गुप्तम् () { … } # private — not importable
499
+ ```
500
+
501
+ **Importing** — `आयात` (āyāta, "incoming") with `आ` (ā, "from") as the
502
+ source preposition, in three forms:
503
+
504
+ ```
505
+ आयात { द्वि, पाई } आ "गणितागारम्"। # named — bind specific exports
506
+ आयात * रूपेण ग आ "गणितागारम्"। # namespace — ग.द्वि, ग.पाई
507
+ आयात "उपस्करः"। # side-effect — just run the module
508
+ ```
509
+
510
+ `devabhasha build entry.deva` (or `run`) resolves every `आयात` relative to
511
+ the importing file (appending `.deva`), compiles each module once, orders
512
+ them dependency-first (topological sort), and links them: each module
513
+ becomes an IIFE returning its export object, named imports destructure it,
514
+ namespace imports bind the whole object. Diamond dependencies compile the
515
+ shared module a single time; missing modules and exports of non-declarations
516
+ are compile-time errors. See `examples/modules/` for a math library split
517
+ into a module and consumed by a main file.
518
+
519
+ The bundler lives in `src/bundler.js`; `compileModule(src)` returns
520
+ `{ code, exports, imports }` for tooling.
521
+
522
+ ## आदर्शकोशः — the standard library (written in Devabhāṣā)
523
+
524
+ The standard library is itself written **in Devabhāṣā**, as `.deva` modules
525
+ under `examples/stdlib/` — the clearest proof the module system earns its
526
+ keep, and the "move features to libraries" principle in practice. Each is
527
+ plain Devabhāṣā built on the array/string/object primitives, with zero
528
+ compiler support:
529
+
530
+ - **सूची** (list): `योगः` (sum), `गुणनफलम्` (product), `न्यूनीकरणम्` (fold),
531
+ `अन्वेषय` (find), `सन्ति`/`सर्वे` (any/all), `न्यूनतमम्`/`महत्तमम्`
532
+ (min/max), `अद्वितीयम्` (unique), `आदिमानि`/`शेषाणि` (take/drop),
533
+ `समतलीकृ` (flatten), `परिसरः` (range).
534
+ - **कोष** (object): `कुञ्जयः`/`मूल्यानि`/`प्रविष्टयः` (keys/values/entries),
535
+ `अस्ति` (has-key), `सङ्ख्या` (count), `विलयः` (merge),
536
+ `प्रतिचित्रयमूल्यानि` (map-values), `गालयकुञ्जीभिः` (pick). Built on the
537
+ `सङ्ग्रह` (Object) global.
538
+ - **पाठ** (string): `आवर्तय` (repeat), `वामपूरणम्` (pad-left),
539
+ `प्रथमाक्षरोच्च` (capitalize), `पदानि` (words), `व्युत्क्रमः` (reverse),
540
+ `परिवर्तय_सर्वम्` (replace-all), `रिक्तः` (is-blank).
541
+ - **परीक्षा** (test framework): `परीक्षा(नाम, fn)` registers and runs a test,
542
+ `अपेक्ष(actual)` returns an asserter (`.समम्`/equal, `.असमम्`/not-equal,
543
+ `.सत्यम्ता`/truthy, `.असत्यम्ता`/falsy), `समम्(अ, ब)` is a standalone deep
544
+ structural equality, and `सारः()` prints the `N पास, M फेल` tally. Staying
545
+ true to the no-exceptions design, assertions **record** their outcome into a
546
+ collector rather than throwing.
547
+
548
+ Use them like any module — and they compose, importing from several at once:
549
+
550
+ ```
551
+ आयात { परिसरः, योगः } आ "सूची"।
552
+ आयात { आवर्तय } आ "पाठ"।
553
+ दर्शय(योगः(परिसरः(१, ५)))। # 10
554
+ दर्शय(आवर्तय("=", योगः([१,२])))। # "==="
555
+ ```
556
+
557
+ With `परीक्षा`, the language tests itself: Devabhāṣā programs (including the
558
+ standard library above) can be tested *in Devabhāṣā*, completing the
559
+ self-hosting story that already covers the compiler. `examples/परीक्षा-उदाहरणम्.deva`
560
+ tests the `सूची` module using `परीक्षा` — both written in the language:
561
+
562
+ ```
563
+ आयात { परीक्षा, अपेक्ष, सारः } आ "stdlib/परीक्षा"।
564
+ आयात { योगः, अद्वितीयम् } आ "stdlib/सूची"।
565
+ परीक्षा("अद्वितीयम्", कार्य(){ अपेक्ष(अद्वितीयम्([१,२,२,३])).समम्([१,२,३])। })।
566
+ सारः()। # → "1 पास, 0 फेल"
567
+ ```
568
+
569
+ Reflection helpers underpin it: `स्वरूपम्(v)` gives a value's type as a
570
+ Sanskrit name (`अङ्क`, `वाक्`, `सूची`, `कोष`, `सत्यासत्य`, `रिक्त`), and
571
+ `सूचीवत्(v)` tests for a list — enabling the framework's deep equality.
572
+
573
+ ## उपकरणानि — tooling (VS Code extension, dev server, source maps & LSP)
574
+
575
+ **VS Code extension.** `devabhasha-1.0.0.vsix` is an installable VS Code
576
+ extension (Extensions panel → "Install from VSIX…", or
577
+ `code --install-extension devabhasha-1.0.0.vsix`). It gives `.deva` files
578
+ syntax highlighting (a TextMate grammar), completion, hover, go-to-definition,
579
+ rename, and live diagnostics, plus a "Run current file" command. Rather than
580
+ spawning the stdio language server, the extension wires the **same
581
+ dependency-free analyzer core** (`src/analyzer.js`) directly to VS Code's
582
+ provider APIs in-process — so the server and the extension are two frontends
583
+ over one analyzer, with no bundled language-client and no child process. The
584
+ analyzer graph is bundled to CommonJS for the extension with `node
585
+ build-extension.js` (esbuild is a build-time tool only; nothing extra ships in
586
+ the `.vsix`).
587
+
588
+ **Dev server with live reload.** `devabhasha serve file.deva [--port N]` starts
589
+ a zero-dependency dev server (Node's built-in `http` + `fs.watch`, live reload
590
+ over Server-Sent Events — no packages) that:
591
+
592
+ - serves an HTML page running your compiled web program, mounted at `#मूलम्`,
593
+ - watches the source (and its `आयात` imports) and **recompiles + reloads the
594
+ browser automatically** when you save, and
595
+ - shows compile *and* runtime errors on the page instead of a blank screen,
596
+ staying up so you can fix and re-save.
597
+
598
+ So the loop is: edit `.deva`, save, see it update in the browser — the modern
599
+ front-end dev experience, for a Sanskrit language.
600
+
601
+ **Source maps.** `devabhasha build file.deva --sourcemap` emits the JS plus a
602
+ standard **Source Map v3** (`.js.map`, with `sourcesContent` and a
603
+ `//# sourceMappingURL` comment), so browser dev-tools and Node stack traces
604
+ point back at the original Devanagari source. Mapping is at statement
605
+ granularity — each statement node carries its source line/col, and the
606
+ codegen tracks output position as it emits. The maps are VLQ-encoded and
607
+ validated against the standard `source-map` library. The API is
608
+ `compileWithMap(source) → { code, map }`.
609
+
610
+ **Language server (LSP).** `src/server.js` is an editor-agnostic Language
611
+ Server speaking the Language Server Protocol over stdio — any LSP client
612
+ (VS Code, Neovim, Helix…) can connect by running `node src/server.js`. It
613
+ provides:
614
+
615
+ - **diagnostics** — compile errors with precise ranges, live as you type
616
+ (cleared automatically when the code becomes valid);
617
+ - **completion** — the full vocabulary (keywords, stdlib methods, गणित
618
+ constants, रूप properties and color words, element tags, events), filtered
619
+ by the prefix being typed;
620
+ - **hover** — what a Sanskrit word means and what it translates to
621
+ (`वर्णः` → color, `रक्तः` → crimson, `वर्गमूलम्` → Math.sqrt);
622
+ - **go-to-definition** — jump from any use of a name to where it is bound;
623
+ - **rename** — rename a binding and every reference that resolves to it,
624
+ atomically, as one workspace edit.
625
+
626
+ Go-to-definition and rename are backed by a **scope-aware symbol table**
627
+ (`src/symbols.js`) that walks the AST tracking lexical scopes — `चर`/`नियत`,
628
+ `कार्य` names and parameters, `भाव`/`रूपनाम` declarations, and `प्रत्येकम्`
629
+ loop variables. It respects **shadowing**: a renamed inner parameter never
630
+ touches an outer variable of the same name, and vice versa. (This is why
631
+ identifiers and declaration names carry source positions — JS-side metadata,
632
+ stripped before the self-hosted parser comparison, so the bootstrap is
633
+ unaffected.)
634
+
635
+ The analysis is pure functions in `src/analyzer.js` (`diagnostics`,
636
+ `completions`, `hover`, `definition`, `renameOccurrences`, `wordAt`);
637
+ `server.js` is a thin JSON-RPC wrapper, so the same engine can power other
638
+ front-ends.
639
+
640
+ ## सञ्चिका / जाल — file & network I/O
641
+
642
+ I/O is **layered**: a program calls the Sanskrit surface (`सञ्चिका` for
643
+ files, `जाल` for the network), which the codegen lowers to a runtime
644
+ interface `__IO`, which a host-injected **backend** implements. The program
645
+ is bound to the *interface*, never to Node — so the same code runs against
646
+ the Node backend (CLI), a browser backend, or an in-memory test backend
647
+ (this is exactly how the test suite proves it, running one program against
648
+ two backends).
649
+
650
+ Every operation is **async and returns a `परिणाम` (Result)** — composing the
651
+ two foundations above. You `प्रतीक्षा` (await) the call and inspect the
652
+ Result; a failure is a value, never a thrown exception, so a missing file
653
+ can't crash the program:
654
+
655
+ ```
656
+ असमकालिक कार्य मुख्यम् () {
657
+ प्रतीक्षा सञ्चिका.लिख("संदेशः.txt", "नमस्ते")। # write
658
+ चर र = प्रतीक्षा सञ्चिका.पठ("संदेशः.txt")। # read
659
+ यदि (र.सफल) { दर्शय(र.मूल्यम्)। } अन्यथा { दर्शय(र.दोषः)। }
660
+
661
+ चर जालम् = प्रतीक्षा जाल.आनय("https://api.example.com")। # fetch
662
+ यदि (जालम्.सफल) { दर्शय(जालम्.मूल्यम्.स्थितिः)। } # → HTTP status
663
+ }
664
+ मुख्यम्()।
665
+ ```
666
+
667
+ **File ops** (`सञ्चिका`): `पठ` (read), `लिख` (write), `विद्यते` (exists),
668
+ `निष्कासय` (remove), `सूचीकृ` (list a directory). **Network** (`जाल`):
669
+ `आनय` (fetch), whose Result value carries `स्थितिः` (status), `पाठः` (body
670
+ text), and `सफलम्` (ok). The Node backend lives in `src/io-node.js`;
671
+ `devabhasha run` injects it, and `devabhasha build` inlines it so the
672
+ produced `.js` is self-contained under Node. See `examples/io.deva`.
673
+
674
+ ## सेवक — HTTP server (backend)
675
+
676
+ Because Devabhāṣā compiles to JavaScript and runs on Node, it can be a
677
+ *backend* language too. `सेवक(handler, port)` (sevaka, "server") starts an HTTP
678
+ server — the same host-bridge pattern as `सञ्चिका`/`जाल`, lowered to a `__SRV`
679
+ runtime the Node backend implements (`src/server-node.js`; `run` injects it,
680
+ `build` inlines it). The handler receives a **request** and **response**, both
681
+ keyed in Devanagari:
682
+
683
+ ```
684
+ सेवक(असमकालिक कार्य(अनुरोधः, प्रत्युत्तरम्){
685
+ यदि (अनुरोधः.मार्गः == "/जनाः") { # route by path
686
+ यदि (अनुरोधः.रीतिः == "GET") { # branch by method
687
+ प्रत्युत्तरम्.प्रेषय_जेसन(सूची)। # JSON response
688
+ } अन्यथा {
689
+ चर परि = प्रतीक्षा अनुरोधः.देहम्_जेसन()। # parse JSON body → परिणाम
690
+ यदि (परि.सफल) {
691
+ सूची.योजय(परि.मूल्यम्)।
692
+ प्रत्युत्तरम्.स्थिति(२०१).प्रेषय_जेसन(परि.मूल्यम्)।
693
+ } अन्यथा {
694
+ प्रत्युत्तरम्.स्थिति(४००).प्रेषय_जेसन(कोष{ दोषः: "bad" })।
695
+ }
696
+ }
697
+ } अन्यथा {
698
+ प्रत्युत्तरम्.स्थिति(४०४).लेखय("न प्राप्तम्")।
699
+ }
700
+ }, ८०८०)।
701
+ ```
702
+
703
+ The **request** (`अनुरोधः`) exposes `मार्गः` (decoded path), `रीतिः` (method),
704
+ `शीर्षाणि` (headers), `प्रश्नाः` (query params), and async body readers `देहम्()`
705
+ (text) and `देहम्_जेसन()` (→ `परिणाम`, so a malformed body is a value, never a
706
+ crash). The **response** (`प्रत्युत्तरम्`) is chainable: `स्थिति(code)`,
707
+ `शीर्षम्(k, v)`, `लेखय(text)`, `प्रेषय_जेसन(value)`. The handler may be
708
+ `असमकालिक` (async); if it throws, the server returns 500 and stays up.
709
+
710
+ This is a deliberately minimal HTTP primitive — enough to write a real JSON API
711
+ or static server (`examples/सेवकः.deva` is a small books API), not a
712
+ production framework like Express. Its value is the same as the rest of the
713
+ language: an honest, teachable surface — and, paired with the reactive frontend,
714
+ the rare ability to write *both ends of a web app in one Sanskrit source*. A
715
+ routing/middleware library written in Devabhāṣā on top of `सेवक` (the
716
+ "framework" proper, like `परीक्षा` is for testing) is the natural next layer.
717
+
718
+ `examples/पूर्णस्तर/` is a **full-stack app**: a reactive Sanskrit frontend
719
+ (`अग्रिम.deva`) fetching from a Sanskrit HTTP/JSON backend (`पश्चिम.deva`) over
720
+ real HTTP, sharing the language, the Result error model, and the JSON shapes.
721
+ Run it with `node चालय.mjs` and open `http://localhost:8100`.
722
+
723
+ ### मार्गकः — a routing library (written in Devabhāṣā)
724
+
725
+ `सेवक` is the bare primitive; `मार्गकः` (mārgaka, "router") is the *framework*
726
+ layer on top — and, like `परीक्षा` the test framework, it's **written in
727
+ Devabhāṣā itself** (`examples/stdlib/मार्गकः.deva`), not baked into the runtime.
728
+ Import it and register routes by method and path:
729
+
730
+ ```
731
+ आयात { मार्गकः } आ "मार्गकः"।
732
+ चर ऐप = मार्गकः()।
733
+ ऐप.उपयुज्(असमकालिक कार्य(अ, प्र, अग्रे){ दर्शय(अ.मार्गः)। प्रतीक्षा अग्रे()। })। # middleware
734
+ ऐप.प्राप्("/ग्रन्थाः", कार्य(अ, प्र){ प्र.प्रेषय_जेसन(सूची)। })। # GET
735
+ ऐप.प्राप्("/ग्रन्थाः/:अंकः", कार्य(अ, प्र){ ... अ.प्राचलाः["अंकः"] ... })। # path param
736
+ ऐप.स्थापय("/ग्रन्थाः", असमकालिक कार्य(अ, प्र){ ... })। # POST
737
+ ऐप.चालय(८०८०)।
738
+ ```
739
+
740
+ The API: `प्राप्` (GET), `स्थापय` (POST), `परिवर्तय` (PUT), `निष्कासय` (DELETE),
741
+ `उपयुज्` (middleware), `चालय` (listen). Path parameters (`:अंकः`) are captured
742
+ into `अनुरोधः.प्राचलाः`. Middleware follows the Koa contract — `असमकालिक`, and
743
+ `प्रतीक्षा अग्रे()` to pass control downstream — which is what lets the server
744
+ await the whole chain before ending the response.
745
+
746
+ **On "optimum":** each route's path is **compiled once at registration** into a
747
+ segment list (static segments + `:param` markers), and routes are **grouped by
748
+ method**, so a request does only a segment-by-segment compare against the routes
749
+ of its own method — no regex, no re-parsing per request. A radix tree would win
750
+ at very large route counts; at app scale this segment-compare is effectively
751
+ optimal and far simpler, an honest tradeoff stated in the source.
752
+ `examples/stdlib/मार्गक-उदाहरणम्.deva` is a books API built on it.
753
+
754
+ **JSON & structured data.** `प्रदत्त` (pradatta, "data") parses and serializes
755
+ JSON, both returning a `परिणाम` (since `JSON.parse` throws and the language
756
+ has none): `प्रदत्त.विश्लेषय(पाठः)` (parse) and `प्रदत्त.सूत्रय(मूल्यम्)`
757
+ (serialize). `अङ्कय(पाठः)` parses a string to a number, also as a Result. The
758
+ I/O layer adds JSON convenience ops so a config or API response becomes usable
759
+ data in one step: `सञ्चिका.पठप्रदत्त`/`लिखप्रदत्त` (read/write JSON files) and
760
+ `जाल.आनयप्रदत्त` (fetch + parse, value carries `प्रदत्तम्`). These compose with
761
+ `प्रतीक्षा` and `अथवा`:
762
+
763
+ ```
764
+ चर विन्यासः = (प्रतीक्षा सञ्चिका.पठप्रदत्त("config.json")) अथवा कोष{ }। # parsed config, or {}
765
+ चर सङ्ख्या = अङ्कय(आदानम्) अथवा ०। # parsed number, or 0
766
+ ```
767
+
768
+ ## असमकालिक — async & await
769
+
770
+ For operations that complete later (timers, and the upcoming I/O layer),
771
+ Devabhāṣā has real `async`/`await` — the model JS, Python, and Rust all
772
+ converged on, and a near-direct map to JS since the target is JS.
773
+
774
+ - `असमकालिक कार्य …` (asamakālika, "asynchronous") marks an async function;
775
+ it returns a promise (`आश्वासन`).
776
+ - `प्रतीक्षा <expr>` (pratīkṣā, "waiting") awaits a promise, valid only
777
+ inside an `असमकालिक` function — the compiler enforces this (a top-level or
778
+ nested-sync `प्रतीक्षा` is a compile error, matching JS's coloring rule).
779
+ - promises chain with `.ततः` (then), `.दोषे` (catch), `.अन्ततः` (finally).
780
+
781
+ ```
782
+ असमकालिक कार्य मुख्यम् () {
783
+ चर परि = प्रतीक्षा आनय(कोषः)। # await a promise
784
+ यदि (परि.सफल) { दर्शय(परि.मूल्यम्)। } # …of a परिणाम (Result)
785
+ अन्यथा { दर्शय(परि.दोषः)। }
786
+ }
787
+ मुख्यम्()।
788
+ ```
789
+
790
+ async composes with the Result model: a fallible async operation returns a
791
+ *promise of a* `परिणाम`, so `प्रतीक्षा` yields a Result the caller inspects —
792
+ honest async **and** honest errors, with no exceptions. Event handlers can be
793
+ async too (`स्पर्शाय करणेन असमकालिक कार्य(){ … प्रतीक्षा … }`), the usual
794
+ shape for I/O-on-click.
795
+
796
+ ## परिणाम — the error model (Result values)
797
+
798
+ Devabhāṣā handles fallible operations with **explicit Result values**, not
799
+ exceptions — keeping with the language's preference for visible over hidden
800
+ control flow (and avoiding a new non-local jump in the self-hosted compiler).
801
+ A fallible function returns a `परिणाम` (pariṇāma, "result/outcome"), built
802
+ with one of two constructors:
803
+
804
+ - `साधितम्(मूल्यम्)` (sādhitam, "achieved") — success, carrying a value
805
+ - `विफलम्(दोषः)` (viphalam, "failed") — failure, carrying an error
806
+
807
+ A `परिणाम` has three fields: `सफल` (ok?), `मूल्यम्` (the value), `दोषः`
808
+ (the error). The caller inspects it with ordinary `यदि`:
809
+
810
+ ```
811
+ कार्य भाग (अंशः, हरः) {
812
+ यदि (हरः == ०) { फलम् विफलम्("शून्येन भागः")। } # Err
813
+ फलम् साधितम्(अंशः / हरः)। # Ok
814
+ }
815
+
816
+ चर फल = भाग(१०, २)।
817
+ यदि (फल.सफल) { दर्शय("ok:", फल.मूल्यम्)। }
818
+ अन्यथा { दर्शय("दोष:", फल.दोषः)। }
819
+ ```
820
+
821
+ Results compose: a function can map an `Ok` value and pass an `Err` through
822
+ unchanged, which is how fallible steps chain without exceptions. The
823
+ constructors live in a host-independent prelude (`__RT`), so Result works in
824
+ any environment — including the I/O layer, where operations return `परिणाम`
825
+ rather than throwing.
826
+
827
+ **`अथवा` — value-or-fallback.** The common "use the value, or a default if it
828
+ failed" pattern would otherwise be a four-line `यदि/अन्यथा` block. `अथवा`
829
+ (athavā, "or else") collapses it to an expression: it yields the Result's
830
+ `मूल्यम्` when `सफल`, else a fallback that is **evaluated lazily** (only on
831
+ failure). It's pure expression sugar — no hidden control flow — and composes
832
+ with `await`, which is exactly the I/O case:
833
+
834
+ ```
835
+ चर सामग्री = (प्रतीक्षा सञ्चिका.पठ("config.txt")) अथवा "रिक्तम्"। # default on read failure
836
+ चर सङ्ख्या = संख्यांकय(आदानम्) अथवा ०। # default on parse failure
837
+ ```
838
+
839
+ `अथवा` chains right-associatively (`अ अथवा ब अथवा ग` tries each in turn,
840
+ first `Ok` wins) and also guards plain `null`/non-Result values, so it
841
+ doubles as a nullish fallback.
842
+
843
+ ## दोषनिरूपणम् — error reporting
844
+
845
+ Compiler errors are structured (`DevabhashaError` with `line`, `col`, and a
846
+ `kind` of lex/parse/codegen) and render with source context and a caret:
847
+
848
+ ```
849
+ अक्षरदोषः (lex error): अज्ञातं चिह्नम् (unknown character) '@'
850
+ line 2, column 9
851
+
852
+ २ | दर्शय(a @ b)।
853
+ | ^
854
+ ```
855
+
856
+ Line numbers display in Devanagari numerals. The same formatting appears in
857
+ the CLI (`devabhasha run`) and live in the playground as you type. Use
858
+ `formatError(err, source)` to render any caught `DevabhashaError`.
859
+
860
+ ## मानकपुस्तकालय — standard library (strings, objects, arrays)
861
+
862
+ The machinery a compiler is made of. These are what make self-hosting
863
+ possible: a lexer needs to index strings, accumulate substrings, push
864
+ records onto arrays, and read object fields.
865
+
866
+ **Strings** — `.दीर्घता` (length), `.अक्षरः(i)` (charAt), `.सङ्केतः(i)`
867
+ (charCodeAt), `.खण्ड(a,b)` (slice), `.अस्ति(x)` (includes), `.विभज(s)`
868
+ (split), `.उच्च`/`.नीच` (upper/lower), `.आरभते`/`.समाप्यते` (starts/ends).
869
+
870
+ **Arrays** — `.योजय(x)` (push), `.अपनय()` (pop), `.दीर्घता` (length),
871
+ `.प्रतिचित्रय(fn)` (map), `.गालय(fn)` (filter), `.सम्मील(s)` (join),
872
+ `.अनुक्रमणिका(x)` (indexOf), indexing via `सूची[i]`.
873
+
874
+ **Builtins** — `संकेताक्षर(code)` (saṅketākṣara, "code-letter") →
875
+ `String.fromCharCode`, e.g. `संकेताक्षर(९२)` yields a backslash. This is
876
+ what lets the self-hosted codegen build escape characters without writing
877
+ escape literals.
878
+
879
+ **Objects** — the `कोष` (kośa, "dictionary") literal:
880
+
881
+ ```
882
+ चर व्यक्तिः = कोष { नाम: "राम", आयुः: ३० }।
883
+ दर्शय(व्यक्तिः.नाम)। # field access
884
+ व्यक्तिः.आयुः = ३१। # field assignment
885
+ ```
886
+
887
+ Property names live in their own namespace, so a word that's a keyword
888
+ elsewhere (`योजय` = DOM mount) is still usable as a method name
889
+ (`सूची.योजय` = array push). The stdlib name→JS mapping lives in
890
+ `src/stdlib.js`.
891
+
892
+ ### Self-hosting milestone
893
+
894
+ With strings, objects, and arrays in place, compiler machinery can now be
895
+ written *in Devabhāṣā itself*.
896
+
897
+ **All three compiler stages are written in Devabhāṣā, and the bootstrap is
898
+ a closed fixpoint** — the compiler reproduces itself byte-for-byte.
899
+
900
+ - `examples/lexer.deva` — port of `src/lexer.js`. Character classes are
901
+ built from codepoint arithmetic (no regex); keyword/operator tables are
902
+ Devabhāṣā data.
903
+ - `examples/parser.deva` — port of `src/parser.js`: recursive descent +
904
+ Pratt expression parsing. Since Devabhāṣā functions don't close over
905
+ outer mutable state, the parser threads an explicit state object
906
+ `अ = कोष { शब्दाः, स्थानम् }` through every function.
907
+ - `examples/codegen.deva` — port of `src/codegen.js`: the AST→JS emitter,
908
+ the Devanagari→ASCII transliterator `आईडी`, and string quoting. The
909
+ quoter builds every special character (backslash, quote, newline, tab)
910
+ from its code point via the `संकेताक्षर` (String.fromCharCode) builtin,
911
+ so the codegen source contains **no escape literals** — which is what
912
+ lets it reproduce its own source exactly.
913
+
914
+ Verified by the standard compiler-bootstrap fixpoint check
915
+ (`test/fixpoint.test.js`):
916
+
917
+ 1. the JS-hosted compiler compiles the three `.deva` sources → a
918
+ Devabhāṣā-hosted compiler (stage 1);
919
+ 2. that compiler recompiles all three sources (stage 2);
920
+ 3. a compiler built from stage-2 output recompiles them again (stage 3);
921
+ 4. **stage 2 === stage 3, byte-for-byte, for all three components** — the
922
+ same criterion GCC/Rust/Go use to certify a self-hosting bootstrap.
923
+
924
+ The stage-3 compiler — built entirely from Devabhāṣā-generated JavaScript,
925
+ with no original toolchain involved — runs real programs (recursion,
926
+ objects, arrays) correctly. `node selfhost-demo.mjs` shows the full
927
+ self-hosted compiler compiling and running Fibonacci.
928
+
929
+ ## Architecture
930
+ A textbook transpiler pipeline — each stage is one file, no dependencies:
931
+
932
+ ```
933
+ source .deva
934
+ │ src/lexer.js tokenize Devanagari → token stream
935
+
936
+ tokens
937
+ │ src/parser.js recursive descent (statements) + Pratt (expressions) → AST
938
+
939
+ AST
940
+ │ src/codegen.js tree-walk → JavaScript text (+ optional DOM runtime)
941
+
942
+ JavaScript
943
+ ```
944
+
945
+ - **`src/keywords.js`** — the *only* Sanskrit-aware file. Edit vocabulary here
946
+ without touching the engine. The lexer maps each Devanagari keyword to a
947
+ language-neutral token type, so the parser and codegen never see Sanskrit.
948
+ - **`src/index.js`** — `compile(source, { includeRuntime }) → string`.
949
+ - **`src/cli.js`** — `build` / `run` commands.
950
+ - **`build-playground.js`** — inlines the ES modules into one HTML file.
951
+
952
+ ## Extending it
953
+
954
+ - **Add a keyword:** one line in `keywords.js`. If it needs new syntax, add a
955
+ parse branch in `parser.js` and an emit case in `codegen.js`.
956
+ - **Add an operator:** add to `OPERATORS` (longest-first) and to `BINARY_PREC`.
957
+ - **Richer web layer:** the runtime in `codegen.js` (`__DB`) is intentionally
958
+ tiny — extend `el` to support a JSX-like compound (`समास`) syntax, or add a
959
+ reactivity primitive.
960
+
961
+ ## Suggested roadmap from here
962
+
963
+ 1. **Source maps** — you have line/col on every token already; thread them into
964
+ codegen for real debugging.
965
+ 2. **A JSX-like element syntax** using compounds, e.g.
966
+ `अङ्गम् दिव् { … }` block form instead of nested calls.
967
+ 3. **Type-ish annotations** drawn from Sanskrit grammatical categories.
968
+ 4. **Editor support** — a TextMate grammar / VS Code extension highlighting the
969
+ keyword set in `keywords.js`.
970
+ 5. **Sanskrit error messages** — partly done; expand `parser.js` diagnostics.
971
+
972
+ ## License
973
+
974
+ MIT.