View on GitHub

NianioLang

Procedural programming language without pointers

Get all dependencies

Debian / Ubuntu

Assumptions

During this tutorial we will use ~/nl2/ directory for NL compiler files and ~/project/ directory for newly created project files.

Download and build NianioLang

cd ~/
git clone https://github.com/nianiolang/nl2.git
cd nl2
make

After executing those commands directory ~/nl2 should contain file mk_cache.exe ‒ main compiler executable.

Prepare NianioLang library files

NianioLang library needs to be initialized before running compiled scripts.

mkdir ~/project
cd ~/project
bash ~/nl2/nl_init_js.sh nl_lib

Expected directory tree after executing commands above:

~/project
└── nl_lib
    └── nl_lib.js

Create basic nianio function

def nianio::nianio(ref state, cmd) {
	var extcmds = [];
	match (cmd) case :inc(var number) {
		state->number += number;
	} case :print {
		extcmds []= :print_text(state->number);
	}
	return extcmds;
}
def nianio::initial_state() {
	return {
		number => 0,
	};
}

Call nianio function from JS code

<html>
	<head>
		<script src="nl_lib/nl_lib.js" type="text/javascript"></script>
		<script src="nl_out/nianio.js" type="text/javascript"></script>
	</head>
	<body>
		<span id='text'></span>
		<br>
		<button onclick='inc_clicked();'>Increment</button>
		<button onclick='print_clicked();'>Print</button>
	</body>
</html>
<script>
	var state = new nl.imm_ref(nl.nianio.initial_state()); // create and initialize nianio state
</script>
<script>
	function inc_clicked() {
		var extcmds = nl.nianio.nianio(state, nl.js_to_imm({name: 'inc', value: 1}));
		handle_extcmds(extcmds);
	}

	function print_clicked() {
		var extcmds = nl.nianio.nianio(state, nl.js_to_imm({name: 'print'}));
		handle_extcmds(extcmds);
	}
</script>
<script>
	function handle_extcmds(extcmds) {
		extcmds = nl.imm_to_js(extcmds);
		for (var i = 0; i < extcmds.length; i++) {
			if (extcmds[i].name == 'print_text') {
				document.getElementById('text').innerHTML = extcmds[i].value;
			}
		}
	}
</script>
<html>
	<head>
		<script src="nl_lib/nl_lib.js" type="text/javascript"></script>
		<script src="nl_out/nianio.js" type="text/javascript"></script>
	</head>
	<body>
		<span id='text'></span>
		<br>
		<button onclick='inc_clicked();'>Increment</button>
		<button onclick='print_clicked();'>Print</button>
		<script>
			var state = new nl.imm_ref(nl.nianio.initial_state()); // create and initialize nianio state
				function inc_clicked() {
				var extcmds = nl.nianio.nianio(state, nl.js_to_imm({name: 'inc', value: 1}));
				handle_extcmds(extcmds);
			}

			function print_clicked() {
				var extcmds = nl.nianio.nianio(state, nl.js_to_imm({name: 'print'}));
				handle_extcmds(extcmds);
			}

			function handle_extcmds(extcmds) {
				extcmds = nl.imm_to_js(extcmds);
				for (var i = 0; i < extcmds.length; i++) {
					if (extcmds[i].name == 'print_text') {
						document.getElementById('text').innerHTML = extcmds[i].value;
					}
				}
			}
		</script>
	</body>
</html>

Complete project directory tree:

~/project
├── index.html
├── nl_lib
│   └── nl_lib.js
├── nl_out
│   └── nianio.js
└── nl_sources
    └── nianio.nl

Download complete project

Nianio function development

Type checking

After adding types to NL code, compiler will statically check types of all function calls issued from NianioLang code. Checking argument types for functions called from JS is possible only in runtime. To do that, add ptd::ensure call at the beginning of each function called from JS:

def nianio::nianio(ref state : @nianio::state, cmd : @nianio::cmd) : @nianio::ext_cmds {
	ptd::ensure(@nianio::state, state);
	ptd::ensure(@nianio::cmd, cmd);
	var ext_cmds = [];
	...
	return ext_cmds;
}

Own types

Own types are types parallel to ptd with usage restricted to passing by ref (no copying, returning or passing by value is allowed). Thanks to that compiler is able to generate efficient target code. Main use case of own types is nianio state. To make state own, first define state type and use it in nianio function declaration

use ptd;
use own;

def nianio::state() {
	return own::rec({
		number => ptd::int(),
	});
}

def nianio::nianio(ref state : @nianio::state, cmd) {
	...
}

Because own types cannot be returned from function, initializing function has to take state as a ref argument and fill it with initial values

def nianio::init_state(ref state : @nianio::state) {
    state = {
        number => 0,
    };
}

Finally, in JS create dummy state variable and pass it to init function as ref

<script>
	var state = new nl.imm_ref(nl.js_to_imm({}));
	nl.nianio.init_state(state);
</script>

Debugging

To enable debugging, add flag --debug to NL compilation command. This allows to show NianioLang code in browser console, set brekpoints on it and peek variables values by moving mouse over identifier (the latter only in Chrome). Warning: with this option on, NianioLang sources will be available in static files directory. Do not use in production.