mirror of https://github.com/OpenRCT2/OpenRCT2.git
Improve stringify of expressions
This commit is contained in:
parent
ae0c2638e3
commit
dfd45651dc
|
@ -13,6 +13,7 @@
|
|||
|
||||
# include "../interface/InteractiveConsole.h"
|
||||
# include "Duktape.hpp"
|
||||
# include "ScriptEngine.h"
|
||||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
|
@ -34,49 +35,7 @@ namespace OpenRCT2::Scripting
|
|||
|
||||
void log(DukValue val)
|
||||
{
|
||||
std::string str;
|
||||
switch (val.type())
|
||||
{
|
||||
case DukValue::Type::UNDEFINED:
|
||||
str = "undefined";
|
||||
break;
|
||||
case DukValue::Type::NULLREF:
|
||||
str = "null";
|
||||
break;
|
||||
case DukValue::Type::BOOLEAN:
|
||||
str = val.as_bool() ? "true" : "false";
|
||||
break;
|
||||
case DukValue::Type::NUMBER:
|
||||
{
|
||||
const auto d = val.as_double();
|
||||
const duk_int_t i = val.as_int();
|
||||
if (AlmostEqual<double>(d, i))
|
||||
{
|
||||
str = std::to_string(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
str = std::to_string(d);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DukValue::Type::STRING:
|
||||
str = val.as_string();
|
||||
break;
|
||||
case DukValue::Type::OBJECT:
|
||||
str = "{}";
|
||||
break;
|
||||
case DukValue::Type::BUFFER:
|
||||
str = "buffer";
|
||||
break;
|
||||
case DukValue::Type::POINTER:
|
||||
str = "pointer";
|
||||
break;
|
||||
case DukValue::Type::LIGHTFUNC:
|
||||
break;
|
||||
}
|
||||
|
||||
_console.WriteLine(str);
|
||||
_console.WriteLine(Stringify(val));
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
|
@ -84,19 +43,6 @@ namespace OpenRCT2::Scripting
|
|||
dukglue_register_method(ctx, &ScConsole::clear, "clear");
|
||||
dukglue_register_method(ctx, &ScConsole::log, "log");
|
||||
}
|
||||
|
||||
private:
|
||||
// Taken from http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
|
||||
template<class T>
|
||||
static typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type AlmostEqual(T x, T y, int32_t ulp = 20)
|
||||
{
|
||||
// the machine epsilon has to be scaled to the magnitude of the values used
|
||||
// and multiplied by the desired precision in ULPs (units in the last place)
|
||||
return std::abs(x - y) <= std::numeric_limits<T>::epsilon() * std::abs(x + y) * ulp
|
||||
// unless the result is subnormal
|
||||
|| std::abs(x - y)
|
||||
< (std::numeric_limits<T>::min)(); // TODO: Remove parentheses around min once the macro is removed
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
|
|
@ -35,8 +35,6 @@
|
|||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
static std::string Stringify(duk_context* ctx, duk_idx_t idx);
|
||||
|
||||
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 1;
|
||||
|
||||
DukContext::DukContext()
|
||||
|
@ -335,7 +333,7 @@ void ScriptEngine::ProcessREPL()
|
|||
}
|
||||
else if (duk_get_type(_context, -1) != DUK_TYPE_UNDEFINED)
|
||||
{
|
||||
std::string result = Stringify(_context, -1);
|
||||
auto result = Stringify(DukValue::copy_from_stack(_context, -1));
|
||||
_console.WriteLine(result);
|
||||
}
|
||||
duk_pop(_context);
|
||||
|
@ -365,17 +363,126 @@ void ScriptEngine::AddNetworkPlugin(const std::string_view& code)
|
|||
LoadPlugin(plugin);
|
||||
}
|
||||
|
||||
static std::string Stringify(duk_context* ctx, duk_idx_t idx)
|
||||
// Taken from http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
|
||||
template<class T>
|
||||
static typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type AlmostEqual(T x, T y, int32_t ulp = 20)
|
||||
{
|
||||
auto type = duk_get_type(ctx, idx);
|
||||
if (type == DUK_TYPE_OBJECT && !duk_is_function(ctx, idx))
|
||||
// the machine epsilon has to be scaled to the magnitude of the values used
|
||||
// and multiplied by the desired precision in ULPs (units in the last place)
|
||||
return std::abs(x - y) <= std::numeric_limits<T>::epsilon() * std::abs(x + y) * ulp
|
||||
// unless the result is subnormal
|
||||
|| std::abs(x - y) < (std::numeric_limits<T>::min)(); // TODO: Remove parentheses around min once the macro is removed
|
||||
}
|
||||
|
||||
std::string OpenRCT2::Scripting::Stringify(const DukValue& val, int32_t depth)
|
||||
{
|
||||
if (depth >= 3)
|
||||
return "...";
|
||||
|
||||
std::string str;
|
||||
switch (val.type())
|
||||
{
|
||||
return duk_json_encode(ctx, idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
return duk_safe_to_string(ctx, idx);
|
||||
case DukValue::Type::UNDEFINED:
|
||||
str = "undefined";
|
||||
break;
|
||||
case DukValue::Type::NULLREF:
|
||||
str = "null";
|
||||
break;
|
||||
case DukValue::Type::BOOLEAN:
|
||||
str = val.as_bool() ? "true" : "false";
|
||||
break;
|
||||
case DukValue::Type::NUMBER:
|
||||
{
|
||||
const auto d = val.as_double();
|
||||
const duk_int_t i = val.as_int();
|
||||
if (AlmostEqual<double>(d, i))
|
||||
{
|
||||
str = std::to_string(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
str = std::to_string(d);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DukValue::Type::STRING:
|
||||
str = "\"" + val.as_string() + "\"";
|
||||
break;
|
||||
case DukValue::Type::OBJECT:
|
||||
if (val.is_function())
|
||||
{
|
||||
auto ctx = val.context();
|
||||
val.push();
|
||||
if (duk_is_c_function(ctx, -1))
|
||||
{
|
||||
str = u8"ƒ [native]";
|
||||
}
|
||||
else if (duk_is_ecmascript_function(ctx, -1))
|
||||
{
|
||||
str = u8"ƒ [ecmascript]";
|
||||
}
|
||||
else
|
||||
{
|
||||
str = u8"ƒ [javascript]";
|
||||
}
|
||||
duk_pop(ctx);
|
||||
}
|
||||
else if (val.is_array())
|
||||
{
|
||||
str = "[";
|
||||
auto ctx = val.context();
|
||||
val.push();
|
||||
auto arrayLen = duk_get_length(ctx, -1);
|
||||
for (duk_uarridx_t i = 0; i < arrayLen; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
str += ", ";
|
||||
if (duk_get_prop_index(ctx, -1, i))
|
||||
{
|
||||
auto arrayVal = DukValue::take_from_stack(ctx);
|
||||
str += Stringify(arrayVal, depth + 1);
|
||||
}
|
||||
if (i >= 4)
|
||||
{
|
||||
str += ", ...";
|
||||
break;
|
||||
}
|
||||
}
|
||||
duk_pop(ctx);
|
||||
str += "]";
|
||||
}
|
||||
else
|
||||
{
|
||||
str = "{";
|
||||
auto ctx = val.context();
|
||||
val.push();
|
||||
duk_enum(ctx, -1, 0);
|
||||
auto index = 0;
|
||||
while (duk_next(ctx, -1, 1))
|
||||
{
|
||||
if (index != 0)
|
||||
str += ", ";
|
||||
auto value = DukValue::take_from_stack(ctx, -1);
|
||||
auto key = DukValue::take_from_stack(ctx, -1);
|
||||
str += Stringify(key, depth + 1);
|
||||
str += ": ";
|
||||
str += Stringify(value, depth + 1);
|
||||
index++;
|
||||
}
|
||||
duk_pop_2(ctx);
|
||||
str += "}";
|
||||
}
|
||||
break;
|
||||
case DukValue::Type::BUFFER:
|
||||
str = "buffer";
|
||||
break;
|
||||
case DukValue::Type::POINTER:
|
||||
str = "pointer";
|
||||
break;
|
||||
case DukValue::Type::LIGHTFUNC:
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
bool OpenRCT2::Scripting::IsGameStateMutable()
|
||||
|
|
|
@ -168,6 +168,7 @@ namespace OpenRCT2::Scripting
|
|||
|
||||
bool IsGameStateMutable();
|
||||
void ThrowIfGameStateNotMutable();
|
||||
std::string Stringify(const DukValue& value, int32_t depth = 0);
|
||||
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ void dukglue_register_property(duk_context* ctx,
|
|||
|
||||
duk_uint_t flags = DUK_DEFPROP_HAVE_GETTER
|
||||
| DUK_DEFPROP_HAVE_SETTER
|
||||
| DUK_DEFPROP_SET_ENUMERABLE
|
||||
| DUK_DEFPROP_HAVE_CONFIGURABLE /* set not configurable (from JS) */
|
||||
| DUK_DEFPROP_FORCE /* allow overriding built-ins and previously defined properties */;
|
||||
|
||||
|
|
Loading…
Reference in New Issue