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

swapro

v4.0.1

Published

swap is a programming language that allows programmers to code in Swahili language constructs

Readme

Swap

Swahili Programming Language (SWAP)

SWAP is a programming language that uses Swahili keywords to represent common programming constructs. It is designed to be simple and easy to learn, making it accessible for beginners and those who are more comfortable with the Swahili language.

It was the first of its kind to bring programming concepts to Swahili speakers in a native language context, and it has inpired to the creation of other programming languages in Swahili, such as pyswahili and swahili-lang.

Credits

Lots of credit to the author of yorlang @anoniscoding, he's the one who inspired me to create this programming language, and also for his great work on yorlang which was the base foundation for the initial version of swap.

TUTORIAL

INSTALLATION

Getting Started with Swap you need to have node runtime installed on your machine, After making sure that node runtime works on your machine run the following command

npm install -g swapro

After installation download run the command

swap -h

if your installation was successful the following result will show up

  ██████    ██      ██    ██████    ████████
██      ██  ██      ██  ██      ██  ██      ██
██          ██      ██  ██      ██  ██      ██
  ██████    ██  ██  ██  ██████████  ████████
        ██  ██  ██  ██  ██      ██  ██
██      ██  ████  ████  ██      ██  ██
  ██████    ██      ██  ██      ██  ██

swahili programming language v3.0.0
by Abdulbasit Rubeya

Usage: cli [options] [file]

Options:
  -v, --version      output the version number
  -l, --lang <lang>  language for error messages (english|swahili)
  -h, --help         output usage information
author: Abdulbasit Sultan Rubeiyya
Examples:
  $ swap file.sw
  $ swap -h
  $ swap -v

The banner is only shown for these info commands (bare swap, -h/--help, -v/--version, -i/--interactive) - it's skipped while a .sw file is running so it doesn't clutter your program's output, and it's drawn in a random color gradient each time, so don't worry if the colors look different between runs.

INTERACTIVE REPL

Run swap -i (or swap --interactive) to start an interactive session instead of running a file - useful for trying out small snippets of Swap without creating a .sw file. Variables and functions you declare stay available for the rest of the session, so you can build on earlier lines:

$ swap -i
swap> hifadhi x = 5;
swap> andika x + 10;
15
swap> toka;
Kwaheri!

Statements are evaluated one at a time as you press Enter, same as in a file - each one still needs its own terminating ;. A block that spans multiple lines (wakati, kama, njia, jaribu, ...) is buffered and only run once its closing } is typed, shown with a continuation prompt (...>). Type toka; to exit the session.

Unlike Node's REPL, a bare expression like 1 + 2 on its own is not valid syntax (same as in a .sw file) - use andika ...; to print a value.

Runtime errors are reported as a single clean message (e.g. Error: There's an error at line 3 near column 10 in file example.sw : ...) rather than a raw Node.js stack trace. If you're working on the interpreter itself and want the full JS stack trace, run with SWAP_DEBUG=1, e.g. SWAP_DEBUG=1 swap file.sw.

To program using swap we will be using vscode, so open the program and install the swap extension

  1. Linux : no more configurations start right away
  2. Windows: no extra configuration needed either — swap accepts LF (Unix), CRLF (Windows), and legacy CR (old Mac) end-of-line formats and normalizes them automatically, so your editor's default line ending just works.

NOTE: The file extension for a swap file is .sw

First Program

Swap does not use any preprocessors, It uses the constant andika to print out the desired content, andika literally means "write"

eg. 1

andika "habari yako";

Result:

habari yako

Every line of code has and must be terminated by a delimiter which is a semicolon(;)

To run your program initiate a CMD in your project directory and run the command swap file.sw

File should be replaced with the name of your file.

Comments

Swap supports three comment styles: # and // for a single-line comment (everything up to the end of the line is ignored), and /* ... */ for a comment that can span multiple lines.

eg. 1b

# this is a comment
// this is also a comment
/* this is a
   multi-line comment */
andika "habari"; // prints habari

Result

habari

Note that string literals have no escape sequence, so a " can't appear inside a string, and there's no way to write a literal */ inside a block comment either — the first */ a block comment finds always ends it.

Variable Declaration

Variables can only be declared in Swap using the keyword hifadhi which means to store something.

eg. 2

hifadhi a = 10;
hifadhi b = 20;
andika a + b;

Result

30

eg. 3

hifadhi jina= "juma";
hifadhi umri = 5 ;
andika jina + " " + "is" + " " + umri + " " + "years old" ;

Result

Juma is 5 years old

Requesting User Input

Swap also supports the program interactive programming by requesting inputs from the user, the input request constant is dai which means "request".

eg. 4

hifadhi jina = dai("andika jina lako: ");

Displaying User Input

eg. 5

hifadhi jina = dai("andika jina lako: ");
andika "Habari " + jina;

Assuming after running the program the user provided it with input "Abdulbasit", then the results will be

Habari Abdulbasit

Conditionals like if, else, else if and switch statements are also defined and used in Swap language.

If / Else If Conditions

if is denoted by kama else is denoted by basi else if as basi kama

eg. 6

hifadhi umri = 20;
kama ( umri < 18 ){
    hifadhi makamo = "mtoto" ;}
    basi kama ( umri > 18 && umri < 50){
    hifadhi makamo = "kijana" ;}
basi {
    hifadhi makamo = "mzee" ;}
andika "juma ni " + makamo ;

Result

juma ni kijana

Explanation: The above program is used to show from which age group does a person belong whether young, youth or an old person.

Switch Case

Also, switch case expressions are included. The switch case is only evaluated once, the value of each expression is always compared with the values of each case. If there is a match, the associated code block is run and then escapes the sequence. Unlike loops, chagua already stops as soon as the matching kesi (or zaidi) block finishes running - there's no fall-through to the next case, so the vunja; (break;) statement is neither needed nor valid inside a chagua block; using it there will throw a parse error. vunja; is only for escaping wakati/hakika loops early.

The switch case values are presented as follows:

| Concept | Swap keyword | |---|---| | switch | chagua | | case | kesi | | default | zaidi |

eg. 7

andika "1. cct basic";
andika "2. cct ordinary";
hifadhi teule = dai("weka chaguo lako hapa: ");
wakati (teule > 0){
    chagua (teule){ 
        kesi 1 :
        andika "chaguo lako ni: " + teule ;
        kesi 2 :
        andika "chaguo lako ni: " + teule ;
        zaidi :
        andika "umekosea tafadhali chagua tena";
    }
    hifadhi teule = dai("weka chagua lako hapa: ");
}

Explanation: The following program prompts a user to choose a tv package. If a program choice is present and is matched with the associated case, the program will echo the user's input and exit, else if the input value is not matched the program will continue to loop until a right input is given or the program is manually terminated.

Increments and Decrements

Increments and decrements like i++ or i-- are not valid and would throw out a fatal error when used. To declare an increment or decrement, normal mathematical expressions are used, i.e.

hifadhi a = a + 1; // for increment
hifadhi a = a - 1; // for decrement

Loops

for Loop

In Swap, the "for" loop is implemented by the hakika statement and expressed as

hakika(hifadhi a = 0; a < 10; hifadhi a = a+1){
    //statement
}

while Loop

In Swap, the while loop is represented by the wakati() statement and is expressed as

wakati(hali/condition){
    //statement;
}

eg. 8

hifadhi a = 10;
wakati(a!=0){
    a = a-1;
}

Result: The program will continue to run until variable "a" decreases to 0. The vunja; keyword can be used to escape loops when necessary.

Functions

A function is a code-block that performs a certain task. In Swap, a function can be a group of a procedure which performs a certain work or can be used to return a value.

Functions in Swap can be defined as independent modules of code blocks that perform certain work.

eg. 9

The function keyword is held by njia.

njia hesabu (a, b){
    rejesha a+b;
}
hifadhi x = hesabu(12,6);
andika x;

or

njia hesabu (a, b){
    andika a+b;
}
hesabu(12,6);

or

njia hesabu (a, b){
    rejesha a+b;
}
andika hesabu(12,6);

Result

18

In Swap there is no pre-declaration of functional prototypes as in languages like C++, therefore the use of functions have to be fully declared before they are called.

Functions as Values (Lambdas)

A njia written where a value is expected — instead of as its own top-level statement — evaluates to a callable value instead of registering a named function. This lets you store a function in a variable, pass one as an argument, or return one from another function.

eg. 9b

hifadhi ongeza = njia (a, b) {
    rejesha a + b;
};
andika ongeza(2, 3);

Result

5

Note the trailing ; after the lambda's closing } — unlike a normal top-level njia declaration, this whole njia (...) { ... } is the right-hand side of a hifadhi ... = ...; assignment, so it needs the same statement-terminating ; any other assignment does.

A lambda can be passed as an argument and called from inside the function it's passed to:

eg. 9c

hifadhi ongezaMaraMbili = njia (a) {
    rejesha a * 2;
};

njia tumia(kazi, x) {
    rejesha kazi(x);
}

andika tumia(ongezaMaraMbili, 10);

Result

20

A lambda can also call itself recursively, and (unlike this version's named njia functions — see the note below) doing so is safe: each call gets its own private set of local variables, so a recursive lambda won't see its own parameters overwritten by a nested call to itself.

Note on scope: a lambda does not automatically capture variables from where it was defined (no lexical closures). Reaching an outer variable from inside a lambda still requires ita, exactly as with named functions (see Variable Scopes) — and resolves dynamically, against whichever scope is actually live on the call stack at the moment the lambda runs, not against the scope it was written in. A pattern like a "counter factory" that expects a returned function to remember state private to where it was created will not behave as it would in a language with true lexical closures.

Known limitation: named (non-lambda) njia functions that call themselves recursively currently share one set of local variable bindings across all of the active recursive calls, rather than each call getting its own — so a parameter or local variable read after a recursive call returns can come back with the innermost call's value instead of the current call's own value. Prefer a lambda (as above) over a named function for recursion until this is addressed.

Import

The import keyword is supplemented by the constant lete which literally means "bring". The import (lete) constant is used to import other files into the main program file.

The constant is followed by a string value which should contain the path to the imported file, and this path must be provided as a suffix to the absolute path of the needed file.

eg. 10

lete "file.sw";

The command will bring a file.sw into your program.

Suppose you want to import a file from another directory:

lete "PATH/file.sw";

Error Handling

Runtime errors (e.g. dividing by zero, or reading an undefined variable) normally stop the whole program. To recover from one and keep running, wrap the risky code in a jaribu ("try") block followed by a required kamata ("catch") block. kamata takes a single variable name in parentheses; if the jaribu block throws an error, that variable is bound to the error's message and the kamata block runs instead of the program crashing.

eg. 12b

jaribu {
    hifadhi x = 10 / 0;
} kamata (hitilafu) {
    andika "Kosa limepatikana: " + hitilafu;
}

andika "Programu inaendelea";

Result

Kosa limepatikana: There's an error at line 2 near column 1 in file example.sw :
 SwapArithmeticException - cannot divide by zero
Programu inaendelea

vunja and rejesha used inside a jaribu/kamata block still work exactly as they do anywhere else — a vunja inside a jaribu nested in a loop still breaks that loop, and a rejesha inside a jaribu nested in a njia still returns from that function.

A bare jaribu without a kamata block is not valid — every jaribu must be followed by kamata (varname) { ... }.

Variable Scopes

A variable scope is the setting within which the variable is declared. All the inner functions (njia) have access to the variables that are from the outer function, unlike the inner functions - the outer functions do not have access to their inner functions.

eg. 11

hifadhi a=3;
njia namba(){
    hifadhi a=a+10;
    andika a;
}
namba();
andika a;

Result:

13
3

To adopt the changes of a variable in inner functions, a variable must be marked with ita, giving it the backtick-quoted variable name(s) to link to the outer scope. This will make all changes in the inner function to the outer function noticeable.

eg. 12

hifadhi a=3;
njia namba(){
     ita `a`;
     hifadhi a=a+10;
     andika a;
}
namba();
andika a;

Result:

13 13

Arrays

An array is a data structure that stores multiple elements in a single variable, and in most cases these elements are all of the same types, like integer or string.

Swap supports two types of arrays:

  • the one-dimensional array and
  • the multi-dimensional array.

One Dimensional Array

It is also known as the linear array. All elements stored can be accessed through a single subscript which either represents a row or a column.

eg. 13

hifadhi array = ["moja","mbili"];
andika array;
andika array[0];

Result:

[ 'moja', 'mbili' ]
moja

Multi-Dimensional Array

It is an array that stores data with more than one array level. A multi-dimensional array is used to store several data groups in one variable.

eg. 14

hifadhi array = [["macho","pua","mdomo","sikio"],[1, 2, 3]];
andika array;
andika array[0];
andika array[1][2];

Result:

[ [ 'macho', 'pua', 'mdomo', 'sikio' ], [ 1, 2, 3 ] ]
[ 'macho', 'pua', 'mdomo', 'sikio' ]
3

Elements can be added to a swap array by leaving the index empty in the last position.

eg. 15

hifadhi array = [1, 2, 3];
andika array;
hifadhi array[]=4;
andika array;

Result:

[ 1, 2, 3 ]
[ 1, 2, 3, 4 ]

Maps (ramani)

A ramani ("map") stores data as key/value pairs, rather than by numeric position like an array. Keys are bare identifiers (like variable or function-parameter names) — they can't be quoted strings or computed expressions.

eg. 15b

hifadhi mtu = ramani(jina: "Juma", umri: 25);
andika mtu;
andika mtu.jina;

Result

{ jina: 'Juma', umri: 25 }
Juma

Reading a property uses . (e.g. mtu.jina). Writing to a property — whether changing an existing key or adding a new one — always needs the hifadhi keyword in front, exactly like assigning to an array element:

eg. 15c

hifadhi mtu = ramani(jina: "Juma");
hifadhi mtu.jina = "Asha";
hifadhi mtu.umri = 30;
andika mtu;

Result

{ jina: 'Asha', umri: 30 }

A value inside a ramani can be any type, including an array or another ramani. ramani() with no entries is a valid, empty map. Property access is single-level only in this version — to reach a value nested inside a ramani stored as another ramani's value, read the inner one into its own variable first (e.g. hifadhi anwani = mtu.anwani; andika anwani.mtaa;) rather than chaining mtu.anwani.mtaa directly.

Built-in Functions

Swap has several helper functions, the following is a list of those helper functions.

herufiKubwa

herufiKubwa is used to convert a string value of a variable into uppercase letters.

eg. 16

andika herufiKubwa("herufi");

Result

HERUFI

herufiNdogo

herufiNdogo is the inverse of herufiKubwa. It converts a string value of a variable into lowercase letters.

eg. 17

andika herufiNdogo("HerUFI");

Result

herufi

kaunta

kaunta is used to count the length of an array.

eg. 18

andika kaunta([26,78,75,"mango"]);

Result

4

hariri

hariri constant is used to edit a part of a string or substring of a string.

eg. 19

andika hariri("wewe ni mbaya", "mbaya", "mzuri");

The function hariri takes in three arguments. Assuming the parameters used by the function hariri are x, y and z, then:

  • x will be the initial input
  • y is the string to find and replace in the input of x
  • z is the string value to replace the input of y

Result

wewe ni mzuri

It has replaced the string "mbaya" in a sentence with the string "mzuri".

tafuta

tafuta constant is used to find a substring in a string.

eg. 20

andika tafuta ("wewe ni mbaya", "mbaya");

Result

kweli

If the substring does not exist in the main string then it would have returned

sikweli

badili

badili is Swap's str_replace equivalent. It replaces occurrences of a substring within a string, and supports several replacement styles depending on the arguments given: simple, multi, global ("yote"), and regex.

Simple replacementbadili(neno, kutafuta, badala) replaces the first occurrence of kutafuta found in neno with badala.

eg. 21

andika badili("wewe ni mbaya", "mbaya", "mzuri");

Result

wewe ni mzuri

Global replacement — passing "yote" (meaning "all") as a fourth argument replaces every occurrence of kutafuta instead of just the first.

eg. 22

andika badili("mbaya mbaya mbaya", "mbaya", "mzuri", "yote");

Result

mzuri mzuri mzuri

Multi replacement — passing arrays of equal length for kutafuta and badala replaces every occurrence of each pair throughout the string, similar to PHP's array form of str_replace.

eg. 23

andika badili("mbwa na paka", ["mbwa","paka"], ["ng'ombe","kuku"]);

Result

ng'ombe na kuku

Note that replacements happen sequentially, one pair at a time (like PHP's str_replace) — so swapping two values via badili(neno, ["a","b"], ["b","a"]) will not work as expected, since the first replacement's output becomes the input for the second.

Regex replacement — passing "regex" as a fourth argument treats kutafuta as a regular expression pattern. An optional fifth argument supplies regex flags (e.g. "g" for global, "i" for case-insensitive).

eg. 24

andika badili("a1 b2 c3", "[0-9]", "#", "regex", "g");

Result

a# b# c#

More Built-in Functions

The following helper functions are also available. See examples/12_more_helpers.sw for a runnable demo of all of them.

Math

| Function | Description | Example | Result | |---|---|---|---| | zungusha(namba) | round to the nearest integer | zungusha(4.5) | 5 | | sakafu(namba) | round down (floor) | sakafu(4.9) | 4 | | paa(namba) | round up (ceiling) | paa(4.1) | 5 | | kamili(namba) | absolute value | kamili(-7) | 7 | | kubwa(...namba) | largest of the given numbers | kubwa(3, 7, 2) | 7 | | ndogo(...namba) | smallest of the given numbers | ndogo(3, 7, 2) | 2 | | kipeo(msingi, kiwango) | power (msingi to the kiwango) | kipeo(2, 10) | 1024 | | mzizi(namba) | square root | mzizi(9) | 3 |

Arrays

| Function | Description | Example | Result | |---|---|---|---| | ongeza(orodha, thamani) | push a value onto the end (mutates, returns the array) | ongeza([1,2,3], 4) | [1,2,3,4] | | toa(orodha) | remove and return the last element (mutates) | toa([1,2,3]) | 3 | | panga(orodha) | sort ascending, in place (numeric if all numbers, else text) | panga([10,2,33,4]) | [2,4,10,33] | | unganisha(orodha, kitenganishi) | join elements into a string | unganisha(["moja","mbili"], "-") | "moja-mbili" | | geuza(orodha) | reverse, in place | geuza([1,2,3]) | [3,2,1] | | kata(orodha, mwanzo, mwisho) | slice (does not mutate); mwisho is optional | kata([1,2,3,4,5], 1, 3) | [2,3] |

Strings

| Function | Description | Example | Result | |---|---|---|---| | pogoa(neno) | trim leading/trailing whitespace | pogoa(" hi ") | "hi" | | jaza(neno, urefu, tabia, mahali) | pad to a length; pads at the start unless mahali is "mwisho" | jaza("5", 3, "0") | "005" | | gawa(neno, kitenganishi) | split into an array; splits into characters if no separator given | gawa("moja,mbili", ",") | ["moja","mbili"] |

Type / JSON

| Function | Description | Example | Result | |---|---|---|---| | ainaYa(thamani) | returns "namba", "neno", or "orodha" for the value's type | ainaYa(5) | "namba" | | kwaJson(thamani) | serialize a number/string/array/ramani to a JSON string | kwaJson([1,2,3]) | "[1,2,3]" | | kutokaJson(maandishi) | parse a JSON string into a number/string/array/ramani | kutokaJson("[1,2,3]") | [1,2,3] |

Note: kweli/sikweli (boolean) values share the same internal representation as strings, so ainaYa currently reports "neno" for a boolean value rather than a distinct boolean type name. Also, Swap string literals have no escape sequence, so a literal " can't appear inside one directly — if you need JSON text containing quotes in source code, build it with kwaJson rather than typing it by hand (see examples/15_closure_helpers.sw).

Functions that take a function (closures)

These take a njia value (see Functions as Values) as one of their arguments — either a variable holding a lambda, a named function passed by name, or an inline lambda literal.

| Function | Description | Example | Result | |---|---|---|---| | ramanisha(orodha, kazi) | map — build a new array from calling kazi on each element | ramanisha([1,2,3], njia(x){ rejesha x*2; }) | [2,4,6] | | chuja(orodha, kazi) | filter — keep elements where kazi is truthy (anything but sikweli) | chuja([1,2,3,4], njia(x){ rejesha x>2; }) | [3,4] | | punguza(orodha, kazi, awali) | reduce — fold the array to one value, starting from the required awali | punguza([1,2,3,4], njia(j,x){ rejesha j+x; }, 0) | 10 | | kilamoja(orodha, kazi) | forEach — call kazi once per element for side effects (e.g. andika); returns the array unchanged | kilamoja([1,2], njia(x){ andika x; }) | prints 1, 2 |