https://github.com/gravatalonga/ninja
Ninja Programming Language - General Scripting Language inspired in PHP and Javascript.
https://github.com/gravatalonga/ninja
golang pratt-parser programming-language
Last synced: 6 months ago
JSON representation
Ninja Programming Language - General Scripting Language inspired in PHP and Javascript.
- Host: GitHub
- URL: https://github.com/gravatalonga/ninja
- Owner: gravataLonga
- License: mit
- Created: 2022-06-16T09:19:53.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2023-08-11T20:07:57.000Z (over 2 years ago)
- Last Synced: 2025-03-23T22:12:18.407Z (about 1 year ago)
- Topics: golang, pratt-parser, programming-language
- Language: Go
- Homepage: https://ninja.jonathan.pt
- Size: 52.8 MB
- Stars: 3
- Watchers: 1
- Forks: 1
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README

[](https://github.com/gravataLonga/ninja/actions/workflows/main.yml)
# Install
## Homebrew
```sh
brew tap gravatalonga/ninja-lang
brew install ninja-lang
```
## YUM / RPM
To enable, add the following file `/etc/yum.repos.d/ninja.repo`:
```sh
[ninja]
name=Ninja Programming Language
baseurl=https://yum.fury.io/gravatalonga/
enabled=1
gpgcheck=0
```
Check if correctly created
```
yum --disablerepo=* --enablerepo=ninja list available
```
To install you only need run following command:
```
yum install ninja-lang
```
## APT
To configure apt access, create a following file `/etc/apt/sources.list.d/ninja.list` with content of :
```
deb [trusted=yes] https://apt.fury.io/gravatalonga/ /
```
Or use this one line command:
```
echo "deb [trusted=yes] https://apt.fury.io/gravatalonga/ /" > /etc/apt/sources.list.d/ninja.list
```
and them you can install
```
sudo apt install ninja-lang
```
## Manual Download
Download from [github](https://github.com/gravataLonga/ninja/releases)
## Manual Installation
```sh
git clone https://github.com/gravataLonga/ninja
cd ninja
go build -o ninja-lang
```
# Documentation
For more detail about language, you can check [here](https://ninja.jonathan.pt) (Still working in progress).
# Demo
Resolved [katas](https://github.com/gravataLonga/ninja-lang-katas) from this [website](https://adventofcode.com/2015)
# Syntax
## Variable
`var = ;`
Examples
```
var a = 1;
var a1 = "Name";
var b = 2.0;
var c = a + 1;
var d = a + b;
var e = ++a;
var f = function () {};
var g = [1, 2, 3, "hello", function() {}];
var h = {"me":"Jonathan Fontes","age":1,"likes":["php","golang","ninja"]}
var i = a < b;
var j = true;
var k = !j;
var l = if (a) { "yes" } else { "no" };
g[0] = 10;
g[4] = "new value"; // it will append to array.
h["other"] = true;
"ola"[0] // print o
```
It's possible to reassign variable for example:
```
var a = 1;
a = a + 1;
puts(a);
```
## Data Types Availables
```
/**
* Booleans
*/
true;
false;
/**
* Integer
*/
1;
20000;
/**
* Floats
*/
100.20;
5.20;
/**
* Scientific Notation
*/
1e3;
2e-3;
/**
* Hexadecimal Number
*/
0x00F
0xf
0x10C
/**
* Strings
*/
"hello"
"\u006E\u0069\u006E\u006A\u0061" // ninja in unicode chars
"\n\r\t\b\f" // special caracters is also supported
/**
* Array
*/
[1, "a", true, function() {}]
/**
* Hash
*/
{"key":"value","arr":[],"other":{}}
/**
* Functions
*/
function () {}()
var a = function () {}; a();
function a() { }; a();
function (a) { return function() { return a; }}(10)();
function (a, b = 1) { return a + b; };
```
## Comments
`// <...>` or `/* <...> */`
Comments can start with double slash `//` ou multiple lines with `\* *\`
## Functions
`var = function (?) { }`
`function (?) { }`
Functions is where power of language reside, it's a first-citizen function, which mean it can accept function as arguments
or returning function. We got two ways declaring functions, literal or block.
```
function say(name) {
puts("Hello: " + name);
}
```
Or
```
var say = function(name) {
puts("Hello: " + name);
}
```
They are exactly same, but this is illegal:
```
var say = function say(name) {
puts("Hello: " + name);
}
```
We also could declare default values for parameters
```
function add (a, b = 20) {
return a + b;
}
add(10);
add(10, 30);
```
### Builtin Functions
There are several builtin functions that you can use:
1. **puts** - print at console
2. **len** - get length of object
3. **first** - get first item of array
4. **last** - get last item of array
5. **rest** - get items after first one
6. **push** - add item to array
7. **exit** - exit program
8. **args** - get arguments passed to ninja programs
9. **rand** - get random number from 0 to 1 float point
10. **time** - return Unix time, the number of seconds elapsed
```
var a = [1, 2, 3, 4];
puts(len(a)); // print 4
puts(len("Hello!")); // print 5
```
```
var a = [1, 2, 3, 4];
puts(first(a)); // print 1
```
```
puts("Hello World"); // print in screen
```
```
var a = [1, 2, 3, 4];
puts(last(a)); // print 4
```
```
var a = [1, 2, 3, 4];
puts(rest(a)); // print [2, 3, 4]; (all but not first)
```
```
var a = [1, 2, 3, 4];
puts(push(a, 5)); // print [1, 2, 3, 4, 5];
```
## Import
You can import another ninja files, it will act like `require file.php` in php language.
```
import "testing.ninja";
var lib = import "mylib.ninja"; // return function() {};
```
## Operators && Logics Operators
` `
Logic's Operators
```
10 < 10; // FALSE
10 > 10; // FALSE
10 == 10; // TRUE
10 != 10; // FALSE
10 <= 10; // TRUE
10 >= 10; // TRUE
10 && 10; // TRUE
10 || 10; // TRUE
!10; // FALSE
!!10; // TRUE
```
> **Note**: a value is considered truthy when a value is not nil or not false.
`? `
Arithmetics Operators
```
1 + 1; // SUM
1 - 1; // SUBTRACT
1 / 1; // DIVIDER
1 * 1; // MULTIPLE
4 % 2; // MOD
10 ** 0; // POW
10 & 2; // AND Bitwise operator
10 | 2; // OR Bitwise operator
10 ^ 2; // XOR Bitwise operator
10 << 2; // Shift left (multiply each step)
10 >> 2; // Shift right (divide each step)
++1; // First increment and then return incremented value
--1; // First decrement and then return decremented value
1++; // First return value and then increment value
1--; // First return value and then decrement value
```
## Data Structures
### Array
`var = [...]`
```
var a = [1 + 1, 2, 4, function() {}, ["a", "b"]];
```
#### Delete index
```
delete a[0];
```
It will keep the order
#### Add Key
```
a[5] = "hello";
push(a, "anotherKey");
// push by empty braces
a[] = 6;
```
### Hash
`var = {:,....}`
```
var a = {"key":"hello","key" + "key":"hello2", "other":["nice", "other"], 2: true};
```
#### Delete Key
```
delete a["key"];
```
#### Add Key
```
a["testing"] = "hello";
```
### Enum
```
enum STATUS {
case OK: true;
case NOK: false;
}
enum RESPONSE {
case OK: 200;
case NOT_FOUND: 404;
case ERR_MSG: "There are some errors"
case TIME: 32.2 + 43.3;
case TEST: if (true) { 0 } else { 1 };
}
```
then you can use his values:
```
puts(STATUS::OK);
```
## Conditions
### If / ElseIf / Else
`if () { } elseif () { } else { }`
#### Simple If Condition
```
if (true) {
puts("Hello");
}
```
#### If / Else
```
if (true) {
puts("Hello");
} else {
puts("Nope");
}
```
#### If / ElseIf / Else
```
if (true) {
puts("Hello");
} elseif (1 > 2) {
puts("1 is greater than 2");
} else {
puts("Nope");
}
```
We can omit else condition
```
if (true) {
puts("Hello");
} elseif (1 > 2) {
puts("1 is greater than 2");
}
if (true) {
puts("Hello");
}
```
### Ternary
` ? : `
```
true ? 10 : 20
```
### Elvis Operator
` ?: `
```
var a = "hello";
var b = a ?: "world";
```
## Loop
`for (?;?;?) { }`
> **Note** initial, condition and iteration is optional, you can omit
```
var i = 0;
for(;i<=3;++i) {
puts(i);
}
var a = [1, 2, 3];
for(var i = 0; i <= len(a)-1; ++i) {
puts(a[i]);
}
for(var i = 0; i <= len(a)-1; i = i + 1) {
puts(a[i]);
}
for(;;) {
break;
}
```
# Object Call
We support object call in any of data type.
## String
Here a full of list of support object call for string:
```
"hello".type(); // "STRING"
"hello".length(); // 5
"a,b,c".split(","); // ["a", "b", "c"];
"hello world".replace("world", "ninja"); // "hello ninja"
"hello world".contain("hello"); // TRUE
"hello world".index("Hello"); // 0
"hello world".upper(); // "HELLO WORLD"
"HELLO WORLD".lower(); // "hello world"
" hello world ".trim(); // "hello world"
"1".int(); // 1
"1.1".float(); // 1.1
```
## Integer
```
1.type(); // "INTEGER"
1.string(); // "1"
1.float(); // 1.0
var a = -1; a.abs(); // 1.0
```
## Float
```
1.0.type(); // "FLOAT"
1.0.string(); // "1.0"
var a = -1.0; a.abs(); // 1.0
1.5.round(); // 2.0
```
## Boolean
```
true.type(); // "BOOLEAN"
```
## Array
```
[1, 2, 3].type(); // "ARRAY"
[1, 2, 3].length(); // 3
[1, 2, 3].joint(","); // "1,2,3"
[1, 2, 3].push(4); // return null, but underlie value of array was change to [1, 2, 3, 4]
[1, 2, 3].pop(); // return 3 and underlie value of array was change to [1, 2]
[1, 2, 3].shift(); // return 1 and underlie value of array was change to [2, 3]
[1, 2, 3].slice(1); // copy array with following elements [2, 3]
[1, 2, 3].slice(1, 1); // copy array with following elements [2]
```
## Hash
```
{"a":1,"b":2}.type(); // "HASH"
{"a":1,"b":2}.keys(); // ["a", "b"];
{"a":1,"b":2}.values(); // [1, 2];
{"a":1,"b":2}.merge({"c":3}); // {"a":1,"b":2,"c":3}
{"a":1,"b":2}.has("a"); // true
```
> **Note:** Order of keys isn't preserved.
## Keywords
```
var true false function return if
else for import delete break enum case
```
## Extending Ninja Programming Language
You can extending ninja by creating a custom plugins, create an file for example:
```
package main
import . "github.com/gravataLonga/ninja/object"
func Hello(args ...Object) Object {
return &String{Value: "Hello World!"}
}
func main() {
panic("this is a plugin")
}
```
Then, compile as plugin:
```
go build -buildmode=plugin -o hello.so hello.go
```
After this you can import and called it in your ninja programs, like so:
```
var plugin = plugin("hello"); // don't need ".so" extension
puts(plugin.hello()); // it will print "Hello World!"
```
## Lexical Scooping
[Inspiration](https://craftinginterpreters.com/resolving-and-binding.html)
> To “resolve” a variable usage, we only need to calculate how many “hops” away the declared variable will be in the
> environment chain. The interesting question is when to do this calculation—or, put differently, where in our
> interpreter’s implementation do we stuff the code for it?
## A variable resolution pass
> After the parser produces the syntax tree, but before the interpreter starts executing it, we’ll do a single walk over
> the tree to resolve all of the variables it contains. Additional passes between parsing and execution are common.
> If had static types, we could slide a type checker in there. Optimizations are often implemented in separate
> passes like this too. Basically, any work that doesn't rely on state that’s only available at runtime can be done
> in this way.
When binding a variable we need to know how depth we are in rabbit hole.
## Examples
1. Check tests
2. Check resolved [katas](https://github.com/gravataLonga/ninja-lang-katas)
## Tests
```
go test -v -race ./...
```
## Profiling && Performance
### Where CPU resources spend more time
Create svg graph:
[interpreter result](https://github.com/google/pprof/blob/main/doc/README.md#interpreting-the-callgraph)
```
go test -bench=. -cpuprofile cpu.prof
go tool pprof -svg cpu.prof > cpu.svg
```
### Cores
```
go test -bench=. -trace trace.out
go tool trace trace.out
```
### Test Race Condition
```
go test -race
```
# References
- Structure and Interpretation of Computer Programs, Second Edition
- Engineering a Compiler, Second Edi- tion. Morgan Kaufmann.
- Parsing Techniques. A Practical Guide.. Ellis Horwood Limited.
- Modern Compiler Design, Second Edition. Springer
- The Elements Of Computing Systems. MIT Press.