NashmiC Logo
Open Source · MIT License

Code in Jordanian.

NashmiC is a compiled programming language with Franco-Arab keywords. Every keyword is a word you'd actually say in Amman. Not textbook Arabic, real dialect.

Why Franco-Arab? Arabic script goes right-to-left. That breaks every terminal, every diff tool, every code editor cursor. Half the Arabic letters change shape depending on position. Keyboard layouts fight you. Tooling barely exists. Franco-Arab (Arabizi) sidesteps all of this — it uses Latin characters that every dev tool already supports, with numbers like 3 for ain and 2 for hamza, exactly like millions of Arabs text every day. You get Arabic meaning with zero tooling friction.

curl -fsSL https://raw.githubusercontent.com/Ziadstr/nashmic/main/install.sh | bash copy
nashmic
dalle fibonacci(n: adad64) -> adad64 {
    iza n <= 1 {
        rajje3 n
    }
    rajje3 fibonacci(n - 1) + fibonacci(n - 2)
}

yalla() {
    lakol i fi 0..15 {
        itba3("fib({i}) = {fibonacci(i)}\n")
    }
}

Getting Started

Install NashmiC and write your first program in under two minutes.

Requirements

  • A C11 compiler (gcc or clang)
  • make
  • git
  • Linux or macOS

Install

Homebrew (macOS / Linux)

bash
brew tap Ziadstr/nashmic
brew install nashmic

AUR (Arch Linux)

bash
yay -S nashmic-git

Debian / Ubuntu

Download the .deb from Releases and install:

bash
sudo dpkg -i nashmic_*.deb

One-liner

bash
curl -fsSL https://raw.githubusercontent.com/Ziadstr/nashmic/main/install.sh | bash

Build from source

bash
git clone https://github.com/Ziadstr/nashmic.git
cd nashmic
make
sudo make install

Editor Support

Install the VS Code extension for syntax highlighting and snippets:

bash
code --install-extension ziadstr.nashmic

Or search "NashmiC" in the Extensions panel (Ctrl+Shift+X). Includes syntax highlighting for 80+ keywords, 22 code snippets, bracket matching, format-on-save, and run/build commands.

Neovim / Helix / Zed (Tree-sitter)

NashmiC has a full tree-sitter grammar with syntax highlighting queries. See the Neovim setup guide for installation instructions.

Try It Online

Don't want to install? Try NashmiC in your browser with pre-loaded examples:

Open Playground

Your First Program

Create a file called marhaba.nsh:

nashmic
yalla() {
    itba3("marhaba ya 3alam!\n")
}

Every NashmiC program needs a yalla() function. It's the entry point, like main in C. The word means "let's go!" in Jordanian.

Run It

bash
mansaf run marhaba.nsh

mansaf run compiles and executes in one step.

Build a Binary

bash
mansaf build marhaba.nsh
./marhaba

Add --tarab for celebratory audio on success. "Tarab" means musical ecstasy. Because shipping code deserves a party.

Try String Interpolation

nashmic
yalla() {
    khalli name: nass = "Ziad"
    khalli age: adad64 = 23

    itba3("marhaba ya {name}!\n")
    itba3("age: {age}, next year: {age + 1}\n")
}

Try a Loop

nashmic
yalla() {
    lakol i fi 0..10 {
        itba3("{i}\n")
    }
}

lakol means "for each," fi means "in." The range 0..10 goes from 0 up to (not including) 10.

Variables & Constants

Declaring data with khalli and thabet.

Variables with khalli

khalli means "let there be" in Jordanian dialect. It declares a mutable variable.

nashmic
khalli name: nass = "Ziad"
khalli age: adad64 = 23
khalli pi: fasle64 = 3.14159
khalli alive: mante2 = ah

The pattern is always: khalli <name>: <type> = <value>

Constants with thabet

thabet means "fixed" or "firm." This value is not going anywhere.

nashmic
thabet MAX_USERS: adad64 = 1000
thabet PI: fasle64 = 3.14159265
thabet GREETING: nass = "marhaba"

Types

Every variable needs an explicit type annotation. NashmiC does not have type inference yet.

TypeC EquivalentDescription
adadint32_t32-bit integer. "adad" means "number"
adad64int64_t64-bit integer
faslefloat32-bit float. "fasle" means "decimal point"
fasle64double64-bit float
mante2boolBoolean. "mante2" means "logical"
harfcharCharacter. "harf" means "letter"
nassconst char*String. "nass" means "text"
fadivoidNo value. "fadi" means "empty"
Boolean values: ah = true (literally "yeah"), la = false (literally "no").

Functions

Declaring functions with dalle and returning values with rajje3.

Declaring Functions

dalle means "a function" in Arabic. Use -> to specify the return type.

nashmic
dalle add(a: adad64, b: adad64) -> adad64 {
    rajje3 a + b
}

dalle greet(name: nass) {
    itba3("marhaba ya {name}!\n")
}

rajje3 means "bring back." It's the return statement.

The yalla() Entry Point

Every program needs yalla(). It's the first thing that runs. "yalla" means "let's go!"

nashmic
yalla() {
    itba3("the program starts here\n")
}

Recursion

nashmic
dalle fibonacci(n: adad64) -> adad64 {
    iza n <= 1 {
        rajje3 n
    }
    rajje3 fibonacci(n - 1) + fibonacci(n - 2)
}

yalla() {
    lakol i fi 0..15 {
        itba3("fib({i}) = {fibonacci(i)}\n")
    }
}

Control Flow

Conditionals, loops, and branching.

Conditionals: iza / wala

iza means "if." wala means "or else." wala_iza means "or if."

nashmic
iza x > 0 {
    itba3("positive\n")
} wala_iza x == 0 {
    itba3("zero\n")
} wala {
    itba3("negative\n")
}

Conditions don't need parentheses. Body must always be in braces.

For-Each: lakol..fi

lakol means "for each," fi means "in."

nashmic
lakol i fi 0..10 {
    itba3("{i}\n")
}

While: tool_ma

tool_ma means "as long as."

nashmic
khalli count: adad64 = 0
tool_ma count < 10 {
    itba3("{count}\n")
    count = count + 1
}

Infinite Loop: liff

liff means "go around" or "spin." Use khalas ("enough!") to break out.

nashmic
liff {
    iza i >= 10 {
        khalas
    }
    i = i + 1
}

Continue: kammel

kammel means "keep going." Skip to next iteration.

FizzBuzz

nashmic
dalle fizzbuzz(n: adad64) {
    lakol i fi 1..n {
        iza i % 15 == 0 {
            itba3("FizzBuzz\n")
        } wala_iza i % 3 == 0 {
            itba3("Fizz\n")
        } wala_iza i % 5 == 0 {
            itba3("Buzz\n")
        } wala {
            itba3("{i}\n")
        }
    }
}

yalla() {
    fizzbuzz(30)
}

Strings & Interpolation

No format specifiers. Just put expressions in braces.

String Basics

Strings use the nass type ("nass" means "text") and support full UTF-8.

nashmic
khalli message: nass = "marhaba ya 3alam"

Interpolation

Put any expression in {braces} inside strings. The compiler figures out the type.

nashmic
khalli name: nass = "Ziad"
khalli age: adad64 = 23

itba3("marhaba ya {name}!\n")
itba3("age: {age}, next year: {age + 1}\n")

Works with variables, arithmetic, function calls, and struct fields.

Type-Aware Formatting

TypeFormat
adad / adad64integer
fasle / fasle64floating-point
nassstring
mante2boolean

You never specify format codes. The compiler uses C11 _Generic to pick the right one.

Escape Sequences

SequenceMeaning
\nNewline
\tTab
\\Literal backslash
\"Literal double quote
{{Literal brace

Structs

Named data types with typed fields.

Declaring with haikal

haikal means "structure" or "skeleton" in Arabic.

nashmic
haikal Point {
    x: fasle64,
    y: fasle64,
}

yalla() {
    khalli p: Point = Point{ x: 3.0, y: 4.0 }
    itba3("Point: ({p.x}, {p.y})\n")
}

All fields must be initialized at creation. No default values. Access fields with dot notation.

As Function Parameters

Structs are passed by value (copied).

nashmic
dalle print_point(p: Point) {
    itba3("({p.x}, {p.y})\n")
}

Nested Structs

nashmic
haikal Line {
    start: Point,
    end: Point,
}

See Methods & Impl for attaching behavior to structs.

Arrays

Working

saff means "row." Dynamic arrays with push, length, and iteration.

Array Literals

nashmic
khalli a3dad: saff<adad64> = [10, 20, 30, 40, 50]
khalli drajat: saff<fasle64> = [95.5, 87.3, 92.1]
khalli farigh: saff<adad64> = []

Indexing & Length

nashmic
itba3("a3dad[0] = %lld\n", a3dad[0])
itba3("toul: %lld\n", a3dad.toul())

Push & Iteration

.zeed() means "add more." lakol x fi arr iterates over elements.

nashmic
khalli a3dad: saff<adad64> = [10, 20, 30]
a3dad.zeed(40)
a3dad.zeed(50)

lakol n fi a3dad {
    itba3("%lld ", n)
}
itba3("\n")

As Function Parameters

nashmic
dalle print_array(nums: saff<adad64>) {
    itba3("[ ")
    lakol n fi nums {
        itba3("%lld ", n)
    }
    itba3("]\n")
}

Under the Hood

A saff<adad64> compiles to a C struct with a pointer, length, and capacity. Array literals allocate with malloc. The .zeed() method doubles capacity when full, giving you amortized O(1) appends. Iteration with lakol becomes a simple index loop in C.

C (generated)
// saff<adad64> compiles to:
struct nsh_array_int64_t {
    int64_t *data;
    int64_t len;
    int64_t cap;
};

// Array literal [10, 20, 30] becomes:
struct nsh_array_int64_t a3dad;
a3dad.cap = 3;
a3dad.len = 3;
a3dad.data = malloc(sizeof(int64_t) * 3);
a3dad.data[0] = 10;
a3dad.data[1] = 20;
a3dad.data[2] = 30;

// .zeed(40) checks capacity, doubles if full:
if (a3dad.len >= a3dad.cap) {
    a3dad.cap *= 2;
    a3dad.data = realloc(a3dad.data, sizeof(int64_t) * a3dad.cap);
}
a3dad.data[a3dad.len++] = 40;

// lakol n fi a3dad becomes:
for (int64_t _i = 0; _i < a3dad.len; _i++) {
    int64_t n = a3dad.data[_i];
    // body
}

No hidden allocations, no garbage collector. You get exactly what you'd write in C, but without the boilerplate.

Enums (Tagged Unions)

Working

Declaring with ta3dad

ta3dad means "enumeration." Each variant can optionally carry data.

nashmic
ta3dad Shape {
    Da2ira(fasle64),
    Mustateel(fasle64),
    Noqta,
}

Creating Values

nashmic
khalli circle: Shape = Da2ira(5.0)
khalli point: Shape = Noqta
khalli color: Color = Ahmar

With Pattern Matching

nashmic
hasab s {
    hale Da2ira(radius) => {
        itba3("Circle: radius %g\n", radius)
    }
    hale Mustateel(width) => {
        itba3("Rect: width %g\n", width)
    }
    hale Noqta => {
        itba3("Point\n")
    }
}

Each hale arm destructures the variant payload into local variables. Use 3adi as a catch-all default.

Under the Hood

Enums become tagged unions in C. The compiler generates a struct with an int _tag field and a union _v that holds the payload for whichever variant is active. Each variant gets a unique integer tag constant. Pattern matching with hasab/hale compiles to a switch(_tag) statement.

C (generated)
// ta3dad Shape { Da2ira(fasle64), Mustateel(fasle64), Noqta }
// compiles to:

#define Shape_Da2ira    0
#define Shape_Mustateel 1
#define Shape_Noqta     2

struct Shape {
    int _tag;
    union {
        double Da2ira;
        double Mustateel;
        // Noqta carries no data
    } _v;
};

// khalli circle: Shape = Da2ira(5.0) becomes:
struct Shape circle;
circle._tag = Shape_Da2ira;
circle._v.Da2ira = 5.0;

// hasab s { hale Da2ira(radius) => ... } becomes:
switch (s._tag) {
    case Shape_Da2ira: {
        double radius = s._v.Da2ira;
        // arm body
        break;
    }
    case Shape_Mustateel: {
        double width = s._v.Mustateel;
        // arm body
        break;
    }
    case Shape_Noqta: {
        // arm body
        break;
    }
}

Tagged unions give you type-safe variants with zero overhead. The tag is just an int comparison, and the union shares memory across variants so you only pay for the largest one.

Optionals

Working

NashmiC has no null. If a value might be absent, use yimkin<T>.

fi and mafi

fi means "there is." mafi means "there isn't." Ask "fi chai?" (is there tea?) and the answer is "fi" or "mafi." NashmiC uses the same logic.

nashmic
dalle find_even(n: adad64) -> yimkin<adad64> {
    iza n % 2 == 0 {
        rajje3 fi(n)
    }
    rajje3 mafi
}

iza fi binding

Safely unwrap an optional — bind the value if present, run the else branch if not.

nashmic
iza fi result = find_even(10) {
    itba3("found even: %lld\n", result)
} wala {
    itba3("no even number\n")
}

Pattern Matching

nashmic
hasab find_even(7) {
    hale fi(val) => {
        itba3("%lld\n", val)
    }
    hale mafi => {
        itba3("nothing\n")
    }
}

Under the Hood

A yimkin<adad64> compiles to a simple struct with a flag and a value. fi(x) sets the flag to 1 and stores the value. mafi sets the flag to 0. The iza fi binding checks the flag and extracts the value into a local variable.

C (generated)
// yimkin<adad64> compiles to:
struct nsh_option_int64_t {
    int _has_value;
    int64_t _value;
};

// rajje3 fi(n) becomes:
struct nsh_option_int64_t _ret;
_ret._has_value = 1;
_ret._value = n;
return _ret;

// rajje3 mafi becomes:
struct nsh_option_int64_t _ret;
_ret._has_value = 0;
return _ret;

// iza fi result = find_even(10) { ... } becomes:
struct nsh_option_int64_t _tmp = find_even(10);
if (_tmp._has_value) {
    int64_t result = _tmp._value;
    // body
} else {
    // wala body
}

No null pointers, no sentinel values, no segfaults. The compiler forces you to check before you unwrap. The whole thing is just a struct with a flag -- zero magic, maximum safety.

Result Type (natije)

Working

No exceptions. No try/catch. Errors are values.

tamam and ghalat

tamam means "all good." ghalat means "wrong." Every function that can fail returns natije<T>.

nashmic
dalle divide(a: fasle64, b: fasle64) -> natije<fasle64> {
    iza b == 0.0 {
        rajje3 ghalat("ya zalameh, division by zero!")
    }
    rajje3 tamam(a / b)
}

wala? propagation

If error, return it up the chain automatically.

nashmic
dalle safe_math(x: fasle64) -> natije<fasle64> {
    khalli result: fasle64 = divide(x, 2.0) wala?
    rajje3 tamam(result + 1.0)
}

Pattern Matching on Results

Match on tamam/ghalat directly:

nashmic
hasab divide(10.0, 0.0) {
    hale tamam(val) => {
        itba3("%g\n", val)
    }
    hale ghalat(msg) => {
        itba3("Error: %s\n", msg)
    }
}

Under the Hood

A natije<fasle64> compiles to a struct with a discriminator flag and a union holding either the success value or the error string. tamam(x) sets the flag to 1 (success), ghalat(msg) sets it to 0 (error). The wala? propagation operator uses a GNU C statement expression to check the flag and return early if it's an error.

C (generated)
// natije<fasle64> compiles to:
struct nsh_result_double {
    int _is_ok;
    union {
        double _ok;
        const char* _err;
    } _v;
};

// rajje3 tamam(a / b) becomes:
struct nsh_result_double _ret;
_ret._is_ok = 1;
_ret._v._ok = a / b;
return _ret;

// rajje3 ghalat("division by zero!") becomes:
struct nsh_result_double _ret;
_ret._is_ok = 0;
_ret._v._err = "division by zero!";
return _ret;

// khalli result: fasle64 = divide(x, 2.0) wala?
// uses a GNU C statement expression:
double result = ({
    struct nsh_result_double _tmp = divide(x, 2.0);
    if (!_tmp._is_ok) {
        struct nsh_result_double _prop;
        _prop._is_ok = 0;
        _prop._v._err = _tmp._v._err;
        return _prop;
    }
    _tmp._v._ok;
});

Errors as values, propagation as a single operator. No stack unwinding, no hidden control flow. The wala? is just a conditional return that the compiler writes for you. You get Rust-style error handling with C-level transparency.

Defer (ba3dain)

Working

ba3dain means "later." Procrastination as a language feature.

How It Works

Schedule a block to run when the scope exits. Multiple defers execute in LIFO order.

nashmic
dalle greet() {
    itba3("start\n")
    ba3dain {
        itba3("cleanup 1\n")
    }
    ba3dain {
        itba3("cleanup 2\n")
    }
    itba3("middle\n")
    rajje3
}
// Output: start, middle, cleanup 2, cleanup 1

With Return Values

nashmic
dalle compute(x: adad64) -> adad64 {
    ba3dain {
        itba3("  compute done\n")
    }
    rajje3 x * 2
}
Cultural note: "ba3dain" is probably the most-used word in Jordan. "I'll do it ba3dain." Unlike real life, ba3dain in NashmiC actually keeps its promise.

Under the Hood

Defers compile to a goto-chain at the end of the function. Each ba3dain block becomes a labeled section. When the function returns, instead of returning directly, it jumps to the last defer label. Each defer block, after executing, falls through (or jumps) to the previous one in LIFO order. The final defer block then performs the actual return.

C (generated)
// dalle greet() {
//     itba3("start\n")
//     ba3dain { itba3("cleanup 1\n") }
//     ba3dain { itba3("cleanup 2\n") }
//     itba3("middle\n")
//     rajje3
// }
// compiles to:

void greet(void) {
    printf("start\n");
    // ba3dain blocks are registered, not executed here
    printf("middle\n");
    goto _defer_2;  // return jumps to the chain

_defer_2:
    printf("cleanup 2\n");  // LIFO: last defer runs first
_defer_1:
    printf("cleanup 1\n");  // then the earlier one
    return;                 // actual return happens here
}

Every rajje3 in the function gets rewritten to jump to the defer chain instead of returning directly. If the function has a return value, it gets stashed in a local variable before the jump. Simple, predictable, and no runtime overhead -- just gotos.

Pattern Matching

Working

hasab means "depending on." hale means "case."

Matching on Enums

nashmic
ta3dad Shape {
    Da2ira(fasle64),
    Mustateel(fasle64),
    Noqta,
}

dalle describe_shape(s: Shape) {
    hasab s {
        hale Da2ira(radius) => {
            itba3("Circle: radius %g\n", radius)
        }
        hale Mustateel(width) => {
            itba3("Rect: width %g\n", width)
        }
        hale Noqta => {
            itba3("Point\n")
        }
    }
}

Default: 3adi

3adi means "whatever." The most Jordanian response possible. It catches anything not matched.

nashmic
hasab value {
    hale 1 => itba3("one\n")
    hale 2 => itba3("two\n")
    3adi => itba3("something else\n")
}

On Results and Optionals

nashmic
hasab parse_number(input) {
    hale tamam(n) => itba3("got: {n}\n")
    hale ghalat(msg) => itba3("error: {msg}\n")
}

Under the Hood

Pattern matching compiles differently depending on what you're matching. For enums, hasab becomes a switch on the _tag field. For results, it checks _is_ok. For optionals, it checks _has_value. Pattern variables (like radius in hale Da2ira(radius)) are scoped to their arm body -- they don't leak out.

C (generated)
// hasab s { hale Da2ira(radius) => ... } compiles to:
switch (s._tag) {
    case Shape_Da2ira: {
        double radius = s._v.Da2ira;  // destructured, scoped to this block
        printf("Circle: radius %g\n", radius);
        break;
    }
    case Shape_Noqta: {
        printf("Point\n");
        break;
    }
}

// hasab divide(10.0, 0.0) { hale tamam(val) => ... } compiles to:
struct nsh_result_double _match = divide(10.0, 0.0);
if (_match._is_ok) {
    double val = _match._v._ok;
    printf("%g\n", val);
} else {
    const char* msg = _match._v._err;
    printf("Error: %s\n", msg);
}

// hasab find_even(7) { hale fi(val) => ... } compiles to:
struct nsh_option_int64_t _match = find_even(7);
if (_match._has_value) {
    int64_t val = _match._value;
    // fi arm
} else {
    // mafi arm
}

The compiler picks the right strategy based on the type. Enums use switch for jump-table efficiency. Results and optionals use if/else since they only have two variants. Either way, the generated C is exactly what you'd write by hand.

Methods & Impl Blocks

Working

tabbe2 means "apply" or "implement." had means "this thing."

Attaching Methods

nashmic
haikal Point {
    x: fasle64,
    y: fasle64,
}

tabbe2 Point {
    dalle magnitude(had) -> fasle64 {
        rajje3 sqrt(had.x * had.x + had.y * had.y)
    }

    dalle distance(had, other: Point) -> fasle64 {
        khalli dx: fasle64 = had.x - other.x
        khalli dy: fasle64 = had.y - other.y
        rajje3 sqrt(dx * dx + dy * dy)
    }
}

Calling Methods

nashmic
khalli p: Point = Point{ x: 3.0, y: 4.0 }
khalli m: fasle64 = p.magnitude()

The instance before the dot becomes the had parameter. Under the hood, p.distance(q) becomes Point_distance(&p, q) in C.

Under the Hood

Methods are just regular C functions with the struct passed as the first parameter. The had keyword becomes a pointer to the struct (struct Type *had). Dot syntax on method calls is pure syntactic sugar -- the compiler rewrites p.magnitude() to Point_magnitude(&p). No vtables, no dynamic dispatch, no runtime cost.

C (generated)
// tabbe2 Point {
//     dalle magnitude(had) -> fasle64 { ... }
//     dalle distance(had, other: Point) -> fasle64 { ... }
// }
// compiles to:

double Point_magnitude(struct Point *had) {
    return sqrt(had->x * had->x + had->y * had->y);
}

double Point_distance(struct Point *had, struct Point other) {
    double dx = had->x - other.x;
    double dy = had->y - other.y;
    return sqrt(dx * dx + dy * dy);
}

// p.magnitude() becomes:
Point_magnitude(&p);

// p.distance(q) becomes:
Point_distance(&p, q);

The naming convention is TypeName_methodName. Since methods are just functions, they get the same optimization treatment from the C compiler -- inlining, constant folding, the whole deal. The dot syntax is there for readability, not for adding overhead.

Keyword Reference

Every keyword is Franco-Arab. The 3 represents ain, 2 represents hamza.

Control Flow

KeywordPronunciationMeaningEquivalent
izaee-za"if"if
walawa-la"or else"else
wala_izawa-la ee-za"or if"else if
tool_matool ma"as long as"while
lakolla-kol"for each"for
fifee"in" / "there is"in / Some
liffliff"go around"loop
khalaskha-las"enough!"break
kammelkam-mel"keep going"continue
rajje3raj-je3"bring back"return
hasabha-sab"depending on"match
haleha-le"case"match arm
3adiaa-di"whatever"default

Declarations

KeywordPronunciationMeaningEquivalent
khallikhal-li"let there be"let
thabettha-bet"fixed"const
dalledal-le"a function"fn / func
haikalhai-kal"skeleton"struct
ta3dadta-adad"enumeration"enum
tabbe2tab-be2"implement"impl

Types

KeywordPronunciationMeaningEquivalent
adada-dad"number"int32
adad64a-dad 64"number (64-bit)"int64
faslefas-le"decimal"float
fasle64fas-le 64"decimal (64-bit)"double
mante2man-te2"logical"bool
harfharf"letter"char
nassnass"text"string
fadifa-di"empty"void
natijena-tee-je"result"Result<T>
yimkinyim-kin"maybe"Optional<T>
saffsaff"row"Vec<T>

Values

KeywordPronunciationMeaningEquivalent
ahah"yeah"true
lala"no"false
tamamta-mam"all good"Ok
ghalatgha-lat"wrong"Err
fifee"there is"Some
mafima-fi"there isn't"None
hadhad"this thing"self / this

Error Handling

KeywordPronunciationMeaningEquivalent
wala?wa-la?"or?" (propagate)? operator
ba3dainba-a-dain"later"defer
faz3afaz-a"panic"panic!
Cultural notes: khalas is THE Jordanian stop word. yalla is universally recognized across the Arab world. 3adi is the most Jordanian response possible. ba3dain is the national motto of procrastination.

Type Reference

Primitive Types

NashmiCC EquivalentSizeDescription
adadint32_t4 bytes32-bit signed integer
adad64int64_t8 bytes64-bit signed integer
faslefloat4 bytes32-bit float
fasle64double8 bytes64-bit float
mante2bool1 byteBoolean (ah or la)
harfchar1 byteASCII character
nassconst char*pointerUTF-8 string
fadivoid0No value

Generic Types

NashmiCEquivalentDescription
natije<T>Result<T, String>tamam(T) or ghalat(nass)
yimkin<T>Option<T>fi(T) or mafi
saff<T>Vec<T>Dynamic array with .zeed() and .toul()

Naming Origins

NashmiCArabicMeaning
adadعددnumber
fasleفاصلةdecimal point
mante2منطقيlogical
harfحرفletter
nassنصtext
fadiفاضيempty
natijeنتيجةresult
yimkinيمكنmaybe
saffصفrow / line

Built-in Functions

Core I/O

itba3(...) - Print

Prints to stdout. Supports string interpolation and printf-style format specifiers.

nashmic
itba3("marhaba ya {name}!\n")

i2ra() - Read

Reads a line from stdin. Returns a nass.

nashmic
khalli input: nass = i2ra()

itla3(code) - Exit

Exits the program with the given code.

Easter Eggs

FunctionWhat it does
nashmi()Prints the NashmiC manifesto
drobi()Random Jordanian proverb. Roasts you after 5 calls.
mansaf()Mansaf recipe in ASCII art
sahteen()"Bon appetit"
nashmic
yalla() {
    nashmi()    // about NashmiC
    drobi()     // random proverb
    mansaf()    // the recipe
    sahteen()   // bon appetit
}

Standard Library

Three modules ship with NashmiC out of the box. All functions are available globally -- no imports needed.

riyadiyat (Math)

Mathematical functions wrapping C's <math.h>. All take and return fasle64 (double) unless noted.

FunctionDescriptionExample
jadhr(x)Square rootjadhr(16.0) = 4.0
qowa(x, n)Power (x^n)qowa(2.0, 10.0) = 1024.0
mutlaq(x)Absolute valuemutlaq(-7.5) = 7.5
3shwa2i(min, max)Random integer in [min, max]3shwa2i(1, 6)
jeta(x)Sinejeta(NSH_PI / 2.0) = 1.0
jeta_tamam(x)Cosinejeta_tamam(0.0) = 1.0
dal(x)Tangentdal(0.0) = 0.0
ardiye(x)Floorardiye(3.7) = 3.0
sa2fiye(x)Ceilingsa2fiye(3.2) = 4.0
da2reb(x)Roundda2reb(3.5) = 4.0
aqall(a, b)Minimumaqall(3.0, 7.0) = 3.0
akthar(a, b)Maximumakthar(3.0, 7.0) = 7.0
log_tabi3i(x)Natural log (ln)log_tabi3i(NSH_E) = 1.0
log10(x)Log base 10log10(100.0) = 2.0

Constants: NSH_PI (3.14159...) and NSH_E (2.71828...)

nashmic
yalla() {
    itba3("jadhr(16) = %f\n", jadhr(16.0))
    itba3("qowa(2, 10) = %f\n", qowa(2.0, 10.0))
    itba3("3shwa2i(1, 100) = %lld\n", 3shwa2i(1, 100))
}

nusoos (Strings)

String manipulation functions. Functions returning strings allocate new memory.

FunctionDescriptionExample
toul(s)String lengthtoul("marhaba") = 7
yihtawi(s, sub)Contains substringyihtawi("marhaba", "hab") = ah
bdaya(s, prefix)Starts withbdaya("marhaba", "mar") = ah
nihaya(s, suffix)Ends withnihaya("marhaba", "aba") = ah
a3la(s)Uppercasea3la("marhaba") = "MARHABA"
asfal(s)Lowercaseasfal("HELLO") = "hello"
badel(s, old, new)Replace all occurrencesbadel("aa", "a", "b") = "bb"
karrer(s, n)Repeat n timeskarrer("ha", 3) = "hahaha"
juz2(s, start, end)Substring slicejuz2("marhaba", 0, 3) = "mar"
harf_3ind(s, i)Character at indexharf_3ind("abc", 1) = 'b'
qass(s, delim)Split stringqass("a,b,c", ",")
damj(arr, sep)Join arraydamj(parts, "-")
nashmic
yalla() {
    khalli name: nass = "nashmic"
    itba3("toul: %lld\n", toul(name))
    itba3("a3la: %s\n", a3la(name))
    itba3("karrer: %s\n", karrer(name, 3))
}

malafat (Files)

File I/O operations for reading, writing, and managing files.

FunctionDescriptionReturns
i2ra_kol(path)Read entire file to stringnass
uktub_malaf(path, content)Write string to filemante2
dahef_malaf(path, content)Append to filemante2
mawjood(path)Check if file existsmante2
imsah_malaf(path)Delete filemante2
hajm_malaf(path)File size in bytesadad
i2ra_sutoor(path)Read file as array of linessaff<nass>
nashmic
yalla() {
    uktub_malaf("test.txt", "marhaba ya dunya!\n")
    khalli content: nass = i2ra_kol("test.txt")
    itba3("content: %s", content)
    imsah_malaf("test.txt")
}

Compiler Messages

Errors come with Jordanian proverbs. Build successes get celebrated.

Error Format

compiler output
error[E0001]: expected expression
  --> src/main.nsh:12:18
     |
  12 |     khalli x = ???
     |                ^^^
     |
      = patience turns sour to sweet

Proverb Categories

Error TypeProverbMeaning
Type mismatchThe rope of lies is shortYou can't fake types
Invalid argumentThe dancer blames the floorDon't blame the language
Unreachable codeCan't reach the grapes? Call them sourThat code won't run
Stack overflowDig a pit for your brother, fall in itYour recursion caught up
Unused variable"Belgian" (unnecessarily complex)Why declare it?
Unhandled errorYou think you're safe?Handle your errors

Build Success

The compiler celebrates with random quips:

output
done! zay il ful       (everything's perfect)
done! nashmi wallah    (noble work)
done! wallah ma 2assart (you didn't fall short)

The --tarab Flag

Build with mansaf build --tarab program.nsh for celebratory audio on success. "Tarab" means musical ecstasy.

Compilation Pipeline

NashmiC compiles through C in six stages:

pipeline
.nsh source → Lexer → Parser → AST → Sema → C Codegen → cc → native binary

Lexer: Tokenizes your source. Handles UTF-8, Franco-Arab keywords, string interpolation, operators.

Parser: Recursive descent + Pratt parser builds an AST. Handles operator precedence, generics (natije<T>, saff<T>), and block scoping.

Semantic Analysis (Sema): Three-pass analysis — declares symbols, resolves types, then type-checks. Catches undeclared variables, type mismatches, invalid field access, wrong argument counts, and unused variables. Errors include Jordanian proverbs.

C Code Generation: Walks the AST and emits C11. Structs → C structs. Enums → tagged unions. Methods → mangled functions. natije<T> → result structs. wala? → GNU C statement expressions.

C Compilation: Your system's gcc or clang compiles the generated C with -std=gnu11 -O2 and links against nsh_runtime.c.

The entire compiler is ~5,000 lines of C11. No dependencies beyond libc and libm. No VM, no interpreter, no garbage collector — just native binaries.