A server/client javascript-based interpreter for simple custom application specific scripts.
npm install @creation-wasteland/nanoscripteval. nscript ensures safe execution of code by limiting access to only explicitly included features through developer-defined modules.
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.
break x;.
hello ${world}.
npm install @creation-wasteland/nanoscript`
Usage
Import NSEngine from the module
`typescript
// 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
// 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
// typescript
const engine = new NSEngine();
engine.addModules([
myModule,
...
])
`
To compile and execute nanoscript code, use the `compileAndRun` method
`typescript
// typescript
const output = engine.compileAndRun(code);
`
Nanoscript Syntax Overview
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.
$3
Variable declarations are similar to javascript. Let and const both have block scoping (similar to how it would work in C)
`javascript
// 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
$3
`javascript
// 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.
`javascript
// 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
}
}
}
`
$3
`javascript
// 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.
$3
You can use ", ', or ` `` to denote string literals
`javascript
// nanoscript
let s1 = "Hello";
let s2 = 'Hello';
let s3 = Hello;
`
$3
There is support for string templating using the ` `` character.
Template elements must be surrounded by `${}`
`javascript
// nanoscript
let s1 = "World";
let s2 = Hello ${s1}; // Hello World
`
$3
Functions can be defined using the `function` keyword. Functions can then be called by name while in scope.
`javascript
// 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.
$3
Lists can be declared with list literal syntax, similar to javascript
`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.
$3
Sets can be declared with list literal syntax, similar to javascript
`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.
$3
Objects can be created with a familiar syntax as well.
Note: Object keys must be enclosed with single or double quotes like strings.
`javascript
// 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.
Examples
`typescript
// 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);
`
`javascript
// 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.
Future Features
- WASM Stack Machine
- Currently the stack machine that executes the code is running in javascript (which is slow af). A WASM based approach is being built to improve performance by 100x
- Custom classes
- Adding the ability to define classes, like one would in C++
- Explicit typing system
- Explicit typing when desired, similar to C `int x = 0; float y = 0.0;`
- First class function objects and Closures
- Adding a little more javascript spice to the language with closures, arrow functions, and using functions as first class objects (by design)
- TypedArrayBuffer Syntatic Sugar
- I want to add fancy syntactic sugar for js typed arrays. Something like `int[30]` in nscript being equal to `new Int32Array(30)`` under the hood (or even being on the stack in the WASM stack machine)