ngg is mature enough for several use cases, but this document has a long way to go.
[Visit nandakumar.co.in]
Page generated on 2026-09-02

The nguigen Programming Language

nguigen, ngg for short, is a general purpose programming language for developing software that is (reasonably) portable, performant, and safe. It compiles to multiple high-level languages including C and JavaScript. ngg itself is written in ngg, which is then built by compiling to C first.

ngg is still evolving, making it hard to describe its exact nature and scope. It originated as a simple script to convert the same GUI spec to C/GTK, C++/Qt, and PyGI code. But this spec-based approach was too limiting and cumbersome, always needing the support of a real programming language. Combined with my taste for low-level programming and frustration with the error-prone nature of C, ngg started becoming a real programming language on its own. At this point, it appears to be the following:

ngg has the following features:

One day this document might become a book, a reference manual, or even a formal spec, but as of now, it is just a collection of random notes, slightly organized whenever possible. I can assure you is that the language and the compiler are more mature than this document.

Rust vs ngg

One thing that makes Rust attractive is that it gets rid of memory management pitfalls without employing a garbage collector. ngg is also being developed with this goal in mind. Other than that, Rust and ngg are totally different languages. For starters, Rust has object composition while ngg has traditional inheritance-based OOP. Another difference is that, ngg is arguably easier to write, and it maintains this easiness in both systems and application levels.

Who Is This Document For?

This document is strictly meant to be a quick reference for those who already know other programming languages, especially C, Java, and the like. It will elaborate on ngg features that differ from or improve on these languages, employing comparisons even within definitions. ngg is by all means okay as one’s first programming language, but this document is not the best starting point then.

[ Go to ToC ]

Syntax

ngg has a syntax that is strange but easy to pick up and use. We will explore this syntax in this chapter, but I want you to remember that ngg is not an alternative syntax to some existing language – it would still be a new language even if you fit the ngg compiler with a new front-end that accepts syntax similar to C or Java.

[ Go to ToC ]

Type System

ngg is strongly and statically typed. The compiler employs type inference to make programming easier (to be clear, this is Go-style type inference, not the one you would encounter in functional languages).

Basic Types and Strings

Basic types like int and double are present in ngg which translate to C directly without any surprises. long and unsigned become single-character prefixes – for example, long long is written as llong and unsigned int is written as uint. Fixed-size integers are named i8, u8, i16, u16, etc.

ngg has string for immutable strings (string constants) and mstring for mutable strings. When the target language is C, they become const char * and char * respectively. Yes, this approach of representing strings as NUL-terminated character arrays is not particularly safe, but it is a requirement to interface with existing C libraries. A safer abstraction can be expected in future for high-level usage.

Structs vs Classes

Structs and classes are both custom-defined containers with any number of fields. However, they have some fundamental differences.

Flenum

It is a common idiom in systems programming to use an integer to hold a set of flags, where each bit represents a particular flag. To set and read these flags, C programmers define constants with powers of two as values and employ error-prone bit operations. ngg offers a feature called “flenum” (short for “flag enum”) to automate this entire business.

The compiler automatically converts flenum accesses to bitwise operations. If a flenum Flags has an element FLAG1 and flags is of type Flags, accessing FLAG1\flags will result in some bitwise operation(s) between flags and FLAG1\Flags. For example, you can assign true or false to FLAG1\flags, and the compiler will take care of generating the bitwise operations. This makes dealing with binary files and interfaces far more easier than in C.

// Nandakumar Edamana
// 2026-01-31

// @test-stdout 0-5-0-1-0-1-0-

flenum Flags
    F1,
    F2,
    F3,
;

fun $main
    var flags Flags
    =printf/["%d-", flags]

    ==F1\flags true
    ==F3\flags true
    =printf/["%d-", flags]

    =printf/["%d-", F2\flags]
    =printf/["%d-", F3\flags]

    ==F3\flags false
    =printf/["%d-", F3\flags]

    var bv1 / true
    ==F3\flags bv1
    =printf/["%d-", F3\flags]

    var bv2 / false
    ==F1\flags bv2
    =printf/["%d-", F1\flags]

    =printf/["\n"]

    return 0
;

[ Go to ToC ]

Memory Management

ngg is meant to be memory safe even when compiled to low-level targets like C. It tries to achieve this without having a garbage collector. Every dynamic object is automatically deleted when its owner (a scope like a function or another object) gets deleted. If the object is meant to outlive a scope, its ownership can be transferred, or reference counting can be used. While these features are already there and used heavily in projects like Vara, there are some gaps, and the compiler still lets one perform manual memory management. That wouldn’t be the case in future.

TODO Dynamic objects without any ownership (var x new CLASS instead of local x new CLASS) can be referred to by any other object anywhere, as they are never freed. The type checker should prevent such references being copied to pointers with ownership (to prevent use after free). This enforcement is pending.

Immortal Classes

If a class is marked immortal, its objects are never destroyed so that they can be safely referred to from anywhere in the code without worrying about issues like use-after-free. This simplification in ownership tracking comes at the cost of leaking memory. However, it should be okay for one-shot programs, as the entire memory is reclaimed by the OS when the program terminates. Immortal classes should be okay in daemons (servers) as well, as long as only a known number of instances are created (e.g.: an Application object or a couple of workers which need to stay until the server is killed anyway).

Subclasses of immortal classes should also be marked immortal.

Unique Ownership

As the name suggests, an object under unique ownership has a single definite owner, which can be another object or a scope (like a function or a branch). The owned object gets deleted during the deletion/exit of the owner. Such ownership can be specified using the keyword local in local variable declarations and the keyword own in function parameter and class attribute declarations.

TODO examples

Unique ownership can be transferred using the keyword steal. For example, if a function has a local object defined using local obj1 new MyClass, and it needs to be passed to a function fun myfun takes obj own MyClass (which takes over the ownership), the call is to be made like =myfun/[steal obj1].

Strong and Weak Reference Counting

TODO

// Started 2026-06-27
// @test-clopt --x-rc-for-all-classes
// @test-stdout 13-13-13-13-
// TODO checks for the generated code

class Value takes n int;

class MyClass1 takes value ownrc Value
    fun print =printf/['%d-', n\@value];;
;

fun process takes x ownrc Value
    =printf/['%d-', n\@x]
;

fun $main
    ownrc value newrc Value/[13]
    own obj1 new MyClass1/[value]
    own obj2 new MyClass1/[value]

    =print\obj1
    =print\obj2
    =process/[value]

    =printf/['%d-', n\@value\obj1]
    =puts/['']
;
// Started 2026-06-28
// @test-clopt --x-rc-for-all-classes

// TODO FIXME `weakrc nullable` doesn't make sense; can I do `nullable weakrc`?
class Node takes parent weakrc nullable Node, child ownrc nullable Node;

fun $main
    ownrc n0 newrc Node/[nil, nil]
    ownrc n1 newrc Node/[n0, nil]
    ownrc n2 newrc Node/[n1, nil]

    ==child\@n0 n1
    ==child\@n1 n2
;

[ Go to ToC ]

Object-Oriented Programming

TODO

// A linked list example that resembles how it is done in
// functional languages. If not for illustrating the use of
// `switch finally`, only one kind of class is needed to represent
// a node (end of the list will be marked by nil pointer).
//
// 2025-09-24

nonfinal class list
    staticrtti;
;

class Nil extends list;
class cons extends list takes h int, t own list;

fun length gives int takes xs list
    switch finally xs
    case [Nil]
        return 0;;
    case as cons consobj
        return sum 1 =length/[t\consobj];;
;

fun $main
    local xs / new cons/[4, new cons/[5, new cons/[6, new Nil]]]

    =printf/['%d-\n', =length/[xs]]
;

Miscellaneous Notes

[ Go to ToC ]

GUI Programming

TODO

// Nandakumar Edamana

// @test-guibuild
// @test-norun

include gui

class myapp
    function $construct
        wid wnd mainwindow given [ title "My Application" ]
            wid vbox1 vbox
                classwide
                wid txt1 entry;

                wid btn button given
                    [ label "Click me!", onclick onbtnclicked ]
                ;

                classwide
                wid lbl1 label;
            ;
        ;
    ;

    lis onbtnclicked for click of button
        local n1str / =ngg-entry-clone-text-utf8/[txt1]
        local msg / f'Hello, {n1str}'
        =set-label\lbl1\this/[msg]
    ;
;

fun $main
    =gui-init
    new myapp
    =gui-run
;

Miscellaneous Notes

[ Go to ToC ]

Reactive Programming

Reactive programming is a handy paradigm in which changes to certain variables automatically get propagated to parts that depend on those variables. Consider programming a Web app that lets one download some resources. When a download starts, a throbber, a progress bar, and a Cancel button need to become visible. Their status needs to be kept up-to-date as the download progresses. If done in the traditional way, the handler of the “download-progress” event (a cooked-up name) needs to know about all those parts where the status has to be reflected. In reactive programming, on the other hand, one declares the relationship that each component has with a variable the stores the status, and all the “download-progress” handler does is updating this variable.

// Nandakumar Edamana
// 2024-01-05

// @test-guibuild
// @test-oc-fgrep myapp_count_Observable_set(((myapp *) user_data)->count, ((myapp *) user_data)->count->value + 1);
// @test-norun

include gui;
include reactive;

const MAXCLICK / 4

class myapp
    var $count int

    function $construct
        wid wnd mainwindow given [ title "Reactive Application" ]
            wid vbox1 vbox
                classwide wid lblA label given [ label xif (ge $count MAXCLICK) then 'Logged in as User 1' else 'Not logged in' ];
                classwide wid btnA button given [ label 'Log Out', visible (ge $count MAXCLICK), onclick onlogout ];
                classwide wid btn button given [ label "Click me!", onclick onbtnclicked, visible (lt $count MAXCLICK) ];
                classwide wid lbl1 label given [ label (dats acat ['Clicked ', $count, ' time(s)']) ];
                classwide wid txt1 entry given [ text (dats acat ['Clicked ', $count, ' time(s)']) ];
                classwide wid prg  progressbar given [ fraction (as double div $count as double MAXCLICK) ];
            ;
        ;
    ;

    lis onbtnclicked for click of button
        ==$count sum $count 1
    ;

    lis onlogout for click of button
        ==$count 0
    ;
;

function $main gives int
    call gui-init;
    new myapp;
    call gui-run;

    return 0;
;

Miscellaneous Notes

[ Go to ToC ]

Metaprogramming

TODO

The purpose of cif, celif, and celse is to enable conditional compilation. The kinds of expressions that can be used with cif and celif are extremely limited, but should serve most purposes. The supported ones are boolean literals, integer literals, and, or, not, streq, compiletime variables, and compiletime function calls. Boolean and integer literals are supported to facilitate quickly disabling portions of code with cif false or cif 0 (like #if false in C). streq _ngg_targ TARGET can be used to conditionally compile a block of code for TARGET exclusively. =_ngg_defined/[ID] will return true if ID is defined during compiletime.

Careless use of _ngg_enum_to_array_of_string can easily clutter the generated code. Hence the compiler will warn if it is applied on the same enum more than once. The compiler will also prevent its use in local contexts so that it won’t miss any duplicate application inside a local context in an included file. The recommended way to use _ngg_enum_to_array_of_string is to apply it on an enum only once, store the returned array in a global variable, and write some wrapper functions to make use of it, which can be called from inside any file in your project.

[ Go to ToC ]

Miscellaneous Notes

This chapter dumps a collection of notes that have not been placed in any specific chapter.

The generated ngd file will contain the following in the case of C output:

  1. A .c target with .ngg prerequisites (or .ngh in case --ngd-use-ngh is set, except for the first one)
  2. All non-ngg dependencies such as files embedded using compiletime evaluation (part of the above rule)
  3. A .o target with all the .ngg files from (1) listed as .h prerequisites

Regarding the .o target, yes, the recommended way to generate Makefile rules for .o files is to use gcc -MMD. Only the C compiler can know all the dependencies (ngg will miss files included using verbatim statements, for example). However, the ngg compiler still has to generate a .o rule because the .c and .h files that it depends on won’t be present during the first run of make.


autoshallow is not allowed for classes with non-nullable own fields. For nullable own fields, they will be set to null.


autoshallow defines a function _ngg_shallowcopy for a class and all its descendents that returns a shallow copy of an object. As an example for its use, the deepclone method of NggExpression (and its descendents) in the code of the compiler itself calls _ngg_shallowcopy before replacing certain fields with deep clones.


classwide is recursively applied.


[ Go to ToC ]

Building ngg Projects

TODO


[ Go to ToC ]

Using Foreign Libraries

The keyword shadow can be used to declare prototypes of foreign types, variables, and functions to be used in an ngg program. One key difference from extern is that this declaration doesn’t generate any code, even in the output .h files. A shadow declaration is only for the ngg compiler to know about foreign resources. This is exactly what you want when you have foreign libraries that already provide .h files.

Let us consider an example. When you call puts in your ngg code, the ngg compiler knows its type from the shadow declaration shadow puts gives int takes s string present in stdio.ngg, provided as part of the standard ngg compile-time library. When you compile the C code of your program generated by the ngg compiler, the C compiler knows about puts from the declaration already present in stdio.h.

The ngg compiler prints #include <stdio.h> in all generated code by default. When you are writing bindings for other libraries (say libjson-c), you will have to add appropriate #include lines on your own (by putting vh #include <HEADER.h> in your ngg code).


Table of Contents