वाक्

वाक् · Vāk — version 0.12.1

A programming language whose type system is Pāṇini's grammar, and whose own compiler is written in Sanskrit

Sanskrit does not decide meaning by word order. It marks the role each participant plays in an action — the agent, the instrument, the source — and lets the words fall where they like. Vāk makes those roles a type system, and then writes its entire front end in itself.

कारकाणि

the part that exists nowhere else

A parameter declares the role it plays in the action, and the compiler checks the grammar of your function the way Pāṇini checked the grammar of a sentence.

Here संग्रहः is the अपादानम् — the ablative, what we draw from — and परीक्षा is the करणम्, the instrumental, the means by which. Because the roles are marked, the arguments carry their own labels and the order stops mattering. All three of these are one call:

कार्यम् छानय(अपादानम् सूची संग्रहः, करणम् कार्यम् परीक्षा) : सूची {
    सूची फलम् = []।
    प्रत्येकम् (अङ्गम् अन्तः संग्रहः) {
        यदि (परीक्षा(अङ्गम्)) { योजय(फलम्, अङ्गम्)। }
    }
    प्रत्यागच्छ फलम्।
}
कार्यम् समः(पूर्णाङ्कः सङ्ख्या) : सत्यता { प्रत्यागच्छ सङ्ख्या % २ == ०। }
मान अङ्काः = [१, २, ३, ४, ५, ६]।

# त्रीणि आह्वानानि, एकम् एव अर्थः — विभक्तिः क्रमम् मोचयति।
मुद्रय छानय(अङ्काः, समः)।
मुद्रय छानय(अपादानम्: अङ्काः, करणम्: समः)।
मुद्रय छानय(करणम्: समः, अपादानम्: अङ्काः)।

फलम् · what it prints

[2, 4, 6]
[2, 4, 6]
[2, 4, 6]
चालय · run this yourself →
The same insight that frees a Sanskrit sentence from word order, applied to a function call.

The analyser enforces the grammar of roles. Pāṇini's rule that an action has one agent and one patient is a real constraint: two कर्ता parameters is an error, so is a label naming a role the function never declared. Word order is deliberately not constrained — that is the whole point of marking the roles.

अनुक्तम् कारकम् · the role that goes unsaid

Sanskrit does not require every kāraka to appear. देवदत्तः पचति — “Devadatta cooks” — is a whole sentence, and it names neither what is cooked nor by what means. Those roles exist; this sentence does not state them. A parameter with a default says exactly that.

And because the roles are named rather than counted, the default may sit anywhere in the list — not only at the end, as a language with positional arguments has to insist:

# अनुक्तम् कारकम् — कारकम् अस्ति, अस्मिन् आह्वाने न उक्तम्।
# The करणम् sits in the middle and is still the one left out.
कार्यम् लिखतु(कर्ता शब्दः लेखकः,
              करणम् शब्दः साधनम् = "लेखन्या",
              कर्म शब्दः ग्रन्थः) : शब्दः {
    प्रत्यागच्छ लेखकः + " '" + ग्रन्थः + "' " + साधनम् + " लिखति"।
}

मुद्रय लिखतु(कर्ता: "कालिदासः", कर्म: "मेघदूतम्")।
मुद्रय लिखतु(कर्म: "रघुवंशम्", करणम्: "तूलिकया", कर्ता: "कालिदासः")।

फलम् · what it prints

कालिदासः 'मेघदूतम्' लेखन्या लिखति
कालिदासः 'रघुवंशम्' तूलिकया लिखति
चालय · run this yourself →
A positional language cannot leave out an argument in the middle. Marked roles can.

A role that is neither supplied nor defaulted is an error that names it — न्यूनाः प्राचलाः: कर्म — because with the order free, a position would tell you nothing about which role is missing.

लक्षणम् & प्रयुज् · reading the roles, then building the call

The roles are not only checked before the program runs — a program can read them while it runs. लक्षणम् is the grammarians' word for a defining characteristic, and a function's is its parameters: their names, types, roles, and whether each may go unstated. प्रयुज्to yoke, to apply — then makes the call, from a सूची in order or a कोशः by role.

कार्यम् लिखतु(कर्ता शब्दः लेखकः,
              करणम् शब्दः साधनम् = "लेखन्या",
              कर्म शब्दः ग्रन्थः) : शब्दः {
    प्रत्यागच्छ लेखकः + " '" + ग्रन्थः + "' " + साधनम् + " लिखति"।
}

# लक्षणम् — यत् कार्यम् आत्मनः विषये घोषयति।
प्रत्येकम् (प्रा अन्तः लक्षणम्(लिखतु).प्राचलाः) {
    मुद्रय प्रा.कारकम्, प्रा.प्रकारः, प्रा.नाम, प्रा.मूलमस्ति।
}

# प्रयुज् — कारकनाम्ना रचितम् आह्वानम्।
मुद्रय प्रयुज्(लिखतु, {"कर्ता": "कालिदासः", "कर्म": "मेघदूतम्"})।

फलम् · what it prints

कर्ता शब्दः लेखकः असत्य
करणम् शब्दः साधनम् सत्य
कर्म शब्दः ग्रन्थः असत्य
कालिदासः 'मेघदूतम्' लेखन्या लिखति
चालय · run this yourself →
The signature read at run time, and a call assembled from role names rather than written out.

Together they are what a bridge from a Sanskrit parser would stand on: a kāraka analysis is exactly the shape प्रयुज् accepts.

भाषा

the language itself

Statements end with a danda — the full stop Sanskrit has used since manuscripts. Types are gradual: write none and nothing is checked, write one and it binds everywhere. विकल्पः is Pāṇini's word for an optional alternative, and पक्षे is the locative, "in this case":

कार्यम् वासरनाम(कर्म पूर्णाङ्कः वारः) : शब्दः {
    विकल्पः (वारः) {
        पक्षे १: प्रत्यागच्छ "सोमवासरः"पक्षे ६, ७: प्रत्यागच्छ "सप्ताहान्तः"अन्यथा: प्रत्यागच्छ "अन्यः वासरः"।
    }
}
प्रत्येकम् (वारः अन्तः [१, ६, ३]) { मुद्रय वासरनाम(वारः)। }

फलम् · what it prints

सोमवासरः
सप्ताहान्तः
अन्यः वासरः
चालय · run this yourself →

कुरु · the loop that asks afterwards

Four loop forms, and this is the one whose body runs before anything is asked. कुरु is the imperative — do! — and the test that closes it is written with यावत्, so the pair reads as one sentence: do this, as long as that.

# अङ्काः कति — शून्यस्य अपि एकः अङ्कः अस्ति।
कार्यम् अङ्कगणना(कर्म पूर्णाङ्कः संख्या) : पूर्णाङ्कः {
    मान शेषः = संख्या।
    मान गणना = ०।
    कुरु {                          # शरीरम् प्रथमम्
        गणना = गणना + १।
        शेषः = पूर्ण(शेषः / १०)।
    } यावत् (शेषः > ०)।             # प्रश्नः पश्चात्
    प्रत्यागच्छ गणना।
}

मुद्रय अङ्कगणना(९८७६), अङ्कगणना(०)।

फलम् · what it prints

4 1
चालय · run this yourself →
Zero has one digit. A loop that tested first would report none — which is the whole reason the form exists.

It needed no new instruction. A यावत् loop already compiles to a conditional jump out and an unconditional jump back; कुरु is those same two in the other order, so every engine — the C runtime included — understood it the day it was added.

And none of it requires a Devanagari keyboard. Every keyword has an ASCII spelling, and ASCII numerals work everywhere:

karyam varga(purnankah a) : purnankah { pratyagaccha a * a; }
pratyekam (n antah [1, 2, 3, 4]) { mudraya n, "->", varga(n); }

फलम् · what it prints

1 -> 1
2 -> 4
3 -> 9
4 -> 16
चालय · run this yourself →
The same language, typed on a plain keyboard.

स्वयंसिद्धिः

it compiles itself

A language that depends on another language to exist has not quite escaped it.

Every stage of the front end is written in Vāk — and each is held to its Python counterpart at the finest granularity that stage allows: the lexer on every token's kind, lexeme, value and line; the parser on the whole syntax tree, node for node; the analyser on every diagnostic's code, line and wording; the compiler on every instruction, constant and line number.

stagewritten in Vākchecked against
lexerशब्दविभाजकः.vakevery token
parserव्याकरणम्.vakthe whole syntax tree
analyserअर्थविश्लेषकः.vakevery diagnostic, word for word
compilerसंकलकः.vakevery instruction
machineयन्त्रम्.vakbyte-identical output

A Vāk program can be run by five engines — a tree-walking interpreter, a bytecode VM, that VM written in Vāk, a VM in C, and वाक्.exe, the whole self-hosted toolchain compiled natively. All five are held to byte-identical output on every example, which is how any of this stays honest.

5engines, one output
48built-in functions
336tests
6kāraka roles
159 KBgzipped — the whole toolchain, in your browser

The page you are reading links to a real compiler. The playground is this entire toolchain compiled to WebAssembly — it lexes, parses, analyses, compiles and runs Vāk inside the browser. Your program never leaves the page — it is not uploaded, not compiled elsewhere, and not seen by anything but your own browser.

स्थापना

getting it

Three ways in, and none of them needs a package manager you do not already have.

In your browser

Nothing to install at all. The playground is this toolchain compiled to WebAssembly — 159 KB gzipped. Your program never leaves the page.

With pip

Python 3.10 or newer, and no third-party packages.

pip install vak-lang
vaak प्रोग्राम.vak

The distribution is vak-lang; the command is vaak.

A standalone binary

The whole toolchain in one file — no Python, no dependencies. Builds for Linux, macOS and Windows are attached to the latest release.

vaak प्रोग्राम.vak

You do not need a Devanagari keyboard. Every keyword has an ASCII spelling and ASCII numerals work everywhere, so a complete Vāk program fits on an ordinary keyboard. The playground also turns romanised typing into Devanagari as you write — in code, while leaving the inside of your strings and comments alone.

The manual covers the rest: verifying the install, UTF-8 terminals and fonts on each platform, building the binary yourself, and the VS Code extension.