npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aayazk/bhojpuri-lang

v1.1.1

Published

A toy programming language with Bhojpuri keywords

Readme

Bhojpuri Lang

A toy programming language with Bhojpuri keywords, inspired by Bhailang. Written in plain JavaScript with zero dependencies. It runs in Node.js and in the browser.

Try it in the playground → New to programming? Press 📘 Seekha there for 12 short lessons in Bhojpuri (or English), each with an example and a small exercise that checks your answer. Write code (with colours as you type), run it, and press Baanta 🔗 to copy a link that opens your code for anyone you send it to. + Naya file adds more files for le aaw. The Console below the editor runs one line at a time, like the interactive prompt.

ka ho bhaiya
  maan la naam = "duniya";
  bol ho "Pranam", naam;
chalat bani bhaiya

Bhojpuri me: Bhojpuri Lang ek khilauna programming language ha, jekar keyword Bhojpuri me ba. Browser me playground khol ke turant code likha aur chalawa. 📘 Seekha dabawe pe 12 go chhot-chhot paath milela, har paath ke saath ek abhyas bhi. Apna computer pe chalawe khatir npx @aayazk/bhojpuri-lang likha. Neeche ke puri jaankari angrezi me ba.

Install

npm install -g @aayazk/bhojpuri-lang   # gives you the `bhojpuri` command
bhojpuri program.bhoj                  # run a program
bhojpuri                               # open the interactive prompt

Or run a file without installing anything:

npx @aayazk/bhojpuri-lang program.bhoj

| Command | What it does | | --- | --- | | bhojpuri program.bhoj | runs the program. poochh reads answers from what you type (or from piped input) | | bhojpuri | opens the interactive prompt; piped lines run without prompts | | bhojpuri --help | lists every keyword and built-in | | bhojpuri --version | prints the version |

The command exits with code 0 when the program finishes, and 1 for a syntax or runtime error (printed to stderr) or a file that can't be read.

Since 1.0, STABILITY.md says what stays the same between versions: a program that works with 1.x keeps working with every later 1.x.

Quick start (from a clone of this repo)

npm test                              # run the test suite
node bin/bhojpuri.js examples/fizzbuzz.bhoj
node bin/bhojpuri.js                  # interactive prompt
npm run playground                    # browser playground at http://localhost:3000

To get a global bhojpuri command while developing:

npm link
bhojpuri examples/hello.bhoj

Interactive prompt

Run bhojpuri without a file to try things out one line at a time:

$ bhojpuri
Bhojpuri Lang 1.1.0 — "chalat bani bhaiya" likh ke ya Ctrl+D se bahar nikal.
bhojpuri> 2 + 3 * 4
14
bhojpuri> maan la naam = "Ramu"
bhojpuri> bol ho "Pranam,", naam
Pranam, Ramu
bhojpuri> kaam dugna(x) {
...   lauta da x * 2;
... }
bhojpuri> dugna(21)
42
bhojpuri> chalat bani bhaiya
  • No markers needed: there's no ka ho bhaiya, and the ; at the end of the line is optional (as it is before a } everywhere). Two statements on one line still need a ; between them.
  • Values are shown: typing a value shows it, with strings in quotes. Statements, assignments and khaali show nothing.
  • Everything is remembered: variables and functions carry over between lines. You can run maan la x = ... again to start a variable over.
  • Multi-line code: while a {, ( or [ is still open, the prompt changes to ... and keeps reading.
  • Errors don't end the session. The arrow keys bring back earlier lines, Ctrl+C throws away the current line, and chalat bani bhaiya or Ctrl+D leaves.
  • A line starting with { is a block. To see a kosh, wrap it in brackets (({ "a": 1 })) or put it in a variable first.
  • Piped input: lines piped into bhojpuri without a file run the same way, without the prompts: printf '2 + 3\nsanyog(1, 6)\n' | bhojpuri.

The language

Every program starts with ka ho bhaiya and ends with chalat bani bhaiya. Statements end with ;, and blocks use { }. The ; can be left out just before a }, so short blocks like jadi (x > 0) { bol ho "haan" } read cleanly.

| Bhojpuri | Meaning | | -------------------- | ------------------ | | ka ho bhaiya | start of program | | chalat bani bhaiya | end of program | | maan la | declare a variable (let) | | bol ho | print | | jadi | if | | na ta jadi | else if | | na ta | else | | jab le | while | | har … se … tak | counting for loop | | har … me | for each item of a list or string, or key of a kosh | | bas kara | break | | aage badha | continue | | kaam | define a function | | lauta da | return | | koshish kara … galti pe | try … catch | | phenk da | throw | | le aaw | use another file (import) | | sach / jhooth | true / false | | khaali | null |

Variables

maan la a = 10;
maan la b = "shabd", c;     // several at once; c starts as khaali
a += 5;                     // also -= *= /= %=

Variables are block-scoped. Names can be written in Devanagari too (maan la नाम = "राम";).

Output

bol ho "jawab:", a * 2;     // several values are joined with a space

Strings

Strings use double or single quotes: "Pranam" or 'Pranam'. They end on the same line, and + joins them. A backslash starts an escape:

| Escape | Gives | | --- | --- | | \n \t \r \0 | new line, tab, carriage return, the zero character | | \" \' \\ | the quote or backslash itself | | \ + anything else | that character, e.g. \{ in a backtick string |

Text is counted in letters as people see them, so a Devanagari letter and its vowel signs count as one. This applies to lambai, indexes, ulta, hissa, khoj, tod(text, "") and har … me:

maan la s = "किताब";
bol ho lambai(s), s[0], ulta(s);     // 3 कि बताकि
har c "नाम" me { bol ho c; }         // ना, then म

Unicode calls these letters grapheme clusters. An emoji with a skin tone is also one letter. How some Devanagari conjuncts (like स्ते in नमस्ते) are grouped follows the Unicode version of the browser or Node.js running the program. Newer versions keep a conjunct together, so lambai("नमस्ते") is 3.

Text in strings

Write a string with backticks to put values straight into the text. Whatever is inside { } is worked out and written in, the same way bol ho would show it:

maan la naam = "Ramu", umar = 24;
bol ho `Pranam {naam}, agila saal {umar + 1} ke ho jaiba`;   // Pranam Ramu, agila saal 25 ke ho jaiba
bol ho `Saaman: {jod(saaman, ", ")}`;
  • Any expression works inside { }: maths, function calls, indexes, even another string.
  • Write \{ for a literal brace. "..." and '...' strings don't change: braces in them are just text.
  • Like other strings, a backtick string has to end on the same line.

Conditions

jadi (a > 10) {
  bol ho "bada";
} na ta jadi (a > 5) {
  bol ho "beech ke";
} na ta {
  bol ho "chhota";
}

Loops

har counts from one number to another. Both ends are included, just like "1 se 10 tak":

har i = 1 se 10 tak {
  bol ho i;                 // 1, 2, … 10
}

har i = 10 se 0 tak kadam -2 {
  bol ho i;                 // 10, 8, 6, 4, 2, 0 (kadam sets the step; it defaults to 1)
}

har … me visits each item of a list, each letter of a string, or each key of a kosh:

har phal ["aam", "kela"] me {
  bol ho phal;
}
  • The loop variable only exists inside the loop.
  • se, tak, kadam and me are only special inside a har loop's header. Everywhere else they're ordinary names, so you can still use them as variables.
  • The start, end and step are worked out once, before the loop starts. Changing the loop variable or the list inside the loop doesn't change which values the loop visits.
  • With a fractional step, values are rounded to 15 significant digits, so har i = 0 se 1 tak kadam 0.1 gives 0, 0.1, … 1 without floating-point leftovers like 0.30000000000000004.

jab le repeats while a condition is true:

maan la i = 0;
jab le (i < 10) {
  i += 1;
  jadi (i == 3) { aage badha; }
  jadi (i == 8) { bas kara; }
  bol ho i;
}

bas kara (break) and aage badha (continue) work in both kinds of loop.

Functions

kaam jodo(a, b) {
  lauta da a + b;
}
bol ho jodo(2, 3);          // 5
  • lauta da; with no value, or reaching the end of the function, returns khaali.
  • Functions can call themselves (recursion). They're values too: you can pass them to other functions or return them.
  • Functions remember the variables around them (closures):
kaam counter() {
  maan la n = 0;
  kaam badhaw() { n += 1; lauta da n; }
  lauta da badhaw;
}
maan la ginti = counter();
ginti(); ginti();
bol ho ginti();             // 3

Recursion can go about 1,000 calls deep (roughly Python's default). Deeper recursion gives an error instead of crashing.

Functions without names

kaam(x) { ... } without a name makes a function right where a value is expected. That's handy for passing a small function to another one:

maan la dugna = kaam(x) { lauta da x * 2 };
bol ho dugna(21);                                           // 42

maan la ank = [1, 2, 3, 4, 5, 6];
bol ho badal(ank, kaam(x) { lauta da x * 10 });             // [10, 20, 30, 40, 50, 60]
bol ho chhaan(ank, kaam(x) { lauta da x % 2 == 0 });        // [2, 4, 6]
bol ho badal(["aam", "kela"], bada);                        // ["AAM", "KELA"]

badal ("change") calls the function on every item and gives a new list of the results. chhaan ("sift") gives a new list of the items the function says sach to. Both leave the original list alone, and any function works: one without a name, one made with kaam naam(...), or a built-in like bada.

Lists

maan la saaman = ["aalu", "pyaaz"];
daal(saaman, "sattu");      // add to the end
bol ho saaman[0];           // aalu
saaman[1] = "tamatar";
bol ho lambai(saaman);      // 3
bol ho nikaal(saaman);      // sattu (removes the last item)
bol ho saaman;              // ["aalu", "tamatar"]

Indexes start at 0. Strings can be indexed too ("ghar"[0] is g), but they can't be changed. Lists are shared by reference, so a function that changes a list changes it for the caller too.

Dictionaries (kosh)

A kosh stores values under keys:

maan la ramu = { "naam": "Ramu", "umar": 24 };
bol ho ramu["naam"];            // Ramu
ramu["gaon"] = "Ballia";        // add a key, or change one that exists
ramu["umar"] += 1;
bol ho lambai(ramu);            // 3
bol ho ramu;                    // {"naam": "Ramu", "umar": 25, "gaon": "Ballia"}

jadi (ba(ramu, "phone")) {      // does the key exist?
  bol ho ramu["phone"];
}
hataw(ramu, "gaon");            // remove a key; returns its value

har k ramu me {                 // loop over the keys
  bol ho k, "=", ramu[k];
}
  • Keys can be strings or numbers, and 1 and "1" are different keys.
  • Keys stay in the order they were added. chaabi(d) gives them as a list.
  • Reading a key that doesn't exist is an error, so a typo in a key name gets caught. Check first with ba(d, key). hataw on a missing key just returns khaali.
  • Like lists, a kosh is shared by reference, and == is only sach for the very same kosh.
  • A { at the start of a statement is still a block. A kosh literal is only read where a value is expected, such as after =, in bol ho or as a function argument.

Built-in functions

| Name | Meaning | | -------------------- | ---------------------------------------- | | lambai(x) | length of a list or string, or the number of keys in a kosh | | daal(list, value) | add value to the end of list | | nikaal(list) | remove and return the last item (khaali if empty) | | sankhya(text) | turn text into a number: sankhya("42") is 42 | | shabd(x) | turn any value into text: shabd(42) is "42" | | kism(x) | the type of a value: sankhya, shabd, list, kosh, sach/jhooth, khaali or kaam | | gol(n) | round to the nearest whole number: gol(2.6) is 3. Halves round up: gol(2.5) is 3, gol(-2.5) is -2 | | gol(n, dashamlav) | round to that many decimal places (0 to 15): gol(2.345, 2) is 2.35, and gol(1.005, 2) is 1.01 | | neeche(n) | round down: neeche(7 / 2) is 3 | | upar(n) | round up: upar(7 / 2) is 4 | | bina_chinh(n) | the number without its sign (absolute value): bina_chinh(-5) is 5 | | sanyog(a, b) | random whole number from a to b, both included | | bada(text) | text in UPPER case | | chhota(text) | text in lower case | | tod(text, sep) | split text into a list: tod("a,b", ",") is ["a", "b"] | | jod(list, sep) | join a list into text: jod(["a", "b"], "-") is "a-b" | | chaabi(kosh) | list of the keys, in the order they were added | | ba(x, item) | sach if a kosh has the key, a list has the item, or a string contains the text | | hataw(kosh, key) | remove a key and return its value (khaali if it wasn't there) | | poochh(question) | ask a question and return the typed answer as text (see Input) | | chhaant(list) | a new, sorted list: all numbers, or all strings | | ulta(x) | a list or string, reversed | | hissa(x, start, end) | part of a list or string (see below) | | khoj(x, item) | where item first appears in a list (or text in a string), or -1 | | kul(list) | the total of a list of numbers | | sabse_bada(list) | the largest item (all numbers, or all strings) | | sabse_chhota(list) | the smallest item | | saaf(text) | text without the spaces, tabs and newlines at the start and end | | jagah(text, purana, naya) | text with every purana replaced by naya | | shuru_me(text, x) | sach if the text starts with x | | ant_me(text, x) | sach if the text ends with x | | badal(list, kaam) | a new list: the kaam applied to every item (see Functions without names) | | chhaan(list, kaam) | a new list of the items the kaam says sach to |

maan la umar = sankhya("24");
bol ho "Agila saal:", umar + 1;             // Agila saal: 25
bol ho jod(tod("aalu pyaaz sattu", " "), ", ");  // aalu, pyaaz, sattu
bol ho "Paasa:", sanyog(1, 6);

Working with lists:

maan la ank = [42, 7, 19, 3];
bol ho chhaant(ank);          // [3, 7, 19, 42]   (ank itself doesn't change)
bol ho ulta(ank);             // [3, 19, 7, 42]
bol ho hissa(ank, 1, 3);      // [7, 19]          from index 1 up to (not including) 3
bol ho hissa(ank, -2);        // [19, 3]          leave out the end to go to the end; negatives count back
bol ho khoj(ank, 19), kul(ank);   // 2 71
bol ho ba(ank, 7), ba("namaste", "mas");   // sach sach

Working with text:

maan la naam = saaf("  Ramu  ");                    // "Ramu"
bol ho jagah("aam aam kela", "aam", "seb");          // seb seb kela
bol ho shuru_me(naam, "Ra"), ant_me("x.bhoj", ".bhoj");   // sach sach

chhaant, ulta and hissa return new lists and leave the original alone. chhaant sorts numbers by size and strings by character code, so capital letters come before lowercase ones.

Giving a built-in the wrong kind of value is an error, not a silent wrong answer: sankhya("abc") stops the program and says "abc" isn't a number.

Built-in names are ordinary variables, so you can reuse the names for your own variables.

Input

poochh asks a question, waits for an answer and returns it as text:

maan la naam = poochh("Tohar naam ka ba? ");
bol ho "Pranam,", naam;

maan la umar = sankhya(poochh("Umar? "));    // turn the answer into a number
bol ho "Agila saal:", umar + 1;
  • The question is optional: poochh() just waits for an answer.
  • The answer is always text. Use sankhya(...) for numbers.
  • When there's nothing left to read, poochh returns khaali. That happens at the end of piped input, after Ctrl+D in a terminal, or on Cancel in the playground. Check with jadi (jawab == khaali).
  • In a terminal, it reads a line of what you type, and piped input works too: printf '50\n25\n' | bhojpuri examples/andaaz.bhoj.
  • In the playground, it opens the browser's question box. The box also shows what was printed since the last question, and the question and answer are added to the output.

Catching errors

koshish kara ("try doing") runs a block. If something goes wrong inside it, even deep inside a function it calls, the galti pe ("on error") block runs instead of the program stopping:

koshish kara {
  maan la umar = sankhya(poochh("Umar? "));
  bol ho "Agila saal:", umar + 1;
} galti pe (g) {
  bol ho "Galti bhail:", g;     // g is the error message
}

phenk da ("throw it") raises your own error. It can be any value, and galti pe (g) gets it unchanged:

kaam bhugtaan(paisa) {
  jadi (paisa < 100) {
    phenk da { "kod": 402, "sandesh": "Paisa kam ba" };
  }
  lauta da "ho gail";
}

koshish kara {
  bhugtaan(50);
} galti pe (g) {
  bol ho g["sandesh"];          // Paisa kam ba
}
  • The (g) is optional: } galti pe { is fine when you don't need the error.
  • A phenk da that nobody catches stops the program and shows the value as the error message.
  • Runtime errors can be caught, including runaway recursion and the playground's loop guard. Syntax errors can't, because they're found before the program starts.
  • bas kara, aage badha and lauta da work inside both blocks.
  • Write galti pe on the same line as the } that closes koshish kara, as with na ta. This matters most at the interactive prompt.
  • On their own, koshish, galti and phenk are still ordinary names. Only the two-word forms are keywords.

Using other files: le aaw

le aaw ("bring it") runs another .bhoj file and makes its top-level functions and variables available:

// lib/ganit.bhoj
ka ho bhaiya
  maan la PI = 3.14159;
  kaam varg(n) { lauta da n * n }
chalat bani bhaiya
// hisaab.bhoj
ka ho bhaiya
  le aaw "lib/ganit.bhoj";
  bol ho varg(7), PI;          // 49 3.14159
chalat bani bhaiya
  • Paths: they're relative to the file that says le aaw, and .bhoj can be left off (le aaw "lib/ganit").
  • Each file runs once, however many files bring it in.
  • Loops and clashes: files that bring each other in are an error, and so is a name that already exists in the file doing the bringing.
  • Errors name the file they come from, with its own line: Chalat samay galti (lib/ganit.bhoj, line 4, col 23): ....
  • Where it works: everywhere. In the bhojpuri command and the interactive prompt it reads files from disk. In the playground, + Naya file adds a file tab, and le aaw finds the other tabs; the program always starts from main.bhoj, and share links carry every file. See examples/hisaab.bhoj, which is also in the playground's example list.

Operators

+ - * / %, == != < > <= >=, && || !. + joins strings, for example "umar: " + 20 (a backtick string like `umar: {20}` is often easier to read). The falsy values are jhooth, khaali, 0 and "". Everything else counts as true.

Decimals are shown to 15 significant digits, so bol ho 0.1 + 0.2 prints 0.3 instead of 0.30000000000000004. Only the display is rounded, not the value. Like most languages, 0.1 + 0.2 == 0.3 is still jhooth, so compare decimals with gol (for example gol(x * 100) == 30) rather than ==.

Comments use // ... and /* ... */.

Errors

Errors point to the exact line and column:

Chalat samay galti (line 3, col 14): "b" naam ke koi variable na ba. Pahile "maan la b" likh ke banaw.
  3 |   bol ho a + b;
    |              ^

When a name looks like a typo of one that exists, the error suggests it. This works for variables, functions, built-ins and kosh keys:

Chalat samay galti (line 3, col 10): "naam" naam ke koi variable na ba. Kahin "naamm" ta na?

Changing the keywords

All keywords and built-in function names live in src/keywords.js, and all error text lives in src/messages.js. Edit a value there and the tokenizer, CLI, playground and error messages pick it up, and so do the playground's colours. After renaming keywords:

  • regenerate the VS Code grammar with node scripts/build-vscode-grammar.js (the tests fail if it's out of date);
  • update the .bhoj examples and the tests.

Editor support

editors/vscode/ is a VS Code extension for .bhoj files. It colours keywords, strings, numbers, comments, built-in calls and function names, including the {…} in backtick strings. It also handles comment toggling, bracket and quote closing, and indentation. To install it from this repository:

cp -r editors/vscode ~/.vscode/extensions/aayazk.bhojpuri-lang-0.1.0

Then restart VS Code. See its README for details.

How it works

source code ──► tokenizer ──► tokens ──► parser ──► AST ──► interpreter ──► output
              src/tokenizer.js         src/parser.js       src/interpreter.js
  • Tokenizer splits the source into keywords, numbers, strings, names and operators. Multi-word keywords are matched longest-first.
  • Parser is a recursive-descent parser that builds an abstract syntax tree (AST). The full grammar is at the top of src/parser.js.
  • Interpreter walks the AST with a chain of scopes. bas kara, aage badha and lauta da are returned as signals up to the enclosing loop or function. Functions keep a reference to the scope they were defined in, which is what makes closures work.
    • src/values.js: the kinds of value and how they're shown.
    • src/scope.js: variables.
    • src/builtins.js: the built-in functions, grouped by topic.

Using it as a library

// npm install @aayazk/bhojpuri-lang
import { run, formatError, BhojpuriError } from "@aayazk/bhojpuri-lang";

const lines = [];
try {
  run(source, {
    print: (line) => lines.push(line),     // where bol ho goes (default: console.log)
    input: (question) => "Ramu",           // answers poochh; return null for "no more input"
    maxLoopIterations: 100_000,            // stop runaway loops
  });
} catch (err) {
  if (err instanceof BhojpuriError) console.error(formatError(err, source));
}

To build your own prompt, use Session. It keeps variables between inputs, and run returns the value to show:

import { Session } from "@aayazk/bhojpuri-lang";

const session = new Session({ print: console.log });
session.run(`maan la x = 20`);
session.run(`x + 1`);          // { exit: false, result: "21" }
session.isComplete(`kaam f() {`); // false: still waiting for the closing }

For le aaw, pass loadFile(path, from), which returns { id, name, source } for the file (or null), and file, the main program's id. id is how a file is recognised (e.g. its full path), name is shown in error messages, and from is the id of the file that asks.

Without an input option, poochh stops with a Bhojpuri error, because there's no one to answer it. There's also a random option that replaces Math.random for sanyog, which is handy in tests.

Everything the package exports:

| Export | What it is | | --- | --- | | run(source, options) | tokenizes, parses and runs a program. Options: print, input, maxLoopIterations, random, loadFile, file (all described above) | | Session | the interactive prompt: new Session(options), then run(source) → { exit, result } and isComplete(source) | | BhojpuriError | what run throws for a mistake in the program | | formatError(err, source) | the error as text, with the line and a ^ under the column | | tokenize(source), parse(tokens), Interpreter | the three steps run uses, for tools that need them separately | | display(value) | a value as bol ho would print it | | KEYWORDS, KEYWORD_MEANINGS, LOOP_WORDS, BUILTINS, BUILTIN_MEANINGS | the names of everything, and what each means in English |

A BhojpuriError has these fields:

  • kind: "SyntaxError" or "RuntimeError".
  • message: the Bhojpuri message. Its wording may improve between versions.
  • line, col: where the mistake is.
  • file, source: the name and code of the file it's in, when that's a file brought in with le aaw rather than the main program.
  • value: the value given to phenk da, for an uncaught phenk da.

Project layout

bin/bhojpuri.js      CLI
src/                 tokenizer, parser, interpreter, values, scope, builtins, keywords, messages,
                     suggest (did-you-mean hints), session (interactive prompt)
examples/*.bhoj      sample programs (examples/lib/ holds a file that hisaab.bhoj brings in)
playground/          browser playground (uses src/ directly as ES modules)
editors/vscode/      VS Code extension (colours, comments, brackets)
scripts/serve.js     zero-dependency static server for the playground
scripts/build-vscode-grammar.js   generates the VS Code grammar from src/keywords.js
test/                node:test suite, one *.test.js file per topic

Contributing

Changes go through Git Flow: branch off develop, document the change in CHANGELOG.md, and open a pull request. main only receives releases. See CONTRIBUTING.md.

License

MIT © Ayyaz Khan