nanoscript (nscript) is a lightweight, JavaScript-inspired programming language designed for simple computational tasks in the browser or on the server. Built with TypeScript, it draws inspiration from an earlier C++-based project, clx, and is tailored to address security concerns that arise with JavaScript's eval
. nscript ensures safe execution of code by limiting access to only explicitly included features through developer-defined modules.
nanoscript operates on a lightweight engine that runs seamlessly in both browser and server environments. Built with TypeScript, nscript code can be executed anywhere JavaScript is supported. While the engine executes code using JavaScript, nanoscript is inherently slower than running equivalent algorithms directly in JavaScript. However, its focus is not on speed but on providing a secure and flexible environment. A typical use case for nanoscript is enabling power-users to write custom logic or advanced configurations to interact with an application safely. Executing external code on a server using eval
or similar methods can expose the application to significant security risks, as eval
enables the execution of fully-featured JavaScript, potentially allowing malicious actions.
Javascript functions and objects can be passed into the engine and called by name to allow integration with your application code. Nanoscript is a convenient way to write code that glues together your more performant application code
break x;
.hello ${world}
.Install using NPM
npm install @creation-wasteland/nanoscript
Import NSEngine from the module
// typescript
import { NSEngine, NenvExport, NenvModule } from "@creation-wasteland/nanoscript";
Add any required modules so that your API can be called from the engine.
To create a nenv module, define a new nenv.NenvModule with a name
and exports
The exports
property should contain a list of NenvExports. Each export needs to have a name
, type
, and object
.
// typescript
const myModule = {
name: "myModule",
exports: [
{ name: "myObject", type: "constant", object: {x: 1, y: 2, z: 3} },
{ name: "myFunction", type: "function", object: (a: number, b: number) => a + b },
...
] as NenvExport[]
} as NenvModule;
To add your module to the engine, use the addModules
method
// typescript
const engine = new NSEngine();
engine.addModules([
myModule,
...
])
To compile and execute nanoscript code, use the compileAndRun
method
// typescript
const output = engine.compileAndRun(code);
Nanoscript is a language that is similar to the most basic elements of javascript/c with some slight syntax modifications. If you are used to javascript, you should instantly be able to use nanoscript.
Variable declarations are similar to javascript. Let and const both have block scoping (similar to how it would work in C)
// nanoscript
let x = 0;
const y = 'hello';
In the future, a strict typing system will be added so that variables can be declared with their datatype
// nanoscript
for (let i = 0; i < 100; i++) {
...
}
let c = 10;
while (c >= 0) {
...
}
One notable "new" feature, is the ability to break directly to different levels.
// nanoscript
for (let i = 0; i < 100; i++) {
for (let j = 0; j < 100; j++) {
if (i + j == 50) {
break 2; // break out of the enclosing loop
}
}
}
// nanoscript
const arr = [0,1,2,3,4,5,6];
for (x in arr) {
...
}
// Same thing
for (let x in arr) {
...
}
// You can also use the ∈ symbol if you want to be extra fancy
for (x ∈ arr) {
...
}
You can precede the variable name with const or let if you want, it does not make a difference.
You can use "
, '
, or `
to denote string literals
// nanoscript
let s1 = "Hello";
let s2 = 'Hello';
let s3 = `Hello`;
There is support for string templating using the `
character.
Template elements must be surrounded by ${}
// nanoscript
let s1 = "World";
let s2 = `Hello ${s1}`; // Hello World
Functions can be defined using the function
keyword. Functions can then be called by name while in scope.
// nanoscript
function func(a, b) {
return a + b;
}
func(1,2); // 3
Currently, functions are not designed to easily be used as first class objects. Still working on this.
Lists can be declared with list literal syntax, similar to javascript
// nanoscript
let arr = [0,1,2,3,4,5]; // js list with 6 elements
let arr2 = []; // empty list
Note: List literals that can be fully determined at compile-time, will be pre-compiled and result in a massive performance increase.
Sets can be declared with list literal syntax, similar to javascript
// nanoscript
let set1 = {0,1,2,3,4,5}; // a set with 6 elements
let notASet = {}; // NOT an empty set, this is an empty object
Note: Set literals that can be fully determined at compile-time, will be pre-compiled and result in a massive performance increase.
Objects can be created with a familiar syntax as well.
Note: Object keys must be enclosed with single or double quotes like strings.
// nanoscript
let empty = {};
let o = {
'x': 1.0,
'y': 2.5
};
let dog = {
'name': 'Dogmeat',
'level': 42
};
Note: Object literals that can be fully determined at compile-time, will be pre-compiled and result in a massive performance increase.
// typescript
const code = ... // Your nanoscript code (below)
const myModule = {
name: "myModule",
exports: [
{ name: "addNumbers", type: "function", object: (a: number, b: number) => a + b },
...
] as nenv.NenvExport[]
} as nenv.NenvModule;
const engine = new NSEngine();
engine.addModules([
myModule,
...
]);
const output = engine.compileAndRun(code);
// nanoscript
let a = 100;
// use the imported function addNumbers from the module
console.log(addNumbers(a, 10)); // prints 110 to the console
The console object and addNumbers functions are pulled in from the nenv and can be called by name inside of the nanoscript script.
int x = 0; float y = 0.0;
int[30]
in nscript being equal to new Int32Array(30)
under the hood (or even being on the stack in the WASM stack machine)This project is licensed under the MIT License. See the LICENSE file for details.