Hello!
I am beginner developer and SL scripter. Because LSL is too dumb language, and development is complicated, i decided to write some translator, to extends its syntax.
Additional feature - it formats source code to make it well-readable and good-looking.
Because i have not too much experience, source code is really bad and weird... Please forgive me for that. But it works, and that is good.
Also please forgive me for my bad english.
Great thanks to Makopo/lslint and Sei_Lisa/kwdb projects!
Usage
lsl_ext <filename.lslx>
Limitations and issues
This is not lint tool. It does not make full validation of source code.
It does not optimizes your code. I plan to add it later, but now - no.
Also it can show error messages with not so accurate column position... but error is somewhere nearby.
If there is not builtins.txt
file in current folder, application will download its latest version.
Compiling
This application is written in go
and requires goyacc
tool (which should be installed automatically).
To build application simply run
go generate
And again, please forgive me for source code quality!
list test = [1, 2, 3, 4, 5, llGetPos()];
test[1] = "qqq";
test[2] += 10;
test[3] = test[4] + 20;
llOwnerSay(test[5]);
llSetPos(test[6]);
delete test[1, 2];
↓↓↓
list test = [1, 2, 3, 4, 5, llGetPos()];
test = llListReplaceList(test, ["qqq"], 1, 1);
test = llListReplaceList(test, [llList2Integer(test, 2) + 10], 2, 2);
test = llListReplaceList(test, [llList2Float(test, 4) + 20.0], 3, 3);
llOwnerSay(llList2String(test, 5));
llSetPos((vector)llList2String(test, 6));
test = llDeleteSubList(test, 1, 2);
Almost same as in Firestorm, but better, as i think. It supports all assignment operators =
, +=
, -=
, *=
, /=
, %=
, and can auto-detect required type (depending on nearest constant value or variable; but test[1] + test[2]
will be detected as strings).
Typecasting:
(string)test[1]
→ llList2String(test, 1)
(key)test[1]
→ llList2Key(test, 1)
(integer)test[1]
→ llList2Integer(test, 1)
(float)test[1]
→ llList2Float(test, 1)
(vector)test[1]
→ (vector)llList2String(test, 1)
(rotation)test[1]
→ (rotation)llList2String(test, 1)
(list)test[1, 5]
→ llList2List(test, 1, 5)
Unfortunately, you cannot use it with vector or rotation fields assignment, like test[2].x = 10;
. Sorry!
string s = "qwertyuiop";
s[5, 10] = "123";
llOwnerSay(s[1, 3]);
delete s[3];
↓↓↓
string s = "qwertyuiop";
s = llInsertString(llDeleteSubString(s, 5, 10), 5, "123");
llOwnerSay(llGetSubString(s, 1, 3));
llSetPos((vector)llList2String(test, 3));
s = llDeleteSubString(s, 3, 3);
Square braces can be used for strings too.
struct TestStruct {
string name = "<unknown>";
integer access;
}
TestStruct one{name: "One"};
TestStruct two{access: -10};
TestStruct[] arr;
Dump(TestStruct s) {
llOwnerSay(s.name + " " + s.access);
}
TestFunc() {
one.access = 5;;
arr += one + two;
arr += TestStruct{ name: "Three", access: 20 };
arr += TestStruct{ name: "Four" };;
integer i = 2;
arr[i + 1].access = 1;
Dump(arr[0]);
Dump(arr[1]);
delete arr[3];
delete arr[0, 1];
}
↓↓↓
list one = ["One", 0];
list two = ["<unknown>", -10];
list arr;
Dump(list s) {
llOwnerSay(llList2String(s, 0) + " " + llList2String(s, 1));
}
TestFunc() {
one = llListReplaceList(one, [5], 1, 1);
arr += one + two;
arr += ["Three", 20];
arr += ["Four", 0];
integer i = 2;
arr = llListReplaceList(arr, [1], 2 * (i + 1) + 1, 2 * (i + 1) + 1);
Dump(llList2List(arr, 2 * 0, 2 * 0 + 1));
Dump(llList2List(arr, 2 * 1, 2 * 1 + 1));
arr = llDeleteSubList(arr, 2 * 3, 2 * 3 + 1);
arr = llDeleteSubList(arr, 2 * 0, 2 * 1 + 1);
}
I missed this feature so much in LSL!
You can define default values for structure fields, and use arrays of structures together with lazy lists. Unfortunately, you cannot use fields of list and struct types. I just don't know how to handle it,
int i = 5;
switch (i + 1) {
case 1, 3, 5:
llOwnerSay("1, 3, 5");
case 2:
llOwnerSay("2");
case 4:
llOwnerSay("4");
default:
llOwnerSay("default");
}
↓↓↓
// switch(i + 1)
if (((i + 1) == 1) || ((i + 1) == 3) || ((i + 1) == 5)) {
llOwnerSay("1, 3, 5");
} else if ((i + 1) == 2) {
llOwnerSay("2");
} else if ((i + 1) == 4) {
llOwnerSay("4");
} else {
llOwnerSay("default");
}
Simple-in-use switch statement.
#pragma legacy_switch
int i = 5;
switch (i + 1) {
case 1, 3, 5:
llOwnerSay("1, 3, 5");
break;
case 2:
llOwnerSay("2");
case 4:
llOwnerSay("4");
break;
default:
llOwnerSay("default");
}
↓↓↓
integer i = 5;
// switch(i + 1)
if (((i + 1) == 1) || ((i + 1) == 3) || ((i + 1) == 5)) jump case_1894;
else if ((i + 1) == 2) jump case_1949;
else if ((i + 1) == 4) jump case_1980;
else jump case_2023;
@case_1894; // case 1, 3, 5:
llOwnerSay("1, 3, 5");
jump switch_end_1876; // break
@case_1949; // case 2:
llOwnerSay("2");
@case_1980; // case 4:
llOwnerSay("4");
jump switch_end_1876; // break
@case_2023; // default:
llOwnerSay("default");
@switch_end_1876;
C-style switch
operator. Almost like in Firestorm, but colon :
is required, and no need in braces {...}
.
Use #pragma legacy_switch
to enable it.
integer i;
for (;; i++) {
if (i > 5) break;
if (i < 3) continue;
llOwnerSay((string)i);
}
↓↓↓
for (; TRUE; i++) {
if (i > 5) {
jump for_end_1068; // break
}
if (i < 3) {
jump for_body_end_1068; // continue
}
llOwnerSay((string)i);
@for_body_end_1068;
}
@for_end_1068;
Can be used in for
, while
, do
loops. If there aren't any break
/continue
statements inside, labels will not be added.
#include "filename.lslx"
Pastes specified script as part of current script.
#pragma legacy_switch
#pragma no_legacy_switch
#pragma skip_unused
#pragma no_skip_unused
Specify options for translator.
legacy_switch
enabled C-style (Firestorm-like) switch
statement support.
skip_unused
- don't print unused variables and functions. Doesn't works well now, will be reworked in future.
Warning!!! Those options can be renamed or changed in future!
boolean
→ integer
true
→ TRUE
false
→ FALSE
int
→ integer
Those aliases has no any special purpose.
const CONSTANT_NAME = "value";
const ANOTHER_CONSTANT = 123;
↓↓↓
string CONSTANT_NAME = "value";
integer ANOTHER_CONSTANT = 123;
Constants cannot be assigned. Has no any special purpose too.
integer a = 5, b = 10, c;
↓↓↓
integer a = 5;
integer b = 10;
integer c;
TestStruct[] arr;
llOwnerSay((string)#some_list);
llOwnerSay((string)#"123");
llOwnerSay((string)#arr);
↓↓↓
list arr;
llOwnerSay((string)llGetListLength(some_list));
llOwnerSay((string)llStringLength("123"));
llOwnerSay((string)(llGetListLength(arr) / 2));
Maybe it is clumsy... i don't really like this... but okay, it it much easier than those long function names.
Also works with lists of structures, returning count of items. If you want to get real list length, use llGetListLength(arr)
or #((list)arr)
.
Scripts are being parsed into syntax tree and displayed back, with custom formatting. It is pretty accurate, but can skip empty lines. To add empty line, put ;
as statement:
llOwnerSay("123");;
Also some lists will be pretty-printed using newlines:
llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, ZERO_VECTOR, 1, PRIM_COLOR, 3, <1.0, 1.0, 1.0>, 1.0]);
llHTTPRequest("https://google.com", [HTTP_METHOD, "GET", HTTP_VERBOSE_THROTTLE, FALSE, HTTP_BODY_MAXLENGTH, 16384], "");
↓↓↓
llSetPrimitiveParams([
PRIM_COLOR, ALL_SIDES, ZERO_VECTOR, 1,
PRIM_COLOR, 3, <1.0, 1.0, 1.0>, 1.0
]);
llHTTPRequest("https://google.com", [
HTTP_METHOD, "GET",
HTTP_VERBOSE_THROTTLE, FALSE,
HTTP_BODY_MAXLENGTH, 16384
], "");