वाक् · Vāk — version 0.12.1
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.
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:
कार्यम् छानय(अपादानम् सूची संग्रहः, करणम् कार्यम् परीक्षा) : सूची {
सूची फलम् = []।
प्रत्येकम् (अङ्गम् अन्तः संग्रहः) {
यदि (परीक्षा(अङ्गम्)) { योजय(फलम्, अङ्गम्)। }
}
प्रत्यागच्छ फलम्।
}
कार्यम् समः(पूर्णाङ्कः सङ्ख्या) : सत्यता { प्रत्यागच्छ सङ्ख्या % २ == ०। }
मान अङ्काः = [१, २, ३, ४, ५, ६]।
# त्रीणि आह्वानानि, एकम् एव अर्थः — विभक्तिः क्रमम् मोचयति।
मुद्रय छानय(अङ्काः, समः)।
मुद्रय छानय(अपादानम्: अङ्काः, करणम्: समः)।
मुद्रय छानय(करणम्: समः, अपादानम्: अङ्काः)।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.
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 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.
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 →
Together they are what a bridge from a Sanskrit parser would stand on: a
kāraka analysis is exactly the shape प्रयुज् accepts.
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":
कार्यम् वासरनाम(कर्म पूर्णाङ्कः वारः) : शब्दः {
विकल्पः (वारः) {
पक्षे १: प्रत्यागच्छ "सोमवासरः"।
पक्षे ६, ७: प्रत्यागच्छ "सप्ताहान्तः"।
अन्यथा: प्रत्यागच्छ "अन्यः वासरः"।
}
}
प्रत्येकम् (वारः अन्तः [१, ६, ३]) { मुद्रय वासरनाम(वारः)। }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.
# अङ्काः कति — शून्यस्य अपि एकः अङ्कः अस्ति।
कार्यम् अङ्कगणना(कर्म पूर्णाङ्कः संख्या) : पूर्णाङ्कः {
मान शेषः = संख्या।
मान गणना = ०।
कुरु { # शरीरम् प्रथमम्
गणना = गणना + १।
शेषः = पूर्ण(शेषः / १०)।
} यावत् (शेषः > ०)। # प्रश्नः पश्चात्
प्रत्यागच्छ गणना।
}
मुद्रय अङ्कगणना(९८७६), अङ्कगणना(०)।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); }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.
| stage | written in Vāk | checked against |
|---|---|---|
| lexer | शब्दविभाजकः.vak | every token |
| parser | व्याकरणम्.vak | the whole syntax tree |
| analyser | अर्थविश्लेषकः.vak | every diagnostic, word for word |
| compiler | संकलकः.vak | every instruction |
| machine | यन्त्रम्.vak | byte-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.
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.
Three ways in, and none of them needs a package manager you do not already have.
१
Nothing to install at all. The playground is this toolchain compiled to WebAssembly — 159 KB gzipped. Your program never leaves the page.
२
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.
३
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.