Vāc (वाच्, "speech / word") is an experimental programming language whose syntax and semantics are derived from the structural principles of Sanskrit grammar (Pāṇini's Aṣṭādhyāyī). It is general-purpose and imperative — comparable to Python or JavaScript in expressive power — but it encodes program structure the way Sanskrit encodes meaning: through morphological role marking rather than word order.
The research question: can an LLM express the same program in fewer tokens when the language carries semantic roles in the grammar itself?
Sanskrit marks each noun's role in an action with a short case suffix
(vibhakti). Word order is therefore free: the suffix, not the position, says
what each word does. Vāc uses this for function arguments — arguments are
tagged by role, not position, so calls are order-independent and
self-documenting, and the "keyword" is a 1–2 character morpheme instead of a
long name= label.
| Kāraka | Role | Suffix | PL meaning |
|---|---|---|---|
| kartṛ (कर्तृ) | agent | (bare) | the result / bound name |
| karma (कर्म) | object | -m |
primary input |
| karaṇa (करण) | instrument | -ena |
"by means of" / second operand |
| sampradāna (संप्रदान) | recipient | -aya |
destination / target |
| apādāna (अपादान) | source | -at |
source / "from" |
| sambandha (संबंध) | relation | -sya |
possession / field of |
| adhikaraṇa (अधिकरण) | locus | -e |
scope / index / "in" |
Sanskrit clauses are verb-final. In Vāc a clause is written [ operands… verb ]
— the operands (case-marked) come first, the verb (function name) last:
[10m 2ena bhaga] # divide: karma(10) by karaṇa(2) -> 5
[2ena 10m bhaga] # identical — order is free, roles are fixed
Operations are named with Sanskrit verbal roots (yoga = add, guṇa =
multiply, vada = speak/print). Identifiers compound freely like Sanskrit
nominal compounds, packing multi-word concepts into one token.
karya samkalana(am, bena): # function "addition" of karma a and karaṇa b
phala a + b
x bhavati 5m 3ena samkalana # x "becomes" the clause's result -> 8
x vada # speak x
yadi x > 5:
"mahat" vada # "large"
anyatha:
"alpa" vada # "small"
A verb-final clause needs no brackets at statement level or as a binding's
value — the line boundary delimits it. Brackets [ … ] are only needed to embed
a call inside an arithmetic expression, e.g. n * [(n - 1)m gunanfala].
| Vāc | Sanskrit | Means |
|---|---|---|
karya |
कार्य | function / task |
phala |
फल | return ("the fruit") |
bhavati |
भवति | becomes (binding) |
yadi |
यदि | if |
athava |
अथवा | else-if (elif) |
anyatha |
अन्यथा | else |
yavat |
यावत् | while ("as long as") |
vada |
वद | print ("speak") |
satya / asatya |
सत्य / असत्य | true / false |
sunya |
शून्य | null / void |
ca / va / na |
च / वा / न | and / or / not |
python3 vac.py examples/factorial.vac
python3 vac.py -c '"namaste" vada'
Identifiers are held identical across both languages (they're arbitrary), so the measurement isolates what the language imposes — keywords and structure.
bench/compare.py— token counts undertiktoken(LLM cost today) and under each language's own lexer (grammar density).bench/keyword_select.py— tokenizer-aware keyword selection (strategy 2, zero training): picks the cheapest Sanskrit surface form per keyword for a target tokenizer (e.g.bhavati→भव); the chosen dialect still runs.bench/tokenizer_compare.py— trains a matched BPE on a Vāc corpus and an equal one on Python (bench/bpe.py,bench/corpus_gen.py).
| measurement | Vāc vs Python |
|---|---|
| structural lexemes (tokenizer-neutral) | 0.85× — grammar denser |
| cl100k_base (today) | 1.26× |
| o200k_base + keyword-optimized (no training) | 1.07× — ≈ parity |
| matched BPE (~400 vocab) | 1.02× — ≈ parity |
The honest headline: the kāraka grammar is ~15% denser in lexemes, but whitespace and case-suffix subtokens erode that to rough parity on real tokenizers — modest, not dramatic.
Full writeup in FINDINGS.md.