kabir-lang 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Afrain Kabir Rafid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,352 @@
1
+ Metadata-Version: 2.4
2
+ Name: kabir-lang
3
+ Version: 1.1.0
4
+ Summary: Kabir — a small, friendly programming language, built from scratch in Python.
5
+ Author: Afrain Kabir Rafid
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/afrainkabirrafid/kabir
8
+ Project-URL: Repository, https://github.com/afrainkabirrafid/kabir
9
+ Keywords: programming-language,interpreter,kabir
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Education
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Software Development :: Interpreters
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ # Kabir
27
+
28
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
29
+ ![Python](https://img.shields.io/badge/Python-3.8%2B-blue.svg)
30
+ ![Tests](https://img.shields.io/badge/tests-176%20passing-brightgreen.svg)
31
+
32
+ **Kabir is a small, friendly programming language, built from scratch in Python — no dependencies, no magic, just a lexer, a parser, and a tree-walking interpreter you can read in an afternoon.**
33
+
34
+ ## What is Kabir?
35
+
36
+ Kabir is a complete little language: variables, numbers, strings with interpolation, arrays, dicts, `if`/`elif`/`else`, `while` and `for` loops, first-class functions with closures and recursion, `import` for `.kbr` files, string methods, and 15 core built-in functions plus 12 standard-library functions — all in about 1,700 lines of plain, dependency-free Python. Programs live in `.kbr` files and run with `python -m kabir run`.
37
+
38
+ It is a **tree-walking interpreter**: source code is lexed into tokens, parsed into an abstract syntax tree (AST), and the interpreter walks that tree and runs it directly. There is no JIT compiler and no native compilation — Kabir trades speed for clarity, so the whole implementation is easy to read, learn from, and hack on.
39
+
40
+ ## Why Kabir exists
41
+
42
+ Kabir is named to honour the **Kabir family** of its creator, **Afrain Kabir Rafid**. It started as a from-scratch language-building project: no parser generators, no existing language renamed, no `eval`/`exec` — every token, grammar rule, and runtime behaviour was written by hand.
43
+
44
+ ## Quickstart
45
+
46
+ You need **Python 3.8 or newer** (the code avoids newer-only features; it was developed and tested on Python 3.12.3 — Python 3.8 itself was not run here). There is nothing to install: Kabir uses only the Python standard library.
47
+
48
+ ```console
49
+ $ git clone https://github.com/afrainkabirrafid/kabir.git
50
+ $ cd kabir
51
+ $ python -m kabir run examples/hello.kbr
52
+ Hello, Rafid!
53
+ Welcome to Kabir version 1.1.0.
54
+ 2 + 3 = 5
55
+ ```
56
+
57
+ That's it — clone, run. (See [Usage](#usage) for the `kabir` command itself.)
58
+
59
+ ## Hello, World
60
+
61
+ Save this as `hello.kbr`:
62
+
63
+ ```kabir
64
+ let name = "Rafid"
65
+ let language = "Kabir"
66
+
67
+ print("Hello, {name}!")
68
+ print("Welcome to {language} version 1.1.0.")
69
+ print("2 + 3 = {2 + 3}")
70
+ ```
71
+
72
+ Run it:
73
+
74
+ ```console
75
+ $ python -m kabir run hello.kbr
76
+ Hello, Rafid!
77
+ Welcome to Kabir version 1.1.0.
78
+ 2 + 3 = 5
79
+ ```
80
+
81
+ ## Usage
82
+
83
+ All commands work both as `python -m kabir ...` (zero-install, from a fresh clone) and as `kabir ...` once the launcher or the package is on your `PATH` (see below).
84
+
85
+ | Command | What it does |
86
+ | --- | --- |
87
+ | `python -m kabir run <file.kbr>` | Run a Kabir program |
88
+ | `python -m kabir <file.kbr>` | Same — a bare `.kbr` argument is run directly |
89
+ | `python -m kabir repl` | Start the interactive REPL |
90
+ | `python -m kabir` | Start the REPL (no arguments) |
91
+ | `python -m kabir --help` | Show help (`-h` and `help` work too) |
92
+ | `python -m kabir --version` | Print the version (`-v` and `version` work too) |
93
+
94
+ ```console
95
+ $ python -m kabir --version
96
+ kabir 1.1.0
97
+ ```
98
+
99
+ ### The `kabir` command
100
+
101
+ The repository ships an executable launcher at `bin/kabir`:
102
+
103
+ ```console
104
+ $ ./bin/kabir --version
105
+ kabir 1.1.0
106
+ $ ./bin/kabir run examples/fizzbuzz.kbr
107
+ ```
108
+
109
+ It lives in `bin/` for a real reason: a repo-root file named `kabir` cannot coexist with the `kabir/` package directory (same path), so the launcher is `bin/kabir`, not `./kabir`. If you install the package with pip (see `pyproject.toml`), the `kabir` command is installed on your `PATH` automatically.
110
+
111
+ ### The REPL
112
+
113
+ ```console
114
+ $ python -m kabir repl
115
+ Kabir 1.1.0 -- type 'exit' or press Ctrl-D to leave.
116
+ kabir> let x = 5
117
+ kabir> x + 1
118
+ 6
119
+ kabir> "hi {x}"
120
+ hi 5
121
+ kabir> exit
122
+ ```
123
+
124
+ Expression statements print their value; other statements print nothing. Multi-line input (for example a `func` definition) is supported — the REPL waits with a `... ` prompt until the braces balance. Type `exit` or `quit`, or press Ctrl-D, to leave.
125
+
126
+ ### Exit codes
127
+
128
+ | Code | Meaning |
129
+ | --- | --- |
130
+ | `0` | Program ran successfully (or `--help` / `--version` was shown) |
131
+ | `1` | A Kabir error (lex, parse, or runtime) or runaway recursion |
132
+ | `2` | A usage error — unknown command, missing file argument, unreadable file |
133
+
134
+ ## A tour of the language
135
+
136
+ ```kabir
137
+ # Comments start with # and run to the end of the line.
138
+
139
+ let name = "Rafid"
140
+ let scores = [64, 34, 25, 12, 22, 11, 90]
141
+
142
+ # Strings interpolate any expression inside { ... }.
143
+ print("Hello, {name}! You have {len(scores)} scores.")
144
+
145
+ # if / elif / else, while, and for over arrays, strings, dicts, or range().
146
+ for score in scores {
147
+ if score >= 50 {
148
+ print("{score} is a pass")
149
+ } elif score >= 40 {
150
+ print("{score} is close")
151
+ } else {
152
+ print("{score} needs work")
153
+ }
154
+ }
155
+
156
+ let total = 0
157
+ for n in scores { total += n }
158
+ print("Average: {total / len(scores)}")
159
+
160
+ # Functions are first-class values; closures capture their surroundings.
161
+ func make_adder(n) {
162
+ return func(x) { return x + n }
163
+ }
164
+ let add5 = make_adder(5)
165
+ print(add5(10)) # 15
166
+
167
+ # Dicts have string keys.
168
+ let user = {"name": "Rafid", "language": "Kabir"}
169
+ print(user["name"])
170
+ print(keys(user))
171
+ ```
172
+
173
+ Expected output:
174
+
175
+ ```text
176
+ Hello, Rafid! You have 7 scores.
177
+ 64 is a pass
178
+ 34 needs work
179
+ 25 needs work
180
+ 12 needs work
181
+ 22 needs work
182
+ 11 needs work
183
+ 90 is a pass
184
+ Average: 36.857142857142854
185
+ 15
186
+ Rafid
187
+ [name, language]
188
+ ```
189
+
190
+ A few things worth noticing:
191
+
192
+ - **One number type.** There is no separate `int`/`float`: `5.0` prints as `5`, `7 / 2` is `3.5`, and `6 / 3` is `2` (an exact integer division stays an integer).
193
+ - **Truthiness is broad.** `0`, `""`, `[]`, `{}`, `false`, and `null` are falsy; everything else — including non-empty strings, arrays, and functions — is truthy.
194
+ - **`and` / `or` return values, not booleans.** `false or "fallback"` is `"fallback"`; `true and "yes"` is `"yes"`. They short-circuit: the right side is only evaluated when needed.
195
+ - **Negative indexes work.** `[10, 20, 30][-1]` is `30`, and `"abc"[-1]` is `"c"`.
196
+ - **Errors are friendly.** A mistake gives you `file:line:col`, the offending source line, and a caret — never a Python traceback. See [Errors](#errors) below.
197
+
198
+ The full reference — every construct, every built-in, each with a runnable example and its exact output — is in [docs/LANGUAGE.md](docs/LANGUAGE.md).
199
+
200
+ ## Examples
201
+
202
+ All ten example files live in [`examples/`](examples/) and are covered by the test suite: nine runnable programs, plus `greet-lib.kbr`, a small library file that `import-demo.kbr` loads.
203
+
204
+ | File | What it shows |
205
+ | --- | --- |
206
+ | [`examples/hello.kbr`](examples/hello.kbr) | First program: variables and string interpolation |
207
+ | [`examples/fizzbuzz.kbr`](examples/fizzbuzz.kbr) | `for` over `range()`, `if`/`elif`/`else`, `%` |
208
+ | [`examples/fibonacci.kbr`](examples/fibonacci.kbr) | Recursion — `fib(n)`, first 15 numbers |
209
+ | [`examples/primes.kbr`](examples/primes.kbr) | `while` loop, helper function, primes up to 100 |
210
+ | [`examples/sorting.kbr`](examples/sorting.kbr) | Bubble sort, index assignment, arrays as values |
211
+ | [`examples/guessing-game.kbr`](examples/guessing-game.kbr) | Interactive loop with `input()` (the secret is a random number from `randint(1, 100)`) |
212
+ | [`examples/import-demo.kbr`](examples/import-demo.kbr) | `import "greet-lib.kbr"` — load and reuse code from another file |
213
+ | [`examples/todo.kbr`](examples/todo.kbr) | In-memory todo CLI: `add`, `list`, `done`, `quit` |
214
+ | [`examples/calculator.kbr`](examples/calculator.kbr) | Interactive calculator; accepts `2+3` and `2 + 3`, handles division by zero |
215
+
216
+ Try the interactive ones by piping input, for example:
217
+
218
+ ```console
219
+ $ printf '2 + 3\n10 / 4\nquit\n' | python -m kabir run examples/calculator.kbr
220
+ Calculator -- type like '2 + 3', or 'quit' to stop.
221
+ calc> 2 + 3 = 5
222
+ calc> 10 / 4 = 2.5
223
+ calc> Goodbye!
224
+ ```
225
+
226
+ ## Built-ins
227
+
228
+ Kabir ships 15 core built-in functions, available in every program with no import:
229
+
230
+ | Function | Purpose |
231
+ | --- | --- |
232
+ | `print(...)` | Print values, space-separated, with a newline |
233
+ | `input(prompt?)` | Read a line from the user (prompt is optional) |
234
+ | `len(x)` | Length of a string, array, or dict |
235
+ | `str(x)` | Convert a value to a string |
236
+ | `num(x)` | Parse a string (or pass a number through) as a number |
237
+ | `type(x)` | Type name: `"number"`, `"string"`, `"boolean"`, `"null"`, `"array"`, `"dict"`, `"function"` |
238
+ | `range(n)` / `range(a, b)` | List of whole numbers: `0..n-1`, or `a..b-1` |
239
+ | `push(array, value)` | Append to an array (returns the array) |
240
+ | `pop(array)` | Remove and return the last item |
241
+ | `sqrt(x)` | Square root |
242
+ | `pow(x, y)` | `x` to the power of `y` |
243
+ | `abs(x)` | Absolute value |
244
+ | `keys(dict)` | List of a dict's keys, in insertion order |
245
+ | `int(x)` | Convert to an integer (truncates) |
246
+ | `float(x)` | Convert to a float |
247
+
248
+ Each one is documented with a runnable example in [docs/LANGUAGE.md](docs/LANGUAGE.md#built-in-functions).
249
+
250
+ ### Standard library
251
+
252
+ On top of the core built-ins, Kabir ships 12 standard-library functions — also plain globals, no import needed — plus the constants `pi` and `e`, and 9 string methods (see [docs/LANGUAGE.md](docs/LANGUAGE.md#standard-library)):
253
+
254
+ | Function | Purpose |
255
+ | --- | --- |
256
+ | `randint(a, b)` | Random whole number from `a` to `b`, both ends included |
257
+ | `randrange(n)` / `randrange(a, b)` | One random number from a `range()` |
258
+ | `choice(array)` | One random item from an array |
259
+ | `shuffle(array)` | Shuffle an array in place (returns the array) |
260
+ | `seed(n)` | Seed the random sequence, so it is repeatable |
261
+ | `floor(x)` / `ceil(x)` | Round a number down / up to a whole number |
262
+ | `sin(x)` / `cos(x)` / `tan(x)` | Trigonometry, in radians |
263
+ | `log(x)` | Natural logarithm |
264
+ | `exp(x)` | `e` to the power of `x` |
265
+ | `pi`, `e` | The constants `3.141592653589793` and `2.718281828459045` |
266
+
267
+ String methods live on the string itself: `"abc".upper()`, `" a ".strip()`, `"a,b".split(",")`, `", ".join(["a", "b"])`, `"hello".contains("ell")`, `"hello".starts_with("he")`, `"hello".ends_with("lo")`, `"aaa".replace("a", "b")`, and `"ABC".lower()`.
268
+
269
+ Code can also be shared across files with `import "other.kbr"` — see [docs/LANGUAGE.md](docs/LANGUAGE.md#import).
270
+
271
+ ## Errors
272
+
273
+ Kabir errors always look like this — location, source line, caret, plain message:
274
+
275
+ ```text
276
+ /tmp/demo.kbr:1:9: Undefined variable 'y' -- it was never defined with 'let'. Check the spelling
277
+ let x = y
278
+ ^
279
+ ```
280
+
281
+ ```text
282
+ /tmp/demo.kbr:1:9: Division by zero -- the number after '/' cannot be 0
283
+ print(1 / 0)
284
+ ^
285
+ ```
286
+
287
+ Strings even get a hint when you use the wrong quotes:
288
+
289
+ ```text
290
+ /tmp/demo.kbr:1:7: Strings use double quotes, not single quotes -- write "hello" instead of 'hello'
291
+ print('hi')
292
+ ^
293
+ ```
294
+
295
+ ## Architecture
296
+
297
+ In plain words, running a program goes through four stages:
298
+
299
+ 1. **Lexer** (`kabir/lexer.py`) — turns source text into tokens (`NUMBER`, `STRING`, `IDENT`, `KEYWORD`, `OP`, `PUNCT`, `NEWLINE`, `EOF`). Strings are tokenised into literal/interpolation parts here.
300
+ 2. **Parser** (`kabir/parser.py`) — a hand-written recursive-descent parser that turns tokens into an AST, following a precedence ladder from `or` down to calls and indexing. `**` is right-associative; a `{` followed by a string key and `:` is read as a dict literal, not a block.
301
+ 3. **AST** (`kabir/ast_nodes.py`) — one small class per node type (`LetStmt`, `IfStmt`, `BinaryOp`, …), each carrying its source line and column so errors can point at the exact spot.
302
+ 4. **Interpreter** (`kabir/interpreter.py`) — walks the AST directly. Lexical scoping is a chain of `Environment` objects; closures capture their defining environment; `return`/`break`/`continue` are implemented as internal signals. Built-ins live in `kabir/builtins.py` and are registered into the global environment at startup.
303
+
304
+ The full internals guide — token types, the precedence ladder, environment/scoping rules, evaluator dispatch, error rendering, and the CLI/REPL flow — is in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
305
+
306
+ ## Tests
307
+
308
+ Zero dependencies here too — the suite is plain `unittest`, run by a small runner:
309
+
310
+ ```console
311
+ $ python tests/run_tests.py
312
+ Ran 176 tests in 0.818s
313
+
314
+ OK
315
+ ```
316
+
317
+ The 176 tests cover the lexer, parser, interpreter, built-ins, standard library, string methods, imports, errors, and every example in `examples/`. Run them before sending a pull request (see [CONTRIBUTING.md](CONTRIBUTING.md)).
318
+
319
+ ## Honest limits
320
+
321
+ Kabir 1.1.0 is a small language, and it is honest about what it is not:
322
+
323
+ - **No JIT, no native compilation.** It is a tree-walking interpreter; it is fast enough for scripts and learning, not for number-crunching.
324
+ - **`import` loads other `.kbr` files** — a program can be split across files, and each file runs once (see the [roadmap](#roadmap) for what is still missing).
325
+ - **No package manager.** There is no package registry; `import` loads a file by path, and the standard library is the 15 core built-ins plus the `random`/`math` functions and string methods listed above.
326
+ - **No file I/O, no dates, no networking.** The only way in or out is `print` and `input` (plus `import`, which reads `.kbr` files).
327
+ - **Recursion depth is capped** at 500 nested calls, and there is no tail-call optimisation.
328
+ - **One number type** means no separate integer/float semantics; a power that overflows float range fails with a friendly error, while integer powers use arbitrary-precision integers.
329
+
330
+ ## Roadmap
331
+
332
+ Roughly in order, and open to discussion:
333
+
334
+ 1. **`import` for `.kbr` files** — load and reuse code across files. — done in 1.1.0
335
+ 2. **Standard-library modules** — starting with `random` (so the guessing game gets a real secret), then string helpers and math. — done in 1.1.0 (random, math, and string methods as built-ins)
336
+ 3. **A formatter** (`kabir fmt`) for canonical `.kbr` style.
337
+ 4. **Pip packaging** — `pyproject.toml` is already in the repo; publishing to PyPI so `pip install kabir` gives you the `kabir` command.
338
+ 5. **More of the language** — `else` on loops is not planned; instead: string methods — done in 1.1.0, a `switch`-style construct only if it earns its place, and better REPL history.
339
+
340
+ Ideas and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
341
+
342
+ ## Contributing
343
+
344
+ Fork the repo, make a small focused change, add tests for any new behaviour, and run the suite (`python tests/run_tests.py`). The full guide — style rules (stdlib only, no `eval`/`exec`), how to add a built-in, and what a good PR looks like — is in [CONTRIBUTING.md](CONTRIBUTING.md).
345
+
346
+ ## License
347
+
348
+ Kabir is released under the MIT License. See [LICENSE](LICENSE).
349
+
350
+ ## Author
351
+
352
+ Created by **Afrain Kabir Rafid** — named to honour his Kabir family.