Documentation
eTamil user manual
The complete guide to writing, running and deploying eTamil programs — from your first line to a database-backed HTTP service.
This manual covers everything needed to write and run eTamil programs. It assumes you can use a terminal, and nothing else.
If you only want a taste, the language tour is shorter. If you want the full keyword list, that is the keyword reference.
Conventions. Code is shown in Tamil script. Every keyword also has a romanized and often an English spelling, all interchangeable — see Three spellings. Where a feature needs a build flag or an environment variable, it says so.
Contents
Getting started — Install · Run a program · Three spellings
The language — Variables and types · Numbers and money · Strings · Input and output · Operators · Conditionals and loops · Functions · Arrays and records · Iteration · Results · Modules · Type checking · Naming rules · Errors · Files
Libraries — Standard library · Accounting and GST
Building services — Databases · HTTP server · JSON · Authentication · Signing · Bytes and encoding · Outbound HTTP · Scheduled work
Practicalities — Editor · Checking and testing · Environment variables · Next steps
1. Install {: #1-install}
The quickest route needs no Rust and no C toolchain.
Windows
Expand-Archive etamil-windows-x64.zip -DestinationPath .
.\etamil-windows-x64\install.ps1
Linux
tar -xzf etamil-linux-x64.tar.gz
./etamil-linux-x64/install.sh
macOS — pick arm64 for Apple Silicon or x64 for Intel; uname -m tells you which.
tar -xzf etamil-macos-arm64.tar.gz
./etamil-macos-arm64/install.sh
xattr -dr com.apple.quarantine ~/.local/lib/etamil
That last line is required, not optional: the packages are not notarized, so macOS quarantines anything downloaded through a browser and Gatekeeper refuses to run it. Clearing the flag once is enough.
Open a new terminal afterwards — the installer edits PATH, and a shell
already running will not see it — then check:
etamil --version
Removal is just as simple: the Windows installer puts everything under
%LOCALAPPDATA%\Programs\eTamil, the Linux one under ~/.local. Neither needs
administrator rights.
To build from source instead — needed for the PostgreSQL and MySQL drivers, the LLVM backend, or work on the compiler itself — see the install page.
2. Run a program {: #2-run-a-program}
echo 'அச்சு "வணக்கம் உலகம்!";' > hello.etamil
etamil --vm hello.etamil
On Windows write the file as UTF-8:
'அச்சு "வணக்கம் உலகம்!";' | Out-File hello.etamil -Encoding UTF8
etamil --vm hello.etamil
| Flag | Effect |
|---|---|
--vm |
Run on the bytecode VM. This is the default and what you want |
--check |
Lex, parse and type check, then stop. Reports every error and never runs the program |
--server |
Start the HTTP server with a worker pool |
--async |
Concurrent server: async accept, handlers on the blocking pool. Ctrl-C stops it |
--llvm |
LLVM backend. Needs --features llvm; Linux and macOS only |
--port <PORT> |
Server port, default 8080 |
--host <HOST> |
Server host, default 127.0.0.1 |
File extensions are conventional, not enforced: .etamil and .qmz are both
used in the repository.
Three spellings
Every keyword accepts Tamil script, a romanized spelling typeable on a plain keyboard, and where one exists an English alias. All produce the same token.
எண் வருவாய் = 100000; // Tamil script
eN varuvAy = 100000; // romanized (ezuqqu)
int income = 100000; // English alias
The romanization is its own scheme, deliberately not ISO 15919: one ASCII
character per Tamil letter, no diacritics, no digraphs. It keeps the three Tamil
nasals distinct where English collapses them into one n — ண is N, ந is
n, ன is Z. The
full letter table is on the
language page.
Pick one spelling per program. Mixing them is legal but produces variables that look identical and are not — see Naming rules.
3. Variables and types {: #3-variables-and-types}
எண் age = 25; // number
எண் price = 99.99; // fixed-point decimal
எண் rate = 15%; // percentage literal — exactly 0.15
சொல் name = "Ravi"; // string
Declaring a type is optional; assigning without one works and infers nothing. When you do declare, the compiler holds you to it, including on later assignments.
The types are எண் (number), சொல் (string), அணி (array), வரிசை (record),
and the boolean values மெய் and பொய்.
4. Numbers and money {: #4-numbers-and-money}
Every number is a fixed-point decimal. There is no f64 anywhere in the
arithmetic path, which is the single most important fact about this language for
financial work.
அச்சு 0.1 + 0.2; // 0.3 — not 0.30000000000000004
அச்சு 99.99 * 3; // 299.97 — not 299.96999999999997
அச்சு 18%; // 0.18 exactly
Equality is exact too. Two amounts a fraction of a paisa apart are never treated as equal — which matters the moment you reconcile anything.
Division keeps full precision rather than rounding at each step, because Indian tax computation rounds once at the end. Round explicitly when you need to:
அச்சு வட்டமிடு(மொத்தம் / 3, 2); // round to 2 places
அச்சு தரை(12.7); // 12
அச்சு மேல்(12.1); // 13
எண் is currently the only numeric type — there is no separate integer, and no
money type carrying a currency, so the checker cannot yet reject adding rupees to
a count.
5. Strings {: #5-strings}
& concatenates. Strings are measured in written letters, not code points:
அச்சு நீளம்("வணக்கம்"); // 5, not 7
A Tamil letter is often a consonant plus a vowel sign or pulli. Counting code points would give the wrong answer and every string helper would inherit it.
Escapes are \n \t \r \" \\. An unknown escape keeps both characters
rather than guessing.
6. Input and output {: #6-input-and-output}
எண் வருவாய்;
அச்சு "Enter income: ";
உள்ளிடு வருவாய்;
அச்சு "Income: " & வருவாய்;
உள்ளிடு always returns text. It converts when compared with or used as a
number, so reading a figure and comparing it against a slab works without an
explicit cast.
7. Operators {: #7-operators}
| Kind | Operators |
|---|---|
| Arithmetic | + - * /, and unary - |
| Comparison | == != < <= > >= |
| Logical | மற்றும் / maRRum / _and, அல்லது / allaqu / _or, இல்லை / illY / _not |
| String | & |
Precedence, loosest first: or → and → not → comparison → + - → * /.
(வருவாய் > 800000 மற்றும் வயது < 60) எனில் {
அச்சு "Taxable";
}
Both sides of a logical operator are always evaluated. There is no short-circuiting, so do not rely on the left side guarding the right.
8. Conditionals and loops {: #8-conditionals-and-loops}
(வருவாய் > 800000) எனில் {
அச்சு "High";
}
இன்றேல் {
அச்சு "Low";
}
எண் i = 0;
(i < 3) சுற்று {
அச்சு i;
i = i + 1;
}
The condition comes first and is parenthesised; எனில் follows it.
9. Functions {: #9-functions}
செயல் declares, திரும்பு returns. Parameters, local scope and recursion all
work. Functions have no declared signatures yet, so parameter types are not
checked.
செயல் வரிசை_மதிப்பு(உருப்படி) {
திரும்பு உருப்படி.அளவு * உருப்படி.விலை;
}
அச்சு வரிசை_மதிப்பு({அளவு: 3, விலை: 54999});
There are no first-class functions — you cannot pass a function as a value yet,
which is why the standard library has no map or filter.
10. Arrays and records {: #10-arrays-and-records}
Arrays use […] and are zero-indexed. Records use {…} with key: value.
விலைகள் = [100, 250, 375];
அச்சு விலைகள்[0]; // 100
உருப்படி = {விவரம்: "மடிக்கணினி", விலை: 54999};
அச்சு உருப்படி.விலை; // field access
அச்சு உருப்படி["விலை"]; // same thing, key computed at runtime
That second form matters: because a key can be computed, the JSON parser is written in eTamil rather than in the host.
11. Iteration {: #11-iteration}
ஒவ்வொரு … இல் walks arrays, records and strings.
ஒவ்வொரு உருப்படி இல் உருப்படிகள் {
அச்சு உருப்படி.விவரம்;
}
12. Results — handling failure {: #12-results-handling-failure}
Failure is a value, not an exception. A fallible call returns சரி (ok) or
தவறு (error), following Rust’s semantics.
| Builtin | Purpose |
|---|---|
சரியா(r) |
true if the result succeeded |
தவறா(r) |
true if it failed |
மதிப்பு(r) |
unwrap the value |
இயல்பு(r, d) |
unwrap, or d if it failed |
? |
propagate a failure to the caller |
ப = ஜேசான்_படி(request_body);
(தவறா(ப)) எனில் {
பதில் 400, "malformed JSON";
}
தரவு = மதிப்பு(ப);
Because parsing returns a result, malformed input is something you handle rather than something that crashes the handler.
13. Modules {: #13-modules}
இறக்கு imports another file. Paths resolve beside the importing file first,
then along ETAMIL_PATH, then next to the compiler binary.
இறக்கு "nUlakam/paNam.qmz";
இறக்கு "../../nUlakam/kaNiqam.qmz";
To use the standard library from anywhere:
export ETAMIL_PATH=/path/to/etamil_compiler
14. Type checking {: #14-type-checking}
--check parses and type checks without running:
etamil --check my_program.qmz
A declared type is enforced with a position:
✗ வரி 2, நெடுவரிசை 6: 'கொடியா' ஈர்ம (Irma, a boolean) என அறிவிக்கப்பட்டது,
ஆனால் ஒரு அணி (an array) வழங்கப்பட்டது
(line 2, column 6: 'கொடியா' is declared a boolean, but was given an array)
The checker is deliberately narrow. A number satisfies சொல், because every
value renders as text and உள்ளிடு hands back text routinely compared with
numbers. A call, an index and a field access make no claim at all, because
functions have no signatures yet — silence there is the absence of a claim, not
approval.
15. Naming rules
A name is stored exactly as you typed it, including when the word is also a
keyword. வங்கி = 5 creates a variable called வங்கி; {வரி: 100} produces the
field வரி.
The consequence: {வரி: 1} and {vari: 1} are different fields, and
வருவாய் and varuvAy are different variables. Pick one spelling per
program. A field name is data — what you typed — not a language construct.
Two positions keep the canonical English name, because what they name belongs to
the host rather than to you: the database type in தளம்_இணை, and the HTTP method
in வழி.
Hard-reserved words cannot be used as names at all — the type keywords and
SQL clause keywords: எண், சொல், அணி, வரிசை, விதி, இடம், உள், வெளி,
குழு, சேர்.
Financial keywords are not reserved: தொகை is a perfectly good variable name.
16. Reading error messages {: #16-reading-error-messages}
Every parse error carries a line and column, in Tamil and English:
✗ வரி 3, நெடுவரிசை 1: ';' எதிர்பார்க்கப்பட்டது, 'அச்சு' கிடைத்தது
(line 3, column 1: expected ';', found 'அச்சு')
Columns count written letters, not bytes, so the position is the one you would point at on screen.
Anything unimplemented fails with an explicit message rather than quietly doing nothing — a silent no-op in a tax calculator is worse than an error.
17. Files and CSV {: #17-files-and-csv}
கோப்பு_திற "output.txt", "write"; // opening for write truncates
கோப்பு_எழுது "output.txt", "வணக்கம்"; // subsequent writes append
கோப்பு_மூடு "output.txt";
கோப்பு_படி "output.txt", data;
அச்சு data;
Counting CSV rows, excluding the header:
தரவுரை_படி "students.csv", total;
அச்சு total;
18. The standard library {: #18-the-standard-library}
nUlakam/ is written in eTamil, not Rust. If the standard library needed a
systems language, the DSL would not be sufficient for what it exists to do.
| Module | Contents |
|---|---|
col.qmz |
strings — துண்டு தேடு பிரி ஒன்றிணை ஒழுங்கு தொடங்குகிறதா முடிகிறதா இடமிருந்து_நிரப்பு |
kaNiqam.qmz |
math — முழுமதிப்பு சிறியது பெரியது கூட்டு சராசரி சதவீதம் |
aNi.qmz |
arrays — உள்ளதா இடம்_காண் தலைகீழ் வெட்டு புலம்_எடு காலியா |
paNam.qmz |
money — ரூபாய் காசு_வடிவம் காசாக லட்சம் கோடி |
jEcAZ.qmz |
JSON — ஜேசான்_ஆக்கு ஜேசான்_படி |
kuRiyAkkam.qmz |
encoding — அறுபத்துநான்கு_ஆக்கு அறுபத்துநான்கு_படி பதினாறு_ஆக்கு பதினாறு_படி |
இறக்கு "nUlakam/paNam.qmz";
அச்சு ரூபாய்(12345678.5); // ₹1,23,45,678.50 — lakh and crore
What the host provides, because a language cannot express it: நீளம் இணை
வகை · சரி தவறு சரியா தவறா மதிப்பு இயல்பு · வட்டமிடு தரை மேல் ·
சொல்லாக்கு எண்ணாக்கு · மேல்_எழுத்து கீழ்_எழுத்து · இன்று
நாள்_வேறுபாடு நாள்_கூட்டு · கடவுச்சொல்_மறை கடவுச்சொல்_சரியா சீட்டு_ஆக்கு
சீட்டு_சரிபார் · கையொப்பம் கையொப்பம்_சரியா · வலை_பெறு வலை_பதி
வலை_அனுப்பு · பைட்டுகள் பைட்டுச்_சரம்.
Everything else is built from those.
19. Accounting and GST {: #19-accounting-and-gst}
The financial vocabulary is in the language — வரவு, பற்று, வரி,
இருப்புநிலை, பேரேடு, மூலதனம் are keywords. The framework above them lives
in nUlakam/kaNakkiyal/, also written in eTamil.
இறக்கு "nUlakam/kaNakkiyal/kaNakkukaL.qmz";
இறக்கு "nUlakam/kaNakkiyal/pErEtu.qmz";
பேரேடு = மதிப்பு(பதிவிடு(பேரேடு, பரிவர்த்தனை_ஆக்கு(
"JV001", "2026-04-01", "தொடக்க மூலதனம்", [
பற்று_வரிசை("1000", 500000),
வரவு_வரிசை("3000", 500000)
])));
பதிவிடு refuses an unbalanced transaction and returns தவறு, so nothing
unbalanced ever reaches the ledger. The ledger is a value: posting returns a new
one rather than editing the old, so an entry cannot be changed after the fact.
GST splits into CGST and SGST within a state, or IGST across states. Full worked examples are on the finance page.
20. Databases {: #20-databases}
Queries are always parameterised. There is deliberately no way to splice a value into SQL text, so the injection class of bug is not available to write.
தளம்_இணை எசுகியூஎல்லைட், "kaNakku.db";
தளம்_வினா "SELECT peyar, qokY FROM pativukaL WHERE vakY = ?", ["வரவு"], வரவுகள்;
Rows come back as an array of records, so a result set iterates like any table.
| Engine | Status | Notes |
|---|---|---|
| SQLite | Working | Built in. Decimals stored as text, so no precision is lost |
| PostgreSQL | Working | --features postgres. Money in native NUMERIC; placeholders are $1, $2 |
| MySQL / MariaDB | Live verified | --features mysql. Placeholders are ? |
| MongoDB, Redis | Not implemented | Neither fits a SQL-shaped trait; both need a design first |
Connections are reused from a process-wide idle cache rather than reopened per
request; leases are exclusive, so a transaction keeps its connection to itself.
ETAMIL_DB_IDLE caps how many stay warm.
PostgreSQL folds unquoted identifiers to lower case — write "qokY" to get that
column name back as you spelled it.
21. The HTTP server {: #21-the-http-server}
etamil --server --port 8080 my_service.qmz
etamil --async --port 8080 my_service.qmz
வழி declares a route, பதில் answers:
வழி பெறு, "/kaNakku/:id" {
பதில் 200, "id=" & param_id & " vakY=" & query_params["vakY"];
}
வழி பதி, "/pativu" {
பதில் 201, "got body: " & request_body;
}
வழி பெறு, "/aRikkY.csv" {
பதில் 200, வரிசைகள், {"Content-Type": "text/csv"};
}
| Variable | Contents |
|---|---|
request_method |
the HTTP method |
request_path |
the path as requested |
request_body |
the raw body |
query_params |
query string, as a record |
headers |
request headers, as a record |
path_params |
matched :name segments, as a record |
param_<name> |
each path parameter, bound directly |
Response headers are an ordinary record; omit them and the server answers
application/json.
--async changes only how connections are accepted — a connection costs a task
rather than a thread. The VM stays synchronous, and handlers run on tokio’s
blocking pool, which is what lets the blocking database drivers keep working.
22. JSON {: #22-json}
இறக்கு "nUlakam/jEcAZ.qmz";
ப = மதிப்பு(ஜேசான்_படி(request_body));
அச்சு ப["qokY"] + 1; // a number, not text
பதில் 200, ஜேசான்_ஆக்கு({நிலுவை: 1500});
Fields serialize in sorted order, which makes a response body stable enough to
assert on in tests. \uXXXX escapes are not decoded. The ஜேசான்_உரை statement
parses but the VM refuses it — build the body with ஜேசான்_ஆக்கு instead.
23. Authentication {: #23-authentication}
bcrypt and JWT live in the host, because hashing and HMAC need bytes and randomness the language cannot reach.
மறை = கடவுச்சொல்_மறை(கடவுச்சொல்);
(கடவுச்சொல்_சரியா(கடவுச்சொல், மறை)) எனில் {
சீட்டு = சீட்டு_ஆக்கு({பயனர்: "ravi", பங்கு: "admin"});
பதில் 200, சீட்டு;
}
Set ETAMIL_JWT_SECRET before starting the server. Everything above these four
builtins — who a user is, which route needs which role — stays in eTamil.
24. Signing and webhooks {: #24-signing-and-webhooks}
கையொப்பம் produces an HMAC-SHA256 signature; கையொப்பம்_சரியா verifies one.
The comparison is constant-time, so it does not leak the expected signature a
byte at a time to anything measuring how long the check takes.
வழி பதி, "/webhook" {
(கையொப்பம்_சரியா(request_body, headers["X-Signature"], ரகசியம்)) எனில் {
பதில் 200, "ok";
}
பதில் 401, "bad signature";
}
25. Bytes, base64 and hex {: #25-bytes-base64-and-hex}
பைட்டுகள் turns text into bytes; பைட்டுச்_சரம் turns them back. A byte array
is an ordinary array of numbers — deliberately not a new value type, so every
array helper already works on it.
இறக்கு "nUlakam/kuRiyAkkam.qmz";
க = அறுபத்துநான்கு_ஆக்கு(பைட்டுகள்("வணக்கம்"));
அச்சு பைட்டுச்_சரம்(அறுபத்துநான்கு_படி(க));
26. Calling other services {: #26-calling-other-services}
வலை_பெறு, வலை_பதி and வலை_அனுப்பு make outbound requests, behind
--features http-client, which is on by default. A non-2xx response is a
result, not a failure — a 404 from a gateway is something you branch on.
ப = வலை_பெறு("https://api.example.in/rates");
(சரியா(ப)) எனில் {
அச்சு மதிப்பு(ப).உடல்;
}
27. Scheduled work {: #27-scheduled-work}
இடைவெளி runs a block on a timer under either server:
இடைவெளி 3600 {
அச்சு "hourly reconciliation";
}
The number is the gap between runs, not a fixed period. If a run overruns the interval, the next starts late rather than starting on top of the one still going — for a job posting ledger entries, that is the difference between late and wrong.
28. Editor support {: #28-editor-support}
The VS Code extension is in eTamil_Code/ in the compiler repository:
highlighting for all 201 keywords in every spelling, completions for the 23
builtins and 122 nUlakam functions, and --check errors shown as you type.
Its grammar and completion data are generated from lexer.rs, and CI fails if
they drift, so the editor cannot fall behind the compiler.
29. Checking and testing {: #29-checking-and-testing}
etamil --check my_program.qmz # parse and type check, do not run
In the compiler repository:
cargo test # 196 language tests + 59 unit + 8 --check
./scripts/run_examples.sh # every example, with expected outcomes
The language tests assert on program results, not exit codes — every bug they cover exited 0 while producing the wrong answer.
30. Environment variables {: #30-environment-variables}
| Variable | Effect |
|---|---|
ETAMIL_PATH |
Where இறக்கு looks after the importing file’s own directory |
ETAMIL_JWT_SECRET |
Signing secret for சீட்டு_ஆக்கு / சீட்டு_சரிபார் |
ETAMIL_DB_IDLE |
How many database connections stay warm in the idle cache |
ETAMIL_TEST_MYSQL |
Set to 1 to include the live MySQL example in the runner |
31. Next steps {: #31-next-steps}
-
Keyword reference
Every token in all three spellings, generated from the lexer.
-
Accounting framework
Double entry, GST, the three statements, and the eCommerce example.
-
Status
What works, what is partial, what is planned.
-
Compiler source
The Rust host, the standard library, and every example.