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.
@@ -0,0 +1,120 @@
1
+ // server-node.js — the सेवक HTTP-server host primitive (Node backend).
2
+ //
3
+ // Mirrors io-node.js: a live `__SRV` object for `devabhasha run`, and an
4
+ // inlined `SRV_NODE_SOURCE` string for `devabhasha build` (self-contained).
5
+ //
6
+ // सेवक(handler, port) starts an http.createServer. The handler receives a
7
+ // request and a response object, both keyed in Devanagari (member access emits
8
+ // raw Devanagari, so the runtime object must expose those exact keys):
9
+ //
10
+ // अनुरोधः (request): मार्गः (url/path), रीतिः (method), शीर्षाणि (headers),
11
+ // प्रश्नाः (query params object), देहम्() → Promise<body text>,
12
+ // देहम्_जेसन() → Promise<Result<parsed JSON>>
13
+ // प्रत्युत्तरम् (response): स्थिति(code), शीर्षम्(k,v), लेखय(text), प्रेषय_जेसन(value)
14
+ //
15
+ // The handler may be async (return a Promise); errors are caught and become 500.
16
+
17
+ import { createServer } from 'http';
18
+
19
+ // Build the Devanagari-keyed request wrapper around a Node IncomingMessage.
20
+ function makeRequest(req) {
21
+ const url = new URL(req.url, 'http://localhost');
22
+ const query = {};
23
+ for (const [k, v] of url.searchParams) query[k] = v;
24
+ let bodyText = null;
25
+ const readBody = () => new Promise((resolve) => {
26
+ if (bodyText != null) return resolve(bodyText);
27
+ let data = '';
28
+ req.on('data', (c) => { data += c; });
29
+ req.on('end', () => { bodyText = data; resolve(data); });
30
+ req.on('error', () => resolve(''));
31
+ });
32
+ return {
33
+ 'मार्गः': decodeURIComponent(url.pathname),
34
+ 'रीतिः': req.method,
35
+ 'शीर्षाणि': req.headers,
36
+ 'प्रश्नाः': query,
37
+ async 'देहम्'() { return readBody(); },
38
+ async 'देहम्_जेसन'() {
39
+ const t = await readBody();
40
+ try { return { 'सफल': true, 'मूल्यम्': JSON.parse(t), 'दोषः': null }; }
41
+ catch (e) { return { 'सफल': false, 'मूल्यम्': null, 'दोषः': 'JSON: ' + (e && e.message || e) }; }
42
+ },
43
+ };
44
+ }
45
+
46
+ // Build the Devanagari-keyed response wrapper around a Node ServerResponse.
47
+ function makeResponse(res) {
48
+ const api = {
49
+ 'स्थिति'(code) { res.statusCode = code; return api; }, // set status, chainable
50
+ 'शीर्षम्'(k, v) { res.setHeader(k, v); return api; }, // set header, chainable
51
+ 'लेखय'(text) { // send text and end
52
+ if (!res.hasHeader('Content-Type')) res.setHeader('Content-Type', 'text/html; charset=utf-8');
53
+ res.end(text == null ? '' : String(text));
54
+ return api;
55
+ },
56
+ 'प्रेषय_जेसन'(value) { // send JSON and end
57
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
58
+ res.end(JSON.stringify(value));
59
+ return api;
60
+ },
61
+ };
62
+ return api;
63
+ }
64
+
65
+ export const __SRV = {
66
+ serve(handler, port) {
67
+ const server = createServer(async (req, res) => {
68
+ try {
69
+ await handler(makeRequest(req), makeResponse(res));
70
+ if (!res.writableEnded) res.end(); // handler forgot to respond
71
+ } catch (e) {
72
+ if (!res.headersSent) res.statusCode = 500;
73
+ if (!res.writableEnded) res.end('सर्वर-दोषः (500): ' + (e && e.message || e));
74
+ }
75
+ });
76
+ server.listen(port || 3000);
77
+ return server;
78
+ },
79
+ };
80
+
81
+ export const SRV_NODE_SOURCE = `// --- देवभाषा HTTP server (Node backend) ---
82
+ const __SRV = (() => {
83
+ const { createServer } = require('http');
84
+ function makeRequest(req) {
85
+ const url = new URL(req.url, 'http://localhost');
86
+ const query = {}; for (const [k, v] of url.searchParams) query[k] = v;
87
+ let bodyText = null;
88
+ const readBody = () => new Promise((resolve) => {
89
+ if (bodyText != null) return resolve(bodyText);
90
+ let data = ''; req.on('data', c => { data += c; });
91
+ req.on('end', () => { bodyText = data; resolve(data); });
92
+ req.on('error', () => resolve(''));
93
+ });
94
+ return {
95
+ "मार्गः": decodeURIComponent(url.pathname), "रीतिः": req.method, "शीर्षाणि": req.headers, "प्रश्नाः": query,
96
+ async "देहम्"() { return readBody(); },
97
+ async "देहम्_जेसन"() { const t = await readBody(); try { return { "सफल": true, "मूल्यम्": JSON.parse(t), "दोषः": null }; } catch (e) { return { "सफल": false, "मूल्यम्": null, "दोषः": 'JSON: ' + (e && e.message || e) }; } },
98
+ };
99
+ }
100
+ function makeResponse(res) {
101
+ const api = {
102
+ "स्थिति"(code){ res.statusCode = code; return api; },
103
+ "शीर्षम्"(k, v){ res.setHeader(k, v); return api; },
104
+ "लेखय"(text){ if (!res.hasHeader('Content-Type')) res.setHeader('Content-Type','text/html; charset=utf-8'); res.end(text == null ? '' : String(text)); return api; },
105
+ "प्रेषय_जेसन"(value){ res.setHeader('Content-Type','application/json; charset=utf-8'); res.end(JSON.stringify(value)); return api; },
106
+ };
107
+ return api;
108
+ }
109
+ return {
110
+ serve(handler, port) {
111
+ const server = createServer(async (req, res) => {
112
+ try { await handler(makeRequest(req), makeResponse(res)); if (!res.writableEnded) res.end(); }
113
+ catch (e) { if (!res.headersSent) res.statusCode = 500; if (!res.writableEnded) res.end('सर्वर-दोषः (500): ' + (e && e.message || e)); }
114
+ });
115
+ server.listen(port || 3000);
116
+ return server;
117
+ },
118
+ };
119
+ })();
120
+ `;
package/src/server.js ADDED
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env node
2
+ // server.js — a Language Server (LSP) for Devabhāṣā, over stdio.
3
+ //
4
+ // Speaks the Language Server Protocol: Content-Length framed JSON-RPC on
5
+ // stdin/stdout. Supports:
6
+ // • textDocument/publishDiagnostics — compile errors as you type
7
+ // • textDocument/completion — keywords, stdlib, style vocab, tags
8
+ // • textDocument/hover — what a Sanskrit word means
9
+ //
10
+ // Editor-agnostic: any LSP client (VS Code, Neovim, etc.) can connect by
11
+ // running `node src/server.js`. The analysis itself lives in analyzer.js;
12
+ // this file is purely the protocol.
13
+
14
+ import { diagnostics, completions, hover, wordAt, definition, renameOccurrences } from './analyzer.js';
15
+
16
+ // ---- LSP completion-item kinds (subset) ----
17
+ const CIK = { keyword: 14, method: 2, property: 10, constant: 21, function: 3,
18
+ field: 5, value: 12, tag: 7, event: 23 };
19
+
20
+ // open documents: uri → text
21
+ const docs = new Map();
22
+
23
+ // ---- JSON-RPC / LSP framing ----
24
+ let buffer = Buffer.alloc(0);
25
+ process.stdin.on('data', chunk => {
26
+ buffer = Buffer.concat([buffer, chunk]);
27
+ for (;;) {
28
+ const headerEnd = buffer.indexOf('\r\n\r\n');
29
+ if (headerEnd === -1) return;
30
+ const header = buffer.slice(0, headerEnd).toString('utf8');
31
+ const m = /Content-Length:\s*(\d+)/i.exec(header);
32
+ if (!m) { buffer = buffer.slice(headerEnd + 4); continue; }
33
+ const len = parseInt(m[1], 10);
34
+ const start = headerEnd + 4;
35
+ if (buffer.length < start + len) return; // wait for more
36
+ const body = buffer.slice(start, start + len).toString('utf8');
37
+ buffer = buffer.slice(start + len);
38
+ try { handle(JSON.parse(body)); } catch (e) { /* ignore malformed */ }
39
+ }
40
+ });
41
+
42
+ function send(msg) {
43
+ const json = JSON.stringify(msg);
44
+ const buf = Buffer.from(json, 'utf8');
45
+ process.stdout.write(`Content-Length: ${buf.length}\r\n\r\n`);
46
+ process.stdout.write(buf);
47
+ }
48
+ function reply(id, result) { send({ jsonrpc: '2.0', id, result }); }
49
+ function notify(method, params) { send({ jsonrpc: '2.0', method, params }); }
50
+
51
+ // ---- diagnostics: compute + publish for a document ----
52
+ function publishDiagnostics(uri) {
53
+ const text = docs.get(uri) || '';
54
+ const diags = diagnostics(text).map(d => ({
55
+ range: {
56
+ start: { line: d.line - 1, character: d.col - 1 },
57
+ end: { line: d.line - 1, character: (d.endCol || d.col + 1) - 1 },
58
+ },
59
+ severity: d.severity || 1,
60
+ source: 'devabhasha',
61
+ message: d.message,
62
+ }));
63
+ notify('textDocument/publishDiagnostics', { uri, diagnostics: diags });
64
+ }
65
+
66
+ // ---- get the word at an LSP position ----
67
+ function wordAtPosition(uri, position) {
68
+ const text = docs.get(uri) || '';
69
+ const lines = text.split('\n');
70
+ const lineText = lines[position.line] || '';
71
+ return wordAt(lineText, position.character);
72
+ }
73
+
74
+ // ---- request/notification dispatch ----
75
+ function handle(msg) {
76
+ const { id, method, params } = msg;
77
+ switch (method) {
78
+ case 'initialize':
79
+ reply(id, {
80
+ capabilities: {
81
+ textDocumentSync: 1, // full document sync
82
+ completionProvider: { triggerCharacters: [] },
83
+ hoverProvider: true,
84
+ definitionProvider: true,
85
+ renameProvider: true,
86
+ },
87
+ serverInfo: { name: 'devabhasha-language-server', version: '1.0.0' },
88
+ });
89
+ break;
90
+
91
+ case 'initialized':
92
+ break;
93
+
94
+ case 'textDocument/didOpen': {
95
+ const { uri, text } = params.textDocument;
96
+ docs.set(uri, text);
97
+ publishDiagnostics(uri);
98
+ break;
99
+ }
100
+ case 'textDocument/didChange': {
101
+ const uri = params.textDocument.uri;
102
+ // full sync: last change holds the whole document
103
+ const text = params.contentChanges[params.contentChanges.length - 1].text;
104
+ docs.set(uri, text);
105
+ publishDiagnostics(uri);
106
+ break;
107
+ }
108
+ case 'textDocument/didClose':
109
+ docs.delete(params.textDocument.uri);
110
+ notify('textDocument/publishDiagnostics', { uri: params.textDocument.uri, diagnostics: [] });
111
+ break;
112
+
113
+ case 'textDocument/completion': {
114
+ const { word, start } = wordAtPosition(params.textDocument.uri, params.position);
115
+ // complete on the prefix up to the cursor
116
+ const cursorInWord = Math.max(0, params.position.character - start);
117
+ const prefix = word.slice(0, cursorInWord);
118
+ const items = completions(prefix).map(it => ({
119
+ label: it.label,
120
+ kind: CIK[it.kind] || 1,
121
+ detail: it.detail,
122
+ documentation: it.doc,
123
+ }));
124
+ reply(id, { isIncomplete: false, items });
125
+ break;
126
+ }
127
+
128
+ case 'textDocument/hover': {
129
+ const { word } = wordAtPosition(params.textDocument.uri, params.position);
130
+ const h = hover(word);
131
+ if (!h) { reply(id, null); break; }
132
+ reply(id, {
133
+ contents: { kind: 'markdown', value: `**${h.label}** — ${h.detail}\n\n${h.doc}` },
134
+ });
135
+ break;
136
+ }
137
+
138
+ case 'textDocument/definition': {
139
+ const uri = params.textDocument.uri;
140
+ const text = docs.get(uri) || '';
141
+ // LSP positions are 0-based; the symbol table is 1-based
142
+ const def = definition(text, params.position.line + 1, params.position.character + 1);
143
+ if (!def) { reply(id, null); break; }
144
+ reply(id, {
145
+ uri,
146
+ range: {
147
+ start: { line: def.line - 1, character: def.col - 1 },
148
+ end: { line: def.line - 1, character: def.col - 1 + def.name.length },
149
+ },
150
+ });
151
+ break;
152
+ }
153
+
154
+ case 'textDocument/rename': {
155
+ const uri = params.textDocument.uri;
156
+ const text = docs.get(uri) || '';
157
+ const newName = params.newName;
158
+ const occ = renameOccurrences(text, params.position.line + 1, params.position.character + 1);
159
+ if (!occ.length) { reply(id, null); break; }
160
+ const edits = occ.map(o => ({
161
+ range: {
162
+ start: { line: o.line - 1, character: o.col - 1 },
163
+ end: { line: o.line - 1, character: o.col - 1 + o.name.length },
164
+ },
165
+ newText: newName,
166
+ }));
167
+ reply(id, { changes: { [uri]: edits } });
168
+ break;
169
+ }
170
+
171
+ case 'shutdown':
172
+ reply(id, null);
173
+ break;
174
+ case 'exit':
175
+ process.exit(0);
176
+ break;
177
+
178
+ default:
179
+ // unknown request → null result so clients don't hang
180
+ if (id !== undefined) reply(id, null);
181
+ }
182
+ }
package/src/stdlib.js ADDED
@@ -0,0 +1,137 @@
1
+ // stdlib.js — the Sanskrit standard library surface.
2
+ //
3
+ // Maps Sanskrit method/property names to JavaScript operations on
4
+ // strings, arrays, and objects. These are the primitives a self-hosting
5
+ // compiler needs: index, slice, length, concat, push, field access.
6
+ //
7
+ // codegen consults these tables when emitting Member / Call nodes so that
8
+ // `पद.दीर्घता` → `pada.length`, `सूची.योजय(x)` → `arr.push(x)`, etc.
9
+
10
+ // Property accessors (no call): noun → JS property
11
+ export const PROPERTIES = {
12
+ 'दीर्घता': 'length', // dīrghatā — "length"
13
+ 'मानानि': 'values', // (used as a marker; handled specially if needed)
14
+ };
15
+
16
+ // Methods (called): Sanskrit name → JS method name.
17
+ // Works uniformly on strings and arrays where JS allows.
18
+ export const METHODS = {
19
+ // --- string & array shared ---
20
+ 'खण्ड': 'slice', // khaṇḍa — "segment" → slice(start, end)
21
+ 'अनुक्रमणिका':'indexOf', // anukramaṇikā — "index" → indexOf(x)
22
+ 'अस्ति': 'includes', // asti — "exists" → includes(x)
23
+ 'संयोजय': 'concat', // saṃyojaya — "join together" → concat
24
+
25
+ // --- string specific ---
26
+ 'अक्षरः': 'charAt', // akṣaraḥ — "letter" → charAt(i)
27
+ 'सङ्केतः': 'charCodeAt', // saṅketaḥ — "code" → charCodeAt(i)
28
+ 'विभज': 'split', // vibhaja — "divide" → split(sep)
29
+ 'उच्च': 'toUpperCase', // ucca — "high" → toUpperCase
30
+ 'नीच': 'toLowerCase', // nīca — "low" → toLowerCase
31
+ 'छिन्द्धि': 'trim', // chinddhi — "cut" → trim
32
+ 'आरभते': 'startsWith', // ārabhate — "begins" → startsWith
33
+ 'समाप्यते': 'endsWith', // samāpyate — "ends" → endsWith
34
+
35
+ // --- array specific ---
36
+ 'योजय': 'push', // yojaya — "join/attach" → push(x)
37
+ 'अपनय': 'pop', // apanaya — "remove" → pop
38
+ 'प्रतिचित्रय':'map', // praticitraya — "map across" → map(fn)
39
+ 'गालय': 'filter', // gālaya — "filter" → filter(fn)
40
+ 'प्रत्येकस्मिन्':'forEach', // pratyekasmin — "in each" → forEach(fn)
41
+ 'सम्मील': 'join', // sammīla — "unite" → join(sep) (array→string)
42
+ 'विपर्यय': 'reverse', // viparyaya — "reverse" → reverse
43
+
44
+ // --- promise (आश्वासन) methods for async chaining ---
45
+ 'ततः': 'then', // tataḥ — "thereupon" → .then(fn)
46
+ 'दोषे': 'catch', // doṣe — "on error" → .catch(fn)
47
+ 'अन्ततः': 'finally', // antataḥ — "finally" → .finally(fn)
48
+
49
+ // --- I/O methods (on सञ्चिका / जाल namespaces) ---
50
+ 'पठ': 'read', // paṭha — "read" → file.read(path)
51
+ 'लिख': 'write', // likha — "write" → file.write(path, data)
52
+ 'विद्यते': 'exists', // vidyate — "exists" → file.exists(path)
53
+ 'निष्कासय': 'remove', // niṣkāsaya — "remove" → file.remove(path)
54
+ 'सूचीकृ': 'list', // sūcīkṛ — "enumerate" → file.list(dir)
55
+ 'आनय': 'fetch', // ānaya — "bring/fetch" → net.fetch(url)
56
+ 'पठप्रदत्त': 'readJson', // paṭha-pradatta — read + parse JSON → Result
57
+ 'लिखप्रदत्त': 'writeJson', // likha-pradatta — serialize + write JSON
58
+ 'आनयप्रदत्त': 'fetchJson', // ānaya-pradatta — fetch + parse JSON → Result
59
+
60
+ // --- Object statics (on the सङ्ग्रह namespace) ---
61
+ 'कुञ्जयः': 'keys', // kuñjayaḥ — "keys" → Object.keys(o)
62
+ 'मूल्यानि': 'values', // mūlyāni — "values" → Object.values(o)
63
+ 'प्रविष्टयः': 'entries', // praviṣṭayaḥ — "entries" → Object.entries(o)
64
+ 'समायोजय': 'assign', // samāyojaya — "merge" → Object.assign(t, s)
65
+
66
+ // --- गणित (Math) methods: used as गणित.<name>(...) ---
67
+ // Mapped here so गणित.वर्गमूलम्(x) → Math.sqrt(x). These names are
68
+ // distinctive enough not to collide with user object methods.
69
+ 'वर्गमूलम्': 'sqrt', // vargamūlam — "square root"
70
+ 'घनमूलम्': 'cbrt', // ghanamūlam — "cube root"
71
+ 'घातः': 'pow', // ghātaḥ — "power" → pow(base, exp)
72
+ 'निरपेक्षम्': 'abs', // nirapekṣam — "absolute"
73
+ 'अधःपातः': 'floor', // adhaḥpātaḥ — "falling down" → floor
74
+ 'ऊर्ध्वपातः': 'ceil', // ūrdhvapātaḥ — "rising up" → ceil
75
+ 'सन्निकर्षः': 'round', // sannikarṣaḥ — "approximation" → round
76
+ 'छेदनम्': 'trunc', // chedanam — "cutting" → trunc
77
+ 'धनर्णचिह्नम्':'sign', // dhanarṇacihnam — "sign of a number" → Math.sign
78
+ 'ज्या': 'sin', // jyā — classical Sanskrit term for sine!
79
+ 'कोटिज्या': 'cos', // koṭijyā — classical term for cosine!
80
+ 'स्पर्शज्या': 'tan', // sparśajyā — "tangent"
81
+ 'विलोमज्या': 'asin', // vilomajyā — "inverse sine"
82
+ 'विलोमकोटि': 'acos', // → acos
83
+ 'विलोमस्पर्श':'atan', // → atan
84
+ 'द्विकोणार्क':'atan2', // → atan2(y, x)
85
+ 'घातीयम्': 'exp', // ghātīyam — "exponential" → exp
86
+ 'लघुगणकः': 'log', // laghugaṇakaḥ — "logarithm" → log (natural)
87
+ 'दशलघुगणकः': 'log10', // → log10
88
+ 'द्विलघुगणकः':'log2', // → log2
89
+ 'अधिकतमः': 'max', // adhikatamaḥ — "greatest" → max(a, b, …)
90
+ 'न्यूनतमः': 'min', // nyūnatamaḥ — "least" → min(a, b, …)
91
+ 'यादृच्छिकम्':'random', // yādṛcchikam — "random" → random()
92
+ 'हिज्या': 'sinh', // hijyā — hyperbolic sine
93
+ 'कोटिहिज्या': 'cosh', // → cosh
94
+ 'स्पर्शहिज्या':'tanh', // → tanh
95
+ 'विलोमहिज्या':'asinh', // → asinh
96
+ 'घातमूलम्': 'hypot', // ghātamūlam — "power-root" → hypot(a,b)
97
+ };
98
+
99
+ // गणित (Math) constants: accessed as गणित.पाई → Math.PI
100
+ export const MATH_CONSTANTS = {
101
+ 'पाई': 'PI', // pāī → Math.PI
102
+ 'यूलरांकः': 'E', // yūlarāṅkaḥ — "Euler's number" → Math.E
103
+ 'मूलद्वि': 'SQRT2', // mūladvi — "root two" → Math.SQRT2
104
+ 'लॉग२इ': 'LOG2E', // → Math.LOG2E
105
+ 'लॉग१०इ': 'LOG10E', // → Math.LOG10E
106
+ };
107
+
108
+ // Global / constructor-ish helpers. Sanskrit name → emitted JS. Looked up
109
+ // when an Identifier is emitted, so संकेताक्षर(९२) → String.fromCharCode(92).
110
+ // NOTE: only names unlikely to be used as ordinary variables belong here —
111
+ // common words like वस्तु ("thing") must NOT be globalized or they'd shadow
112
+ // user variables.
113
+ export const GLOBALS = {
114
+ 'संकेताक्षर': 'String.fromCharCode', // saṅketākṣara — "code-letter"
115
+ 'गणित': 'Math', // gaṇita — "mathematics" → Math
116
+ 'साधितम्': '__RT.ok', // sādhitam — "achieved" → Ok(value)
117
+ 'विफलम्': '__RT.err', // viphalam — "failed" → Err(error)
118
+ 'सञ्चिका': '__IO.file', // sañcikā — "file" → file I/O namespace
119
+ 'जाल': '__IO.net', // jāla — "net/web" → network namespace
120
+ 'सेवक': '__SRV.serve', // sevaka — "server" → start an HTTP server
121
+ 'सङ्ग्रह': 'Object', // saṅgraha — "collection" → Object (keys/values/entries)
122
+ 'कालचक्र': '__DB.interval', // kālacakra — "wheel of time" → repeating timer
123
+ 'कालनाशः': '__DB.clearTimer', // kālanāśa — "end of time" → stop a timer
124
+ 'कुञ्जिश्रोता': '__DB.onKey', // kuñjiśrotā — "key-listener" → keyboard handler
125
+ 'स्वरूपम्': '__RT.typeOf', // svarūpam — "intrinsic form/type" → typeof (Sanskrit-named)
126
+ 'सूचीवत्': 'Array.isArray', // sūcīvat — "list-like" → Array.isArray
127
+ 'प्रदत्त': '__RT.json', // pradatta — "data" → JSON parse/serialize (Result-returning)
128
+ 'अङ्कय': '__RT.toNumber', // aṅkaya — "to number" → parse string→number (Result)
129
+ 'प्रभाव': '__DB.effect', // prabhāva — "influence/effect" → fine-grained reactive effect
130
+ 'बन्ध': '__DB.bindText', // bandha — "binding" → a text node bound to a reactive thunk
131
+ 'आवली': '__DB.keyedList', // āvalī — "row/series" → keyed list reconciliation
132
+ 'सफाई': '__DB.onCleanup', // saphāī — "cleanup" → teardown hook inside an effect
133
+ 'आलस्यचित्रम्': '__DB.lazyImage', // ālasya-citra — "lazy image" → load on scroll-into-view
134
+ };
135
+
136
+ // Object-construction keyword handled in parser: कोष (kośa, "treasury/dictionary")
137
+ // produces an object literal: कोष { कुञ्जी: मूल्यम्, ... }
package/src/style.js ADDED
@@ -0,0 +1,103 @@
1
+ // style.js — रूप: the Sanskrit→CSS vocabulary.
2
+ //
3
+ // Translates Sanskrit style-property names → CSS properties (camelCase, for
4
+ // the JS `style` object) and Sanskrit value words → CSS values. Built up
5
+ // step by step; this is the isolated styling surface (cf. karaka-web.js).
6
+
7
+ // --- property names: Sanskrit → CSS (camelCase) ---
8
+ export const STYLE_PROPS = {
9
+ // color & background
10
+ 'वर्णः': 'color', // varṇa — "color"
11
+ 'पृष्ठभूमिः': 'backgroundColor', // pṛṣṭhabhūmi — "background"
12
+ 'अपारदर्शिता': 'opacity', // apāradarśitā — "opacity"
13
+
14
+ // typography
15
+ 'अक्षरमानम्': 'fontSize', // akṣaramāna — "letter-size"
16
+ 'अक्षरभारः': 'fontWeight', // akṣarabhāra — "letter-weight"
17
+ 'पङ्क्तिमानम्': 'lineHeight', // paṅktimāna — "line-height"
18
+ 'संरेखणम्': 'textAlign', // saṃrekhaṇa — "alignment"
19
+ 'अक्षरकुलम्': 'fontFamily', // akṣarakula — "font-family"
20
+ 'अलङ्कारः': 'textDecoration', // alaṅkāra — "decoration"
21
+
22
+ // box model
23
+ 'चौडाई': 'width', // — width
24
+ 'अधिकतमचौडाई': 'maxWidth', // adhikatama-cauḍāī — "maximum width" → max-width
25
+ 'रेखोच्चता': 'lineHeight', // rekhocchatā — "line height" → line-height
26
+ 'पाठसंरेखणम्': 'textAlign', // pāṭha-saṃrekhaṇam — "text alignment" → text-align
27
+ 'उच्चता': 'height', // uccatā — "height"
28
+ 'अन्तरालः': 'padding', // antarāla — "inner gap" → padding
29
+ 'बाह्यान्तरः': 'margin', // bāhyāntara — "outer gap" → margin
30
+ 'सीमा': 'border', // sīmā — "border"
31
+ 'कोणवृत्तिः': 'borderRadius', // koṇavṛtti — "corner-rounding"
32
+
33
+ // layout
34
+ 'प्रदर्शनम्': 'display', // pradarśana — "display"
35
+ 'स्थितिः': 'position', // sthiti — "position"
36
+ 'शीर्षात्': 'top', // śīrṣāt — "from the top" → top
37
+ 'अधस्तात्': 'bottom', // adhastāt — "from below" → bottom
38
+ 'वामतः': 'left', // vāmataḥ — "from the left" → left
39
+ 'दक्षिणतः': 'right', // dakṣiṇataḥ — "from the right" → right
40
+ 'स्तरः': 'zIndex', // stara — "layer" → z-index
41
+ 'दिक्': 'flexDirection', // dik — "direction"
42
+ 'न्यायः': 'justifyContent', // nyāya — "arrangement" → justify
43
+ 'मेलनम्': 'alignItems', // melana — "alignment" → align-items
44
+ 'अन्तरम्': 'gap', // antara — "gap"
45
+ };
46
+
47
+ // --- value words: Sanskrit → CSS value ---
48
+ export const STYLE_VALUES = {
49
+ // colors (named)
50
+ 'रक्तः': 'crimson', // rakta — "red"
51
+ 'नीलः': 'navy', // nīla — "blue"
52
+ 'हरितः': 'green', // harita — "green"
53
+ 'पीतः': 'gold', // pīta — "yellow/gold"
54
+ 'श्वेतः': 'white', // śveta — "white"
55
+ 'कृष्णः': 'black', // kṛṣṇa — "black"
56
+ 'धूसरः': 'gray', // dhūsara — "gray"
57
+ 'केसरः': 'saffron', // kesara — "saffron" (→ #F4C430 via palette below)
58
+ 'अरुणः': 'tomato', // aruṇa — "reddish"
59
+ 'श्यामः': 'slategray', // śyāma — "dark"
60
+
61
+ // display / layout keywords
62
+ 'प्रवाहः': 'flex', // pravāha — "flow" → flex
63
+ 'खण्डः': 'block', // khaṇḍa — "block"
64
+ 'रेखा': 'inline', // rekhā — "line" → inline
65
+ 'अदृश्यम्': 'none', // adṛśya — "invisible" → none
66
+ 'केन्द्रम्': 'center', // kendra — "center"
67
+ 'आदिः': 'flex-start', // ādi — "beginning"
68
+ 'अन्तः': 'flex-end', // anta — "end"
69
+ 'मध्ये': 'space-between',// madhye — "in between"
70
+ 'पङ्क्तिः': 'row', // paṅkti — "row"
71
+ 'स्तम्भः': 'column', // stambha — "column"
72
+
73
+ // font weights / alignment
74
+ 'गुरुः': 'bold', // guru — "heavy" → bold
75
+ 'लघुः': 'normal', // laghu — "light" → normal
76
+ 'वामम्': 'left', // vāma — "left"
77
+ 'दक्षिणम्': 'right', // dakṣiṇa — "right"
78
+ 'रेखाङ्कनम्': 'underline', // rekhāṅkana — "underline"
79
+ };
80
+
81
+ // A few named colors that aren't standard CSS keywords get hex values.
82
+ const COLOR_HEX = {
83
+ saffron: '#F4C430',
84
+ };
85
+
86
+ // Translate one property name. Unknown names pass through unchanged
87
+ // (so raw CSS like 'cursor' still works).
88
+ export function styleProp(name) {
89
+ return STYLE_PROPS[name] || name;
90
+ }
91
+
92
+ // Translate one value WORD. Unknown words pass through unchanged.
93
+ export function styleValue(word) {
94
+ const v = STYLE_VALUES[word];
95
+ if (v === undefined) return word;
96
+ return COLOR_HEX[v] || v;
97
+ }
98
+
99
+ // Is this bare word a known Sanskrit style value? (If not, codegen treats
100
+ // it as a variable reference rather than a CSS literal.)
101
+ export function isStyleWord(word) {
102
+ return Object.prototype.hasOwnProperty.call(STYLE_VALUES, word);
103
+ }