NSL is a simple embeddable statically typed programming language. It's designed to be easily implemented in any host programming language. Don't expect a general purpose language, it's mostly meant to just call functions in the host environment.
This repository will hosts all official implementations.
- C#
- TypeScript
- Zig (?)
This library can be used using UCPeM
https://github.com/bt7s7k7/NSL
NSLCSharp
end
NSL has a syntax similar to many shell languages like bash. It's designed need the least amount of typing while minimizing implementation difficulty.
NSL is made of statements. Each statement is a function call. Statements are separated by newlines or ;
All statements are followed by arguments. Arguments are separated by whitespace.
print "Hello" "world!"
# Output: Hello world
print "String 1"
print "String 2"; print "String 3"
# Output:
# String 1
# String 2
# String 3
Comments are denoted using #
and continue until the end of a line.
To get a list of all available functions and their signatures call the help
function.
String literals start with "
or `
and end with their starting character. They can contain the following escape sequences:
\n → Newline
\t → Tab
\" → "
\` → `
\\ → \
String literals may be turned into template strings by prefixing them with $
. Template elements are surrounded by $<
, >
.
print $"Hello $[getName]"
The statement in the template element will be executed and its result inserted into the string. So if for example getName
returned Jano, the resulting string will be Hello Jano
.
Number literals are simply numbers.
Boolean literals are just literally true
or false
.
Variables are defined by the var
keyword.
var $x 5
# This defines a variable named $x of type Number and assignes value 5 to it
To change the value of the variable simply call it with the desired value.
$x 10
To get the value of the variable call it.
print $x
Results of other statements can be piped into other statements.
getName | print
The output of the first statements will be set as the first argument of the second statement. If the first statement returns an array, you can call the second statement for every element of that array.
getAllNames |> print
If you want to pipe the output of statement to more than one statement you can use blocks.
findDoor |{
unlock
open
}
You can give a piece of code to a statement to be executed by it. It can give you an argument, accessed with the $v
variable. The result of the action will be given back to the statement implementation.
arr 1 2 3 4 5 | filter $v => {$v > 2} |> print
# Prints all numbers greater than five
First you will have to construct a FunctionRegistry
. To get a registry with the standard library functions:
var functions = FunctionRegistry.GetStandardFunctionRegistry();
To run some code use the Runner
class.
var runner = new Runner(functions);
var (result, diagnostics) = runner.RunScript(script, Path.GetFullPath(file));
/* ↑ File path for debug output */
The IEnumerable<Diagnostic> diagnostics
is a list of all errors during the compilation of your code. If it's empty that means the compilation was successful and the NSLValue result
variable contains the resulting value.
object resultingValue = result.GetValue();
If you want deeper control over the compilation process you can call each compilation layer yourself.
var tokenizationResult = NSLTokenizer.Instance.Tokenize(script, path);
var parsingResult = Parser.Parse(tokenizationResult);
var emittingResult = Emitter.Emit(parsingResult, functions);
var diagnostics = emittingResult.diagnostics;
if (diagnostics.Count() == 0)
{
var result = runner.Run(emittingResult.program);
}
To get a nice example of using this check out the REPLRunner.cs.
There are three ways to implement your own functions. For simple functions your signature can be automatically generated.
registry.Add(NSLFunction.MakeAuto<Func<string, bool>>("isFoo", (a) => a == "foo"));
/* ↑ ↑ ↑ name ↑ implementation
| | result
| arguments
It's important to mention that you are not allowed to use IEnumerable
other than object
. To use something different you have to override the type symbols yourself.
registry.Add(NSLFunction.MakeAuto<Func<IEnumerable<object>>>(
"getNumbers",
() => new object[] { 5, 20, 8, 14 },
new Dictionary<int, TypeSymbol> { { -1, PrimitiveTypes.numberType.ToArray() } }
));
For more complex examples see StandardFunctions.cs.