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/LICENSE +21 -0
- package/README.md +974 -0
- package/package.json +47 -0
- package/src/analyzer.js +125 -0
- package/src/bundler.js +129 -0
- package/src/cli.js +99 -0
- package/src/codegen.js +864 -0
- package/src/devserver.js +148 -0
- package/src/errors.js +71 -0
- package/src/index.js +30 -0
- package/src/io-browser.js +31 -0
- package/src/io-node.js +102 -0
- package/src/karaka-web.js +49 -0
- package/src/keywords.js +64 -0
- package/src/lexer.js +140 -0
- package/src/parser.js +687 -0
- package/src/server-node.js +120 -0
- package/src/server.js +182 -0
- package/src/stdlib.js +137 -0
- package/src/style.js +103 -0
- package/src/symbols.js +194 -0
- package/src/vibhakti.js +87 -0
|
@@ -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
|
+
}
|