Forest(PPL)
Write it like C, run it like LISP. Every program is a tree you can display, save, rebuild and change while it runs.
var begin = 0;
var end = 3;
for(i, begin + 1, end + 1, 1)
{
write(i);
}
var (begin[0]);
var (end[3]);
loop (i)( + ( begin )( 1 ) )( + ( end )( 1 ) )( 1 )
(
do
(
(write(i))
)
);
1
2
3
Infix .scr code is translated into parenthesized .ppl, then a syntax tree is built and traversed. Run showcode yes to see this translation for any of your own files.
From download to your first program
Download and unpack
Download
ForestPPL_SFX.exefrom the repository and run it; it is a self-extracting archive. You getForestPPL.exe, the library DLLs,Configuration.dataand the foldersData,Examples,FunctionsandCodeGen.Start the interactive console
With no arguments the interpreter waits for commands at the
>prompt. Switch to the C-like notation withcode scr.console>ForestPPL.exe >code scr; >write("Hello World!"); Hello World! >help // or ?, lists all commands >? d // help for a single commandRun a bundled example
readcode(short formrc) reads and runs a file. Start with the cowsay series: each file adds one language feature to the previous one. Values after the file name replace$1$,$2$,$3$in the script.console>rc examples\cowsay\cowsay1.scr >rc examples\cowsay\cowsay4.scr 1 1 10Write your own file
Save this as
lines.scrand run it outside the console. The notation is chosen by the file extension.lines.scrvar n = $1$; for(i, 0, n) { write#("line {0} of {1}", i + 1, n); }terminalForestPPL.exe lines.scr 3Turn on checking and highlighting
checksyntax yesreports syntax errors before anything runs. The VS Code extension ships in the repository and installs with one command.terminalcode --install-extension forestppl-vscode-0.3.0.vsix
The essentials on one page
Examples come from the tutorial. Where the difference helps, the ppl form of the same construct sits next to the scr form.
Variables, constants, arrays
Every value is stored as a Unicode string: 5.2 is kept as "5.2", booleans as "True" and "False". A leading underscore makes a name private. The names result and all are reserved.
var x = 0;
x = 1 + 2; // "set" may be omitted
const PI2 = 6.28;
array y[3];
y[x - 1] = 2 + 3;
array colors[] = {"Red", "Green"};
writearray colors row;var (x [0]);
set (x) (+(1)(2));
array (y[3]);
set (y[+(1)(2)]) (0);
set (x) ( -(0)(+(3)(2)) ); // x = -(3+2)Declaring a name twice fails with name [x] already exists. Delete it with del x, or allow redeclaration with recreate yes.
Conditions and loops
for(i, start, end[, step]) becomes a ppl loop … do block. Braces around an if or for body may be dropped when it holds one simple statement. With nested ifs, keep the braces on the outer one.
for(i, 0, 3)
{
if (i == 0)
{
write#("i == 0");
continue;
}
else
write#("i != 0 = {0}", i);
}
x = 1 == 1 ? t : f;loop (i)(0)(3)(1)
(
do
(
(write(hello))
(write(world))
)
);Also available: switch / case / default, break, continue and setloopend.
Functions
A program runs top to bottom; there is no main. Results come back through parameters. Local variables live in the Local tree and are removed when the function returns.
function f(a, b, c, q)
{
q = a + b + c;
}
var res;
call f(1, 2, 3, res);
write#("res={0}", res); // res=6
sumdata string;
call f(1, 2, 3, res);
write#("res={0}", res); // res=123createnode Functions.Calc;
function
(
Functions.Calc.Sum(result1)(n1)(n2)
(
(set(result1)(+(n1) (n2)))
)
);Parameters can be typed (array: arr, storage: stor). Functions support finally and failure blocks, plus delegates and callbacks: delegate, dlgtcall, callback.
Nodes, public and private
A node groups data and functions under one name. Anything starting with _ is visible only to functions of its own node.
createnode N1;
var N1.x = "public var N1.x";
var N1._x = "private var N1._x";
function N1._f() // private
{
write(N1.x);
write(N1._x);
}
function N1.f() // public
{
N1._f();
}
N1.f();
write(N1._x); // Error: [GetValue] [N1._x] private object, no accessGive a long name a short alias with using x = xxxxx.
Storage: multidimensional and jagged arrays
Storage holds data of any dimension, including rows of different lengths. Work with it through sget, sset, swrite, sinfo, sresize and the StorageLib library, which adds sorting, median, Unique, Shuffle and PushRow.
storage s[2,2];
s[0,0] = "left top";
s[0,1] = s[0,0];storage (stor)(2)(2)
sset (stor)(0)(0)("var (x)(0);d")
eval (sget(stor)(0)(0))Save data to disk and restore it with savedata and readdata.
StorageLib: indices pick the level
Every StorageLib function counts its own parameters from the right; the parentheses before them are indices. The same call therefore works on the whole array, a slice or a row. For a 2×3×2 array holding 1..12:
storage s;
storage.ReadFile(s)("test_nd.csv")(",");
storage.Sum(s)(res); // 78 whole array
storage.Sum(s)(1)(res); // 57 slice s[1]
storage.Sum("s[1][2]")(res); // 23 row s[1][2] = 11 + 12
storage.PushRow(s)(0); // adds a zero-filled row to slice 0Statistics add up every number below the chosen node and skip text; Push, Pop, Sort and similar functions need the indices to reach a row. See the reference for the rules.
Libraries
The Main library is always loaded. Others come in with import or through UserImportN lines in Configuration.data. Build your own extension as a DLL with CodeGen.exe.
import Console;
import String;
var text = "##Hello World!##";
array colors[] = {"Black","DarkBlue","DarkGreen","DarkCyan",
"DarkRed","DarkMagenta","DarkYellow","Gray",
"DarkGray","Blue","Green","Cyan",
"Red","Magenta","Yellow","White"};
array ArrayChars;
call String.ToCharArray(text, ArrayChars);
for(i, 0, length(text))
{
Console.ForegroundColor(colors[i]);
Console.Write(ArrayChars[i]);
}
Console.DefaultColors();Help for a library: Math.help. Help for one function: Math.help(Sinh). Loaded libraries: importlist.
eval, debugging, timing
A script can build ppl code as a string and run it immediately. That lets you change variables and logic at runtime without restarting the engine.
>var x = "var(r); set(r)(/(180)(Math.PI())); write(r);";
>eval(x);
57.29577951308232
>write(eval("+(1)(2)"))
3start;
array x[10];
for(i, 0, length(x))
{
x[i] = i;
}
writearray x row;
stop("test duration:"); // test duration: 0.7918 msecTo see what your program does: dstree prints the syntax tree of every statement, printscope shows the scope stack, traceppl yes traces execution, and suspend pauses a script so you can type commands by hand.
Code and data live in trees
The d (display) command shows what is in memory at any moment: nodes N and leaves L. It is the main tool for understanding the language.
- GlobalPublic variables, arrays, storage and nodes, shared by all code.
- LocalVariables of the running function, removed when it returns.
- FunctionsFunctions only, no data. Loaded by default from
Functions\CommonFunctions.ppl. - ConfigurationSettings from
Configuration.data: code mode, logging, debugging, auto-loaded libraries. - Scope ManagerTracks scopes: block variables disappear with their block, so nothing leaks.
>code scr
>array y[] = {1,2,3};
>var x = 0;
>d
-N2 NS
---N3 Global
-----L0 empty (const)
-----N4 y [Array 3]
-------L0 # [1]
-------L1 # [2]
-------L2 # [3]
-----L1 x [0]
---N3 LocalWhat ships in the box
The number on each card is how many functions the reference documents. Highlighted libraries load by default with the shipped Configuration.data.
Console commands
The service and special commands you will use in your first week. The reference has the full list with examples.
| Command | Group | What it does |
|---|
Platforms and recent changes
New in ForestPPL
- Scope Manager for variables in functions, loops and blocks
- Producer–consumer pipeline for commands from the console and files
- Much faster AST traversal
- New Storage with jagged arrays, plus the StorageLib library
- Syntax checking before execution with
checksyntax - No more limits on index calculation and parameter passing
- New API for external libraries and an HTML reference
- New libraries: Requests (HTTP client) and NumPPL (NumPy-style arrays)
Questions, ideas, collaboration
All conversation about ForestPPL happens in GitHub Discussions. Post there when you get stuck, want to build something together, or have an idea for the language.
Found a reproducible bug? File it under Issues with the script, the command you ran and the error text.