Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 36 additions & 9 deletions indra/alscript/lsl/allsltoslua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -607,9 +607,17 @@ namespace
Expr sideEffect(LSLExpression* e);
// The arguments, each as its parameter's type -- but those SLua takes
// either text or a uuid for (`text`, a bit for each by its place),
// as they are.
// as they are; and, for ll's or llcompat's (`builtin`), text given
// for a key (Writer::textAsKey).
// One may be given written already: its place, and its text.
std::string args(LSLASTNode* list, LSLParamList* params, U16 text = 0, int put = -1, const std::string& put_text = {});
std::string args(LSLASTNode* list, LSLParamList* params, U16 text = 0, int put = -1, const std::string& put_text = {},
bool builtin = false);
// Text given where ll or llcompat takes a key: as it is, which they
// take as LSL's functions did, where uuid() would stop the script
// on text that is no UUID, which LSL's function answered as it
// answers a key of nothing; `:: any` tells the type checker, which
// asks for a uuid. A UUID written out is a uuid, as anywhere.
std::string textAsKey(LSLExpression* given);
// What SLua has in a library call's stead, where it means the same;
// nothing where it has nothing.
std::optional<Expr> idiom(LSLFunctionExpression* e, const std::string& lsl, std::string& called);
Expand Down Expand Up @@ -2391,7 +2399,23 @@ namespace
return out;
}

std::string Writer::args(LSLASTNode* list, LSLParamList* params, U16 text, int put, const std::string& put_text)
std::string Writer::textAsKey(LSLExpression* given)
{
LSLExpression* inner = unbracketed(given);
const bool uuid = inner && inner->getNodeSubType() == NODE_CONSTANT_EXPRESSION &&
inner->getChild(0)->getNodeSubType() == NODE_STRING_CONSTANT &&
ALLSLTraits::isUuid(static_cast<LSLStringConstant*>(inner->getChild(0))->getValue());
if (slType(given) != LST_STRING || uuid)
{
return coerced(given, LST_KEY).text;
}
noteOnce(given, "SluaKeyAsText",
"SLua's ll takes a key as text, as LSL's functions did, so text is given as it is; `:: any` tells the type checker, which "
"asks for a uuid. uuid() would stop the script on text that is no UUID.");
return bracketed(value(given), PRIMARY) + " :: any";
}

std::string Writer::args(LSLASTNode* list, LSLParamList* params, U16 text, int put, const std::string& put_text, bool builtin)
{
std::string out;
LSLASTNode* param = params ? params->getChild(0) : nullptr;
Expand All @@ -2406,7 +2430,10 @@ namespace
to = slType(given);
}
// A parameter the script only reads as a truth is given one.
out += (out.empty() ? "" : ", ") + (at == put ? put_text : boolean(taken) ? truthOf(given) : coerced(given, to).text);
out += (out.empty() ? "" : ", ") + (at == put ? put_text
: boolean(taken) ? truthOf(given)
: builtin && to == LST_KEY ? textAsKey(given)
: coerced(given, to).text);
param = param ? param->getNext() : nullptr;
}
return out;
Expand Down Expand Up @@ -2663,7 +2690,7 @@ namespace
else
{
const LSLIType to = param ? param->getIType() : static_cast<LSLExpression*>(arg)->getIType();
one = coerced(static_cast<LSLExpression*>(arg), to).text;
one = to == LST_KEY ? textAsKey(static_cast<LSLExpression*>(arg)) : coerced(static_cast<LSLExpression*>(arg), to).text;
}
out += (out.empty() ? "" : ", ") + one;
param = param ? param->getNext() : nullptr;
Expand Down Expand Up @@ -3117,7 +3144,7 @@ namespace
table += lines ? listIndent(0) + "}" : " }";
called = "ll." + lsl.substr(2);
LSLParamList* params = e->getIdentifier()->getSymbol()->getFunctionDecl();
return Expr{ called + "(" + args(e->getArguments(), params, 0, call->at, table) + ")" };
return Expr{ called + "(" + args(e->getArguments(), params, 0, call->at, table, true) + ")" };
}

std::optional<Expr> Writer::idiom(LSLFunctionExpression* e, const std::string& lsl, std::string& called)
Expand Down Expand Up @@ -3446,15 +3473,15 @@ namespace
noteOnce(e, "SluaUse", "SLua would use [1] for [2].", { trait->sluaUse, lsl });
}
const std::string args_text =
(slua & ALLSLTraits::SluaIndexArgs) ? llArgs(e, indexes) : args(e->getArguments(), params, trait ? trait->sluaTextArgs : 0);
(slua & ALLSLTraits::SluaIndexArgs) ? llArgs(e, indexes) : args(e->getArguments(), params, trait ? trait->sluaTextArgs : 0, -1, {}, true);
return { called + "(" + args_text + ")", PRIMARY, (slua & ALLSLTraits::SluaBool) != 0 };
}
// An index SLua's ll counts from 1, or nil for none, read as LSL's:
// from 0, or -1.
const U8 not_ll = compat_only & ~ALLSLTraits::SluaIndexResult;
if (mOptions.sluaCalls && (slua & ALLSLTraits::SluaIndexResult) && !(slua & not_ll) && ll_indexes)
{
const std::string args_text = (slua & ALLSLTraits::SluaIndexArgs) ? llArgs(e, indexes) : args(e->getArguments(), params);
const std::string args_text = (slua & ALLSLTraits::SluaIndexArgs) ? llArgs(e, indexes) : args(e->getArguments(), params, 0, -1, {}, true);
called = "ll." + bare;
return { "(" + called + "(" + args_text + ") or 0) - 1", ADD };
}
Expand Down Expand Up @@ -3499,7 +3526,7 @@ namespace
noteOnce(e, "SluaUse", "SLua would use [1] for [2].", { trait->sluaUse, lsl });
}
called = "llcompat." + bare;
return { called + "(" + args(e->getArguments(), params) + ")" };
return { called + "(" + args(e->getArguments(), params, 0, -1, {}, true) + ")" };
}

Expr Writer::signedUnless(bool same, const std::string& call, LSLASTNode* at)
Expand Down
46 changes: 44 additions & 2 deletions indra/alscript/tests/allsltoslua_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ namespace tut
template<> template<>
void allsltoslua_object::test<23>()
{
set_test_name("keys that hold text: link_message's id as SLua passes it, a string; a key given text that is no UUID kept a string; text where SLua takes it, uuid() where only a uuid will do");
set_test_name("keys that hold text: link_message's id as SLua passes it, a string; a key given text that is no UUID kept a string; text where SLua takes it, ll's key too");
const ALLSLToSLua::Result r = convert("key DOMAIN = \"MY CHANNEL\";\n"
"key gOwner;\n"
"send(key to) { llMessageLinked(LINK_SET, 0, \"\", to); }\n"
Expand All @@ -869,7 +869,8 @@ namespace tut
has(r, "send(\"other text\")") && has(r, "ll.MessageLinked(LINK_SET, 0, \"\", to)"));
ensure("compared as text: " + r.text, has(r, "if id == DOMAIN then") && has(r, "if id == tostring(gOwner) then"));
ensure("a key given the id, text: " + r.text, has(r, "local k = id") && has(r, "{k}`)"));
ensure("uuid() where only a uuid will do, said: " + r.text, has(r, "ll.GetOwnerKey(uuid(\"abc\"))") && noted(r, "SluaKeyText"));
ensure("ll's key given text as it is, said: " + r.text,
has(r, "ll.GetOwnerKey(\"abc\" :: any)") && noted(r, "SluaKeyAsText") && !noted(r, "SluaKeyText"));
checksClean(r);
}

Expand Down Expand Up @@ -2865,4 +2866,45 @@ namespace tut
ensure_equals("unused", unused, std::string("Variable 'found' is never used; prefix with '_' to silence\n"));
checksClean(r);
}

template<> template<>
void allsltoslua_object::test<84>()
{
set_test_name("text given where ll or llcompat takes a key is given as it is, as LSL's function took it, `:: any` for the type "
"checker, said once; a UUID written out still a uuid; the script's own function's key still uuid()");
const std::string lsl = "string gJson;\n"
"integer mine(key who) { return who == llGetOwner(); }\n"
"default { touch_start(integer n) {\n"
" llOwnerSay(\"selected: \" + llKey2Name(llJsonGetValue(gJson, [\"selected\"])));\n"
" llOwnerSay(llKey2Name(\"a2e76fcd-9360-4f6d-a924-\" + \"938f923df11d\"));\n"
" llOwnerSay(llKey2Name(\"a2e76fcd-9360-4f6d-a924-938f923df11d\"));\n"
" if (mine(llJsonGetValue(gJson, [\"owner\"]))) llOwnerSay(\"mine\");\n"
"} }\n";
const ALLSLToSLua::Result r = convert(lsl);
ensure("Tapple's, as it is: " + r.text, has(r, "ll.Key2Name(ll.JsonGetValue(gJson, {\"selected\"}) :: any)"));
ensure("bracketed where it must be: " + r.text, has(r, "ll.Key2Name((\"a2e76fcd-9360-4f6d-a924-\" .. \"938f923df11d\") :: any)"));
ensure("a UUID written out, a uuid: " + r.text, has(r, "ll.Key2Name(uuid(\"a2e76fcd-9360-4f6d-a924-938f923df11d\"))"));
ensure("the script's own function's key, uuid(): " + r.text, has(r, "mine(uuid(ll.JsonGetValue(gJson, {\"owner\"})))") &&
noted(r, "SluaUuidText"));
ensure("said once: " + r.text, count(r, "-- LSL: SLua's ll takes a key as text") == 1);
checksClean(r);
if (!newSolver)
{
// ll's calls alone, strict.
const ALLSLToSLua::Result calls = convert("default { touch_start(integer n) {\n"
" llOwnerSay(llKey2Name(llJsonGetValue(\"{}\", [\"selected\"])));\n"
" llOwnerSay((string)llGetOwnerKey(\"a\" + (string)n));\n"
"} }\n");
std::string said;
for (const ALScriptProblem& p : service.check("--!strict\n" + calls.text))
{
said += p.severity == ALScriptProblem::Severity::Error ? p.message + "\n" : std::string();
}
ensure("strict:\n" + said + "---\n" + calls.text, said.empty() && calls.text.find(":: any") != std::string::npos);
}

const ALLSLToSLua::Result close = ALLSLToSLua::convert(lsl, ALLSLToSLua::Options::closeToLSL());
ensure("llcompat's the same: " + close.text, close.converted && close.text.find(":: any)") != std::string::npos);
checksClean(close);
}
}
1 change: 1 addition & 0 deletions indra/newview/skins/default/xui/en/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,7 @@ If you feel this is an error, please contact support@secondlife.com</string>
<string name="SluaKeyCase">SLua's uuid writes a key's letters small, and finds it the same key as one written small, where LSL's key kept its capitals and was compared as text.</string>
<string name="SluaVectorText">ll.DumpList2String writes a vector's or a rotation's parts as LSL did, with five places; SLua's tostring writes as few as each needs, &lt;1, 2, 3&gt;, which tovector reads back the same.</string>
<string name="SluaUuidText">uuid() stops the script on text that is no UUID, where LSL's keys held any text; touuid() answers nil instead.</string>
<string name="SluaKeyAsText">SLua's ll takes a key as text, as LSL's functions did, so text is given as it is; `:: any` tells the type checker, which asks for a uuid. uuid() would stop the script on text that is no UUID.</string>
<string name="SluaDetectedTable">detected[n] is what LSL read with llDetected*(n - 1): SLua counts it from 1, and one past its end is an error, where LSL answered nothing.</string>
<string name="SluaMathDomain">math.sqrt, math.log and math.log10 answer nan or -inf out of their range, where LSL answered 0 or stopped with a math error.</string>
<string name="SluaBase64Decode">llbase64.decode keeps every byte it decodes, a NUL and what is not UTF-8 among them, where LSL's llBase64ToString cut the string at a NUL and made what was not UTF-8 '?'.</string>
Expand Down
Loading