A programming language whose keywords, whose type system, and whose own compiler are written in Sanskrit

Vāk takes Pāṇini's analysis of a sentence — that meaning comes from the role each participant plays, not from where it sits — and makes it a type system. Every stage of the language is written in the language itself.

# कारकाणि — a parameter declares the role it plays in the action
कार्यम् छानय(अपादानम् सूची संग्रहः, करणम् कार्यम् परीक्षा) : सूची {
    सूची फलम् = []।
    प्रत्येकम् (अङ्गम् अन्तः संग्रहः) {
        यदि (परीक्षा(अङ्गम्)) { योजय(फलम्, अङ्गम्)। }
    }
    प्रत्यागच्छ फलम्।
}

मुद्रय छानय(करणम्: समः, अपादानम्: [, , , , , ])।

स्थापनाInstalling Vāk

There is no package to install and nothing to configure. Pick whichever of these four suits you — they run the same language.

The standalone binary nothing else required

वाक्.exe ships in the repository. It is the whole toolchain — lexer, parser, analyser, compiler and machine — compiled to one file, and it needs neither Python nor anything else. Copy it where you like and run it:

वाक्.exe प्रोग्राम.vak

The binary in the repository is built for 64-bit Windows. On Linux or macOS, build your own — see Building the binary below; the C sources are platform-neutral.

With pip Python 3.10 or newer

Vāk pulls in no third-party packages. A standard Python is all it needs; it was developed on 3.13.

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

Three names, and they differ on purpose. The distribution is vak-lang, the command and the import name are vaak. vak on PyPI is an unrelated bioacoustics package, and shadowing it would let pip overwrite one install with the other.

This gives you the interpreter, the bytecode virtual machine, the native back end, the REPL and the standard library, and the vak command works from any directory.

From a clone everything a wheel does not carry

Clone it for the C runtime, the Vāk-written toolchain behind --self, the documentation generators and the editor extension:

git clone https://github.com/vidyadheeshp/vak.git
cd vak
python -m vaak examples/01_namaste.vak

Working from a clone, stay in the repository root. python -m vaak finds the package only because you are standing in the directory that contains it; from anywhere else you will get No module named vak. Installing with pip avoids this entirely. To run from elsewhere without installing, put the root on the module path:

# bash / zsh
PYTHONPATH=/path/to/vak python -m vaak ~/प्रोग्राम.vak

# PowerShell
$env:PYTHONPATH = "C:\path\to\vak"; python -m vaak प्रोग्राम.vak

On Windows the two launchers in the repository do this and set UTF-8 for you:

.\vaak.ps1 examples/01_namaste.vak     # PowerShell
vaak.cmd examples/01_namaste.vak       # cmd.exe

In a browser nothing installed at all

The playground is this same toolchain compiled to WebAssembly. It runs entirely in the page — your program is not uploaded, and nothing you type leaves your machine. Use it to try the language before deciding whether to install anything.

Checking that it works

python -m vaak --version          # वाक् (Vāk) 0.12.1
python -m vaak --builtins         # the 39 built-in functions
python -m vaak                    # संवादः — the interactive session

Then a program of one line. Put this in परीक्षा.vak and run it — if you see the greeting, everything works:

मुद्रय "नमस्ते जगत्"

A terminal that can show Devanagari

Two separate things have to be right: the console must be in UTF-8, and the font must contain Devanagari.

  • Windows. The CLI reconfigures its own output, so Devanagari normally works in Windows Terminal. If you see boxes, the font is the problem, not the encoding — choose Nirmala UI or Noto Sans Devanagari. The older conhost console may also need chcp 65001.
  • Linux and macOS. A UTF-8 locale is the default almost everywhere; locale will confirm it. Most terminal fonts fall back to a system Devanagari face automatically.

You do not have to type Devanagari at all. Every keyword has an ASCII spelling and ASCII numerals work everywhere, so a complete Vāk program can be written on a plain keyboard. The switch at the top of this page shows any example either way.

Building the binary

The native back end turns a program into C and hands it to gcc, so a C compiler is the only extra requirement. On Windows, w64devkit is the one Vāk looks for by default; on Linux and macOS the system compiler is already there.

python -m vaak --native प्रोग्राम.vak       # produce an executable
python -m vaak --run-native प्रोग्राम.vak   # build it and run it

If no compiler is found, Vāk says so rather than failing obscurely.

The editor extension

Copy the vscode-vak folder from the repository into your VS Code extensions directory and reload the window:

# Windows
%USERPROFILE%\.vscode\extensions\vscode-vak

# macOS and Linux
~/.vscode/extensions/vscode-vak

Then point it at whichever toolchain you installed — set vak.executable to your वाक्.exe, or leave that empty and set vak.pythonPath so it uses python -m vaak. You get highlighting, the .vak file icon, romanised typing that becomes Devanagari, and the analyser's diagnostics as you save — the same ones listed in दोषसूची.

चालनम्Running Vāk

Three ways to run a program, all producing identical output. The first needs Python; the last needs nothing at all.

python -m vaak प्रोग्राम.vak          # the tree-walking interpreter
python -m vaak --vm प्रोग्राम.vak     # compile to bytecode, run on the SanskritVM
./वाक्.exe प्रोग्राम.vak              # the self-hosted toolchain, no Python

Two more you will want early — the first analyses without running, the second starts a REPL:

./वाक्.exe --परीक्षा प्रोग्राम.vak    # report problems, run nothing
python -m vaak                        # संवादः — the REPL

The संवादः remembers. What you type is kept in ~/.vaak_history and read back next time, so the up arrow reaches yesterday's session. This needs Python's readline, which is not in the standard library on Windows — there the REPL works exactly as before and simply forgets between sessions.

The full set of options, read from the argument parser itself. Four carry a Sanskrit alias:

विकल्पःसंस्कृतम्what it does
--tokensprint the token stream and stop
--astprint the syntax tree and stop
--checkrun the semantic analyser and stop, without executing
--no-checkskip the semantic analyser and run the program directly
--vmcompile to bytecode and run on the SanskritVM
--bytecode--vyakhyadisassemble the compiled bytecode and stop
--self--svayamcompile with the Vāk-written toolchain, then run on the VM
--self-vm--svayam-yantramlex, parse, compile AND run entirely in Vāk
--native--deshiyacompile to a standalone native executable (needs gcc)
--run-nativecompile natively and run the result
--builtinslist the built-in functions
--karakaslist the kāraka roles
--versionprint the version and stop

An editor. The vscode-vak extension in the repository gives Vāk syntax highlighting, the .vak file icon, romanised typing that becomes Devanagari as you write, and live diagnostics from the real analyser — the same messages listed in दोषसूची.

Fonts. No monospace coding font contains Devanagari, so an editor falls back to a proportional face and columns drift. Setting editor.fontFamily to a chain ending in Nirmala UI or Noto Sans Devanagari fixes the rendering, though nothing makes Devanagari truly monospaced. Installation is covered in स्थापना.

वाक्यानिStatements and the danda

A statement ends with a danda — the full stop Sanskrit has used since manuscripts. , the double danda, ends a section. A semicolon means the same thing and is easier to type.

मुद्रय "नमस्ते जगत्"मान क = ;
मुद्रय क॥

मुद्रय prints its arguments separated by a space. Comments run from # to the end of the line.

चराःVariables and numerals

मान declares a variable, ध्रुव a constant. A type may be written in place of मान, and then it binds — the analyser and every engine hold you to it.

मान नाम = "वाक्"ध्रुव पाई = ३.१४१५९पूर्णाङ्कः वर्षम् = २०२६शब्दः अभिवादनम् = "नमस्ते"।

वर्षम् += मुद्रय नाम, वर्षम्, पाई।

Devanagari and ASCII numerals are the same numbers — २०२६ and 2026 are one value, and you may mix them freely. Output uses whichever form the value was written in where that is knowable, and देवनागरी() converts on demand.

प्रवाहःControl flow

यदि and अन्यथा branch. Parentheses around the condition are optional; the braces are not.

यदि (अङ्कः > १००) {
    मुद्रय "महान्"।
} अन्यथा यदि (अङ्कः > १०) {
    मुद्रय "मध्यमः"।
} अन्यथा {
    मुद्रय "अल्पः"।
}

Four loops, because four different things are worth saying. यावत् repeats while a condition holds, कुरु does the same but asks afterwards, आवृत्तिः repeats a fixed number of times, and प्रत्येकम् … अन्तः walks a collection.

यावत् (क < १०) { क += । }

कुरु { क += । } यावत् (क < १०)।

आवृत्तिः () { मुद्रय "ॐ"। }

प्रत्येकम् (अङ्कः अन्तः [, , , ]) {
    यदि (अङ्कः == ) { अनुवर्त। }
    मुद्रय अङ्कः।
}

विरम leaves a loop, अनुवर्त starts its next turn.

कुरु · asking after, not before

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

कुरु {
    उत्तरम् = पठ("नाम? ")।
} यावत् (दीर्घता(उत्तरम्) == )।
Read at least once, then keep asking while nothing was typed. यावत् alone cannot express this — it would have to test a variable that does not exist yet.

अनुवर्त inside a कुरु jumps to the test rather than back to the top of the body, since the body has already had its turn.

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

विकल्पः · Choosing among alternatives

Where a chain of अन्यथा यदि only compares one value over and over, विकल्पः says it once. The word is Pāṇini's own: a vikalpa is an optional alternative in a rule. Each alternative is introduced by पक्षे — the locative, “in this case” — matching दोषे, the locative Vāk already uses for catch.

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

One पक्षे may carry several values, separated by commas. अन्यथा is the fallback, and it is the fallback wherever it is written. The subject is evaluated once.

पक्षाः do not fall through. The alternative that matches is the only one that runs, so nothing has to be written to stop it. That frees विरम to keep its ordinary meaning: inside a विकल्पः it leaves the enclosing loop, not the विकल्पः — and outside a loop it is an error, exactly as it always was.

The analyser reports a पक्षः whose type could never match the subject, and a value given twice — neither could ever run. A विकल्पः without an अन्यथा is only advice, not an error, but it does mean an unmatched subject does nothing at all.

A विकल्पः also counts as returning on every path, so long as it has an अन्यथा and every पक्षः returns — which is why the function above needs no closing प्रत्यागच्छ.

कार्याणिFunctions

कार्यम् defines a function. A return type after the colon is optional and binding. Functions are values: pass them, return them, close over their surroundings.

कार्यम् क्षेत्रफलम्(पूर्णाङ्कः दैर्घ्यम्, पूर्णाङ्कः विस्तारः) : पूर्णाङ्कः {
    प्रत्यागच्छ दैर्घ्यम् * विस्तारः।
}

कार्यम् योजकः(आधारः) : कार्यम् {
    प्रत्यागच्छ कार्यम्(क) : पूर्णाङ्कः { प्रत्यागच्छ आधारः + क। }।
}

मान दश = योजकः(१०)।
मुद्रय क्षेत्रफलम्(, ), दश()।

Functions are hoisted: a function may call one defined later in the same block.

A parameter may carry a default, which must be written out — a number, a string, सत्य, असत्य or शून्य. Nothing is computed at the call, so the value cannot be shared between calls and the mutable-default trap familiar from Python cannot arise.

कार्यम् अभिवादय(शब्दः नाम, शब्दः वचनम् = "नमस्ते") : शब्दः {
    प्रत्यागच्छ वचनम् + ", " + नाम + "।"।
}

मुद्रय अभिवादय("रामः")।              # नमस्ते, रामः।
मुद्रय अभिवादय("रामः", "स्वागतम्")।   # स्वागतम्, रामः।

Where the parameters are unmarked, a default may not sit before a parameter that has none — position is all the call has to go on. Where every parameter declares its kāraka, that restriction lifts.

प्रयुज् · applying a call you did not write

Sometimes the arguments are computed rather than written out. प्रयुज्prayuj, “to yoke, to apply” — takes a कार्यम् and either a सूची, which fills the parameters in order, or a कोशः whose keys are kāraka names, which fills them by role:

प्रयुज्(योगः, [, , ])।                        # योगः(१, २, ३)
प्रयुज्(लिखतु, {"कर्ता": "कालिदासः", "कर्म": "मेघदूतम्"})।
The second form is a call whose roles were decided while the program ran.

This is what a wrapper needs — a function standing in front of another without knowing how many arguments it takes:

कार्यम् गणयित्वा(कार्यम् क, सूची अर्घाः) {
    मुद्रय "आह्वानम्:", दीर्घता(अर्घाः)।
    प्रत्यागच्छ प्रयुज्(क, अर्घाः)।
}

प्रयुज् becomes a single instruction, because the number of arguments is only known once the program is running. That has one consequence worth stating plainly: it must be called, and cannot be passed around as a value. All five engines refuse the indirect form rather than three allowing it and two not.

लक्षणम् · what a कार्यम् declares

Lakṣaṇa is the grammarians' word for a defining characteristic — the mark by which a thing is known. A function's mark is its parameters, and लक्षणम् hands them back as an ordinary कोशः:

कोशः ल = लक्षणम्(छानय)।
मुद्रय ल.नाम, ल.प्राचलसंख्या, ल.प्रतिफलप्रकारः।
प्रत्येकम् (प्रा अन्तः ल.प्राचलाः) {
    मुद्रय प्रा.कारकम्, प्रा.प्रकारः, प्रा.नाम, प्रा.मूलमस्ति।
}
छानय 2 सूची / अपादानम् सूची संग्रहः असत्य / करणम् किमपि परीक्षा सत्य

The two are meant to be used together: read the roles a function wants, then build the call from them.

कोशः अर्घाः = {}।
प्रत्येकम् (प्रा अन्तः लक्षणम्(लिखतु).प्राचलाः) {
    यदि ( प्रा.मूलमस्ति) { अर्घाः[प्रा.कारकम्] = मूल्यम्_आनय(प्रा.कारकम्)। }
}
प्रत्यागच्छ प्रयुज्(लिखतु, अर्घाः)।
Every role the function requires, gathered by name and applied.

For a built-in, प्रतिफलप्रकारः comes back as किमपि. That is the analyser's knowledge rather than the runtime's, and two of the five engines have no way to reach it — reporting it from the three that can would be a divergence.

संग्रहाःLists and dictionaries

सूची is an ordered list, कोशः a dictionary — literally a treasury, the word Sanskrit uses for a lexicon. Negative indices count from the end.

मान अङ्काः = [, , , , ]।
मुद्रय अङ्काः[], अङ्काः[-], दीर्घता(अङ्काः)।
योजय(अङ्काः, )।

मान छात्रः = { "नाम": "रामः", "वयः": २० }।
मुद्रय छात्रः.नाम, छात्रः["वयः"]।
छात्रः.नगरम् = "काशी"

A dictionary key may be reached with a dot or with brackets; the dot form is the same lookup written more quietly.

प्रकाराःTypes

Typing is gradual. Write no type and nothing is checked; write one and it is enforced everywhere — at declaration, at assignment, at every call, and on the way out of a function. किमपि flows both ways, so only a provable mismatch is an error.

प्रकारःwhat it holds
पूर्णाङ्कःwhole number — pūrṇāṅkaḥ, “complete-numbered”
दशांशःa number with a fractional part — daśāṃśaḥ, “tenth-part”
अङ्कःeither kind of number
शब्दःtext — śabdaḥ, the word for word
सत्यताtruth — सत्य or असत्य
सूचीa list
कोशःa dictionary
कार्यम्a function
किमपिanything at all — kimapi, “whatever”
शून्यम्nothing — śūnyam, the zero India gave the world
पूर्णाङ्कः आयुः = २५।
आयुः = "पञ्चविंशतिः"# प्रकारदोषः — शब्दः दीयते, पूर्णाङ्कः अपेक्षितः
The analyser reports this before the program runs.

कारकाणिKāraka roles

This is the part of Vāk that exists nowhere else.

Pāṇini analysed a sentence not by word order but by kāraka — the role each participant plays in the action. Vāk lets a parameter declare its role, and then checks the grammar of your function the way Pāṇini checked the grammar of a sentence.

कारकम्विभक्तिः · what the role means
कर्ताप्रथमा (nominative) — the agent, who acts
कर्मद्वितीया (accusative) — the patient, what the action most wants
करणम्तृतीया (instrumental) — the means by which it is done
सम्प्रदानम्चतुर्थी (dative) — the recipient it is given to
अपादानम्पञ्चमी (ablative) — the source it departs from
अधिकरणम्सप्तमी (locative) — the locus it happens in
कार्यम् छानय(अपादानम् सूची संग्रहः, करणम् कार्यम् परीक्षा) : सूची {
    सूची फलम् = []।
    प्रत्येकम् (अङ्गम् अन्तः संग्रहः) {
        यदि (परीक्षा(अङ्गम्)) { योजय(फलम्, अङ्गम्)। }
    }
    प्रत्यागच्छ फलम्।
}
The collection is what we draw from; the test is the means by which.

Because the roles are marked, the arguments carry their own labels and the order stops mattering — exactly the freedom case marking gives a Sanskrit sentence. All three of these calls are the same call:

छानय(अङ्काः, समः)।
छानय(अपादानम्: अङ्काः, करणम्: समः)।
छानय(करणम्: समः, अपादानम्: अङ्काः)।

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

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

A parameter with a default says exactly that. The role is part of the action, and this call leaves it unexpressed.

कार्यम् छानय(अपादानम् सूची संग्रहः, करणम् किमपि परीक्षा = शून्य) : सूची {
    यदि (परीक्षा == शून्य) { प्रत्यागच्छ संग्रहः। }
    सूची फलम् = []।
    प्रत्येकम् (अङ्गम् अन्तः संग्रहः) {
        यदि (परीक्षा(अङ्गम्)) { योजय(फलम्, अङ्गम्)। }
    }
    प्रत्यागच्छ फलम्।
}

छानय(अपादानम्: अङ्काः)।              # करणम् अनुक्तम् — everything comes back
छानय(करणम्: समः, अपादानम्: अङ्काः)।   # both stated, in the other order

Because the roles are named rather than counted, a default may sit anywhere in the parameter list — not only at the end, as a language with positional arguments must insist. A call names the roles it supplies and says nothing about the rest:

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

लिखतु(कर्ता: "कालिदासः", कर्म: "मेघदूतम्")।   # साधनम् अनुक्तम्
The करणम् sits in the middle and is still the one left out.

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

What gets checked. Pāṇini's rule that an action has one agent and one patient is enforced: two कर्ता parameters, or two कर्म, is an error. A label that names no declared role is an error, and so is giving the same role twice. Word order is deliberately not enforced — that is the whole point of marking the roles.

Softer observations come back as सूचनाः rather than errors: a करणम् that is not a means, an अपादानम् that is not a collection, a function with a कर्म that returns nothing.

१०

अर्थविश्लेषकःThe semantic analyser

Before anything runs, the analyser walks the syntax tree and reports what it can prove wrong: undefined names, type mismatches, assignments to constants, wrong argument counts, विरम outside a loop, kāraka violations, unreachable code, a declared return type some path does not satisfy.

$ ./वाक्.exe --परीक्षा प्रोग्राम.vak
अर्थदोषः (Semantic Analysis) — 2 दोषाः, 0 सूचनाः
  दोषः [ध्रुवदोषः] प्रोग्राम.vak:6 — ध्रुवः 'सीमा' न परिवर्तनीयः / cannot reassign the constant 'सीमा'
         6 | सीमा = ११।
  दोषः [नामदोषः] प्रोग्राम.vak:7 — अपरिभाषितम् नाम 'अज्ञातम्' / undefined name 'अज्ञातम्'
         7 | मुद्रय बाह्यम्(), अज्ञातम्।

Every message is bilingual, and every diagnostic carries a Sanskrit code — नामदोषः, प्रकारदोषः, कारकदोषः, ध्रुवदोषः, प्राचलदोषः, प्रवाहदोषः. Errors stop the program; सूचनाः are advice and do not.

The analyser exists twice: once in Python and once written in Vāk itself. The two are compared on sixty-five files — every example, the standard library, forty deliberately broken programs, and the toolchain's own source — and must produce identical diagnostics, in the same order, with the same wording.

११

दोषनिग्रहःExceptions

प्रयत्नः is the attempt, दोषे the locative — “in the event of an error” — and अन्ततः what happens at the end regardless. उत्सृज throws.

प्रयत्नः {
    यदि (हरः == ) {
        उत्सृज { "प्रकारः": "गणितदोषः", "सन्देशः": "शून्येन भागः" }।
    }
    मुद्रय अंशः / हरः।
} दोषे (द) {
    मुद्रय "दोषः प्राप्तः:", द.प्रकारः, "—", द.सन्देशः।
} अन्ततः {
    मुद्रय "समाप्तम्"।
}

The caught value is an ordinary कोशः carrying at least प्रकारः and सन्देशः. Runtime failures from the language itself arrive in the same shape, so one handler catches both.

१२

प्रदानम्Reading from the user

पठ reads one line from the user and hands it back as a शब्दः. Give it an argument and that is printed first, as a prompt.

शब्दः नाम = पठ("नाम किम्? ")।
मुद्रय "नमस्ते,", नाम + "!"पूर्णाङ्कः वयः = संख्या(पठ("वयः? "))।
मुद्रय "आगामिवर्षे भवतः वयः", वयः + , "भविष्यति।"
संख्या turns the line into a number — and it reads Devanagari numerals, so the user may type ३८ or 38.

What comes back is always text, even when it looks like a number, so wrap it in संख्या when you want arithmetic. A line that cannot be read as a number raises a catchable कार्यकालदोषः:

प्रयत्नः {
    पूर्णाङ्कः क = संख्या(पठ("अङ्कम् लिखतु: "))।
    मुद्रय "वर्गः:", क * क।
} दोषे (द) {
    मुद्रय "सः अङ्कः न आसीत् —", द.सन्देशः।
}

At the end of input पठ returns the empty string rather than failing, in every engine. So a program that reads until there is nothing left tests for it:

यावत् (सत्य) { शब्दः पं = पठ()। यदि (दीर्घता(पं) == ०) { विरम। } … }

That does mean an empty line and the end of input look alike. If you need to tell them apart, read the whole of standard input another way.

In the playground there is no terminal to type into, so the box under the editor is what पठ reads — one line per call.

१३

आनयModules and files

आनयbring — imports. A module is any .vak file; whatever it declares at the top level is what it exports.

आनय "गणितम्"# गणितम्.वर्गः(...)
आनय "गणितम्" इति ग।                  # ग.वर्गः(...)
आनय "गणितम्" तः वर्गः, क्रमगुणितम्।   # वर्गः(...)

Modules run once and are cached; a circular import is reported rather than hung on. Files are read and written with the built-ins:

शब्दः विषयम् = सञ्चिकापठ("काव्यम्.txt")।
सञ्चिकालिख("फलम्.txt", विषयम्)।
यदि (सञ्चिकास्ति("फलम्.txt")) { मुद्रय निर्देशिका(".")। }

१४

पुस्तकालयःThe standard library

Two modules ship with Vāk, and both are written in Vāk — they are ordinary .vak files you can read, and a fair sample of what the language looks like in use. Bring one in with आनय:

आनय "गणितम्"मुद्रय गणितम्.क्रमगुणितम्()।        # १२०

आनय "शब्दाः" तः विलोमः_वा।
मुद्रय विलोमः_वा("नयन")।            # सत्य

Every signature below is printed from the module's own source, so it is exactly what the file declares — including the kāraka role each parameter takes.

आनय "गणितम्"। Mathematics — two constants and sixteen functions.

गणितम्what it does
दशांशः पाईπ, to fifteen places
दशांशः ईe, the base of the natural logarithm
वर्गः(अङ्कः क) : अङ्कःक squared
घनः(अङ्कः क) : अङ्कःक cubed
निरपेक्षम्(अङ्कः क) : अङ्कःthe absolute value of क
चिह्नम्(अङ्कः क) : पूर्णाङ्कःthe sign of क — १, ०, or -१
क्रमगुणितम्(पूर्णाङ्कः न्) : पूर्णाङ्कःन् factorial
वर्गमूलम्(अङ्कः क) : दशांशःthe square root of क, by Newton's method
मसाभा(पूर्णाङ्कः अ, पूर्णाङ्कः ब) : पूर्णाङ्कःthe greatest common divisor of अ and ब
लसागु(पूर्णाङ्कः अ, पूर्णाङ्कः ब) : पूर्णाङ्कःthe least common multiple of अ and ब
अभाज्यः_वा(पूर्णाङ्कः न्) : सत्यताwhether न् is prime
अभाज्याः_यावत्(पूर्णाङ्कः सीमा) : सूचीevery prime up to सीमा, as a list
माध्यम्(अपादानम् सूची अङ्काः) : अङ्कःthe arithmetic mean of the list
मध्यमा(अपादानम् सूची अङ्काः) : अङ्कःthe median of the list
घातः(अङ्कः आधारः, पूर्णाङ्कः घाताङ्कः) : अङ्कःआधारः raised to the power घाताङ्कः
बहुलकः(अपादानम् सूची अङ्काः) : अङ्कःthe most frequent value; the smaller one when two tie
विचरणम्(अपादानम् सूची अङ्काः) : दशांशःthe population variance — the mean squared deviation
प्रमाणविचलनम्(अपादानम् सूची अङ्काः) : दशांशःthe population standard deviation

आनय "शब्दाः"। Strings — thirteen functions over शब्दः.

शब्दाःwhat it does
आदौ_अस्ति(अधिकरणम् शब्दः पूर्णः, कर्म शब्दः आदिः) : सत्यताwhether पूर्णः begins with आदिः
अन्ते_अस्ति(अधिकरणम् शब्दः पूर्णः, कर्म शब्दः अन्तः_) : सत्यताwhether पूर्णः ends with अन्तः_
पुनरावृत्तिः(कर्म शब्दः स, पूर्णाङ्कः वारम्) : शब्दःस repeated वारम् times
पूरय(कर्म शब्दः स, पूर्णाङ्कः दैर्घ्यम्, शब्दः अक्षरम्) : शब्दःस padded with अक्षरम् until it reaches दैर्घ्यम्
अक्षरसूची(कर्म शब्दः स) : सूचीस as a list of its characters
विलोमः_वा(कर्म शब्दः स) : सत्यताwhether स reads the same in both directions
गणय_अक्षरम्(कर्म शब्दः अक्षरम्, अधिकरणम् शब्दः वाक्यम्) : पूर्णाङ्कःhow many times अक्षरम् occurs in वाक्यम्
शब्दगणना(अधिकरणम् शब्दः वाक्यम्) : कोशःa dictionary of each word in वाक्यम् and its count
रिक्तम्_वा(कर्म शब्दः अ) : सत्यताwhether अ is a space, tab, newline or carriage return
आदौ_छिन्द(कर्म शब्दः स) : शब्दःस with leading whitespace removed
अन्ते_छिन्द(कर्म शब्दः स) : शब्दःस with trailing whitespace removed
छिन्द(कर्म शब्दः स) : शब्दःस with whitespace removed from both ends
प्रतिस्थापय(अधिकरणम् शब्दः वाक्यम्, कर्म शब्दः पुरातनम्, करणम् शब्दः नूतनम्) : शब्दःवाक्यम् with every पुरातनम् replaced by नूतनम्

१५

यन्त्रम्How Vāk runs

The point of the project is a language that does not depend on a host language.

A Vāk program can be executed by any of five engines, and all five are held to byte-identical output on every example:

  1. The tree-walking interpreter, in Python — the reference, the definition of what a program means.
  2. The SanskritVM, in Python — a stack machine over bytecode whose every instruction has a Sanskrit name.
  3. The SanskritVM written in Vāk — the machine's semantics, expressed in the language it runs.
  4. The SanskritVM in C — the native runtime, ~2,600 lines.
  5. वाक्.exe — the whole Vāk-written toolchain compiled natively. No Python anywhere.

The front end is entirely self-hosted. Lexer, parser, analyser, compiler and virtual machine all exist written in Vāk, and each is checked against 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.

python -m vaak --self प्रोग्राम.vak      # compiled by Vāk, run on the Python VM
python -m vaak --self-vm प्रोग्राम.vak   # lexed, parsed, compiled AND run by Vāk
python -m vaak --native प्रोग्राम.vak    # a standalone executable (needs gcc)
python -m vaak --bytecode प्रोग्राम.vak  # disassemble

Python is used to build वाक्.exe once, exactly as a C compiler is used once to build a self-hosting C compiler. After that the language stands on its own.

Where it runs. The Python implementation is portable as it stands. The native runtime is C11; the few places that need the operating system — the command line, opening a file by a Devanagari path, listing a directory — have both a Windows branch and a POSIX one, chosen by a single switch that every file shares. Building with VAK_POSIX=1 forces the POSIX branch, so the code Linux and macOS will run can be exercised anywhere.

And it runs in a browser: the same C machine, compiled to WebAssembly, is what the playground executes. Nothing is sent anywhere — the lexer, parser, analyser, compiler and VM are all in the page.

१६

दोषसूचीEvery diagnostic

The analyser reports 6 kinds of error and 6 kinds of warning. A दोषः stops the program from running; a सूचना is printed and the program runs anyway. Every one carries its Sanskrit name, so a message can be looked up here.

सङ्केतःkindwhat it means
कारकदोषःदोषःa kāraka role is repeated — Pāṇini allows one agent and one patient
ध्रुवदोषःदोषःan assignment to a ध्रुव constant
नामदोषःदोषःa name is used that was never declared
प्रकारदोषःदोषःa value does not match the declared प्रकारः
प्रवाहदोषःदोषःविरम or अनुवर्तस्व outside a loop
प्राचलदोषःदोषःa call passes the wrong number of arguments
अगम्यदोषःसूचनाa statement after प्रत्यागच्छ that can never run
अप्रयुक्तसूचनासूचनाa variable declared and never read — a leading underscore says it is deliberate
कारकसूचनासूचनाsome parameters carry kāraka roles and some do not
पुनर्घोषणासूचनाa name declared twice in the same scope
प्रतिफलसूचनासूचनाa function promises a return type but has a path that returns nothing
प्रवाहसूचनासूचनाa विकल्पः with no अन्यथा — an unmatched value does nothing

Separately, an error caught by दोषे carries its kind in प्रकारः. These are the 4 values it can hold:

प्रकारःraised by
दोषःError
अक्षरदोषःLexical Error
व्याकरणदोषःSyntax Error
कार्यकालदोषःRuntime Error
प्रयत्नः {
    मुद्रय संख्या("न अङ्कः")।
} दोषे (त्रुटिः) {
    यदि (त्रुटिः.प्रकारः == "कार्यकालदोषः") {
        मुद्रय "गणने दोषः / a runtime error"।
    }
}

Each one, happening

A list of diagnostics teaches less than watching one fire. Below is the shortest program that provokes each, and the message the analyser actually gives — both taken from the test suite and regenerated whenever this page is built, so a reworded message changes here too.

अगम्यदोषः

कार्यम् क() : पूर्णाङ्कः { प्रत्यागच्छ मुद्रय "अगम्यम्"। }
मुद्रय क()।

इदम् वाक्यम् कदापि न चलति / unreachable code after Return on line 1

अप्रयुक्तसूचना

कार्यम् क() { मान अ = मुद्रय "अ इति न प्रयुक्तम्"। }
क()।

'अ' घोषितम् किन्तु न प्रयुक्तम् / 'अ' is declared but never used

कारकदोषः

कार्यम् क(अ) { मुद्रय अ। }
क(कर्म: )।

क: कर्म इति कारकम् नास्ति / declares no कर्म parameter

कारकसूचना

कार्यम् क(कर्ता अ, ब) { मुद्रय अ। }

क: केचन प्राचलाः कारकयुक्ताः, केचन न / only 1 of 2 parameters carry a कारकम्

ध्रुवदोषः

ध्रुव क = । क = 

ध्रुवः 'क' न परिवर्तनीयः / cannot reassign the constant 'क'

नामदोषः

अज्ञातम् = 

अपरिभाषितम् नाम 'अज्ञातम्' — प्रथमम् 'मान' इति उपयुज्यताम् / undefined name 'अज्ञातम्'

पुनर्घोषणा

मान क = मान क = 

'क' अस्मिन् एव परिवेशे पुनः घोष्यते / 'क' is redeclared in the same scope

प्रकारदोषः

मुद्रय -"अ"

'-' अङ्कम् इच्छति, शब्दः दत्तः / unary '-' needs a number, got शब्दः

प्रतिफलसूचना

कार्यम् क(अ) : पूर्णाङ्कः { यदि (अ) { प्रत्यागच्छ । } }

क: पूर्णाङ्कः इति प्रतिज्ञातम्, किन्तु मार्गः अस्ति यत्र किमपि न प्रत्यागच्छति / declared पूर्णाङ्कः but some path returns nothing

प्रवाहदोषः

विरम

'विरम' पाशस्य बहिः / 'विरम' outside a loop

प्रवाहसूचना

मान क = विकल्पः (क) {
    पक्षे : मुद्रय "अ"मुद्रय अज्ञातम्।
    पक्षे : विरम।
}

विकल्पे 'अन्यथा' नास्ति — अमिलितः विषयः किमपि न करोति / no अन्यथा, so a subject that matches nothing does nothing

प्राचलदोषः

मुद्रय दीर्घता(, , )।

दीर्घता: 1 प्राचलाः अपेक्षिताः, 3 दत्ताः / expected 1 argument(s), got 3

१७

पदकोशःEvery keyword

Each keyword may be written in Devanagari, in IAST transliteration, or in plain ASCII. They are the same word — the lexer holds all three spellings, so 28 keywords are what there is to learn.

Where a cell lists two Devanagari words joined by ·, they are synonyms for the identical token, not different keywords: दोषे and गृहाण both begin a catch block. 10 constructs have such a pair.

देवनागरीIAST · ASCIImeaning
मानmāna · manadeclare a variable
ध्रुवdhruvadeclare a constant
कार्यम् · कार्यkāryam · kārya · karyam · karyadefine a function
प्रत्यागच्छ · प्रतिदाpratyāgaccha · pratidā · pratyagaccha · pratidareturn from a function
मुद्रयmudrayaprint
यदिyadiif
अन्यथाanyathā · anyathaelse
यावत् · यावतyāvat · yavatwhile
आवृत्तिः · आवृत्तिāvṛttiḥ · avrttihrepeat a fixed number of times
प्रत्येकम् · प्रत्येकमpratyekamfor each
विकल्पः · विकल्पvikalpaḥ · vikalpahchoose among alternatives
पक्षेpakṣe · pakshein this case
अन्तःantaḥ · antahof the collection
विरमviramaleave the loop
अनुवर्तanuvartanext turn of the loop
प्रयत्नः · प्रयत्नprayatnaḥ · prayatnahattempt
दोषे · गृहाणdoṣe · gṛhāṇa · doshe · grihanain the event of an error
अन्ततः · अन्तेantataḥ · antatahat the end, regardless
उत्सृज · क्षिपutsṛja · kṣipa · utsrja · kshipathrow
आनयānaya · anayaimport
इतिitiunder the name
तःtaḥ · tahtaking from
सत्यsatyatrue
असत्यasatyafalse
शून्यśūnya · shunya · sunyanothing
caand
वाvā · vaor
nanot

१८

अन्तर्निहितानिThe 48 built-ins

Available everywhere without an import. Two more — गणितम् and शब्दाः — are libraries written in Vāk and brought in with आनय.

नामromanwhat it does
लिखlikhमुद्रयति / print values
दोषलिखdoshalikhदोषप्रवाहे मुद्रयति / print to the error stream
प्रतिच्छेदःpratichchhedahकणशः संयोगः / bitwise and
संयोगःsamyogahकणशः विकल्पः / bitwise or
वियोगःviyogahकणशः भेदः / bitwise exclusive or
पूरकःpurakahकणशः निषेधः / bitwise not
वामसारःvamasarahवामतः सारणम् / shift left
दक्षिणसारःdakshinasarahदक्षिणतः सारणम् / shift right
पठpathaउपयोक्तुः पङ्क्तिम् पठति / read a line
प्रकारprakaraमूल्यस्य प्रकारः / type of a value
संख्याsankhyaअङ्कः करोति / convert to number
शब्दshabdaशब्दः करोति / convert to string
देवनागरीdevanagariदेवनागरी-अङ्काः / Devanagari digits
दीर्घताdirghataदीर्घता / length
सूचीsuchiसूचीम् रचयति / build a list
परासparasaअङ्कपरासः / numeric range
योजयyojayaसूच्याम् योजयति / append
निष्कासnishkasaअपनयति / remove an item
अस्तिastiअस्ति वा / membership test
कुञ्जिकाःkunjikaकोशस्य कुञ्जिकाः / keys of a dict
मूल्यानिmulyaniकोशस्य मूल्यानि / values of a dict
क्रमkramaक्रमबद्धा सूची / sorted copy
विपर्ययviparyayaविपरीतक्रमः / reversed
अक्षराणिaksharaniशब्दस्य अक्षराणि / syllables of a string
संकेतःsanketaअक्षरस्य संकेतः / code point of a character
वर्णःvarnaसंकेतस्य वर्णः / character for a code point
अंशःamshaशब्दस्य सूच्याः वा अंशः / a slice
विभजvibhajaशब्दम् विभजति / split a string
संयोजsamyojaसूचीम् संयोजयति / join a list
योगyogaयोगफलम् / sum
न्यूनतमnyunatamaन्यूनतमम् / minimum
अधिकतमadhikatamaअधिकतमम् / maximum
मूलmulaवर्गमूलम् / square root
पूर्णpurnaपूर्णाङ्कः / truncate to integer
यादृच्छिकyadrcchikaयादृच्छिकः अङ्कः / random number
कालkalaकालः / current time in seconds
प्राचलाःprachalahआदेशपङ्क्त्याः प्राचलाः / command-line arguments
प्रयुज्prayujकार्यम् सूच्या कोशेन वा प्रयुङ्क्ते / apply a function to computed arguments
लक्षणम्lakshanamकार्यस्य प्राचलाः कारकाणि च / a function's parameters and their roles
खण्डम्_चालयkhandam_chalayaसंकलितम् खण्डम् चालयति / run a compiled chunk
दोषdoshaदोषम् उत्पादयति / raise an error
सञ्चिकापठsanchikapathaसञ्चिकाम् पठति / read a whole file
सञ्चिकापङ्क्तयःsanchikapanktayahसञ्चिकायाः पङ्क्तयः / read a file as lines
सञ्चिकालिखsanchikalikhसञ्चिकाम् लिखति / write a file
सञ्चिकायोजयsanchikayojayaसञ्चिकायाम् योजयति / append to a file
सञ्चिकास्तिsanchikastiसञ्चिका विद्यते वा / does the path exist
सञ्चिकानाशयsanchikanashayaसञ्चिकाम् नाशयति / delete a file
निर्देशिकाnirdeshikaनिर्देशिकायाः सूची / list a directory