PPGA Script is a scripting language that transpiles to Lua, original designed to be used in https://github.com/jprochazk/anikibot. It provides a more familiar C-style syntax and syntactic sugar, designed to reduce the amount of boilerplate when writing scripting commands in lua.
See tour.ppga for a quick tour.
- You may clone the repo and build from source, then use the scripts
ppga.sh
andppga.bat
.
OR
-
You may install the binary with
cargo install
from this repo:$ cargo install --git https://github.com/OptimalStrategy/ppga/ --features=build-binary
There's two implementations of the transpiler, in Rust and C++.
There's a syntax highlighting plugin for vscode. It can be installed from the vscode-ppga directory.
Feature | Description | Example | Generated Lua |
---|---|---|---|
Literals | None. |
1;
3.141592;
true;
false;
nil;
"a string"; |
1
3.141592
true
false
nil
"a string" |
Array and Dict literals | None. | // Arrays use python-style syntax
// and are initialized from 0.
let arr = [1, 2, 3];
// Dicts are similar to Lua but
// don't require the `[]`
let dict = {1 = 2, 3 = 4};
// Indexing uses the [] syntax:
let empty = {};
empty["string"] = "hello"; |
local arr = {[0] = 1, [1] = 2, [2] = 3}
local dict = {
[1] = 2,
[3] = 4
}
local empty = {}
empty["string"] = "hello"
|
F-strings | An interpolated string expression that compiles to concatenation of string literals and tostring() calls. A backslash can be used to escape formatter brackets: \{} . |
print(f"{a} + {b} = {a + b}");
print(f"\{escaped}"); |
print(tostring(a) .. " + " .. tostring(b)
.. " = " .. tostring(a + b))
print("{escaped}") |
Arithmetic Expressions | None. | print(1 + 2 * 3 / 4 ** 5 % 10); |
print(1 + 2 * 3 / 4 ^ 5 % 10) |
Integer Division | Integer division operator. | print(1 \ 2); |
print(1 // 2) |
Comparison and Equality operators | None. | print(3 < 4, 5 <= 6, 8 > 7, 9 >= 8, 10 != 11,
7 == 7); |
print(3 < 4, 5 <= 6, 8 > 7, 9 >= 8, 10 ~= 11,
7 == 7) |
Logic operators | None. | print(true and false or true); |
print(true and false or true) |
Concatenation Operator | This operator is the same as in lua. Reusing `+` for concatenation is not possible without rolling out a type system. | print("a" .. "b");
|
print("a" .. "b")
|
Default Operator | This operator is similar to `??` in C#. If `a` is not `nil`, its value will be returned, otherwise, the `b` value will be returned. This feature requires the PPGA internals included. | print(a ?? b); |
print(__PPGA_INTERNAL_DEFAULT(a, b))
|
Variable Declarations | Let bindings correspond to `local` lua variables, while `global` ones transpile to variables without a binding keyword. A `global` variable must be initialized at declaration. | let a;
global b = 4; |
local a
b = 4
|
Function Declarations | All functions are `local` by default. The `global` keyword may be used to make them global. The "fat arrow" syntax can be used if the function's body is a single expression. | global fn f() {}
fn g() {}
fn h(x) => x * x
|
function f()
end
local function g()
end
local function h(x)
return (x * x)
end
|
Lambda Expressions | Lambdas use the same syntax as named functions, except that they don't need an identifier. | print(fn(y, f) {});
print(fn(x) => x * x); |
print(function (y, f)
end)
print(function (x)
return (x * x)
end)
|
Rest Arguments / Variadics | Rest arguments use the `@` symbol and transpile to `...`. | fn f(a, @) {
print(a, @);
}
|
local function f(a, ...)
print(a, ...)
end
|
Ellipsis / Unpacking | Strips the parentheses and unpacks the given expression with `table.unpack`. | fn f(x) {
fn packed() {
return x, 5;
}
if not x {
// returns the result of packed
// as a single value:
// => return (packed())
return packed();
}
// unpacks the result of packed() as two values:
// => return unpack({packed()})
return ...packed();
} |
local function f(x)
local function packed()
return (y), (5)
end
if not(x) then
return (packed())
end
return __PPGA_INTERNAL_UNPACK(packed())
end
|
For Loop (ranges) | For-range loops transpile to Lua range loops | // From 0 to 3
for i in range(3) {
print(i);
}
// From 2 to 4
for i in range(2, 4) {
print(i);
}
// From 0 to 10 with step = 2
for i in range(0, 10, 2) {
print(i);
} |
for i = 0, 3, 1 do
print(i)
end
for i = 2, 4, 1 do
print(i)
end
for i = 0, 10, 2 do
print(i)
end
|
For Loop (containers) | For-in loops transpile to `pairs` or `ipairs` depending on the keyword used. | let container = [1, 2, 3];
container["string"] = "hello";
// Table iteration, uses pairs
for key, value in container {
print(key, value);
}
// Array iteration, uses ipairs
fori idx, value in container {
print(idx, value);
} |
local container = {[0] = 1, [1] = 2, [2] = 3}
container["string"] = "hello"
for key, value in pairs(container) do
print(key, value)
end
for idx, value in ipairs(container) do
print(idx, value)
end
|
Error Propagation with `?` and `err` Blocks | None. | fn may_fail(fail) {
if fail {
return nil, "error";
}
return "success", nil;
}
fn main() {
// The ? operator simplifies Go-style
// error handling. By default, this
// will make the whole program
// crash if an error is encountered.
let ok = may_fail(false)?;
print(f"First result: {ok}");
// Sometimes it is desirable to log or
// try to recover from the error.
// An err block may be used for this purpose:
let ok = may_fail(true) err {
print(f"An error has occurred: {err}");
return ...recovery();
}?;
return ok;
} |
local function may_fail(fail)
if fail then
return (nil), ("error")
end
return ("success"), (nil)
end
local function main()
local ok = nil
do
local _ok_L10S283, _err_L10S283 =
__PPGA_INTERNAL_HANDLE_ERR(
__PPGA_INTERNAL_DFLT_ERR_CB,
may_fail(false)
)
if _err_L10S283 ~= nil then
return (nil), (_err_L10S283)
end
ok = _ok_L10S283
end
print("First result: " .. tostring(ok))
local ok = nil
do
local _ok_L18S562, _err_L18S562 =
__PPGA_INTERNAL_HANDLE_ERR(
function (err)
print("An error has occurred: "
.. tostring(err))
return (unpack(recovery()))
end,
may_fail(true)
)
if _err_L18S562 ~= nil then
return (nil), (_err_L18S562)
end
ok = _ok_L18S562
end
return (ok)
end
|
|