mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-24 00:03:11 +01:00
Improve stringify of expressions
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
# include "../interface/InteractiveConsole.h"
|
# include "../interface/InteractiveConsole.h"
|
||||||
# include "Duktape.hpp"
|
# include "Duktape.hpp"
|
||||||
|
# include "ScriptEngine.h"
|
||||||
|
|
||||||
namespace OpenRCT2::Scripting
|
namespace OpenRCT2::Scripting
|
||||||
{
|
{
|
||||||
@@ -34,49 +35,7 @@ namespace OpenRCT2::Scripting
|
|||||||
|
|
||||||
void log(DukValue val)
|
void log(DukValue val)
|
||||||
{
|
{
|
||||||
std::string str;
|
_console.WriteLine(Stringify(val));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Register(duk_context* ctx)
|
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::clear, "clear");
|
||||||
dukglue_register_method(ctx, &ScConsole::log, "log");
|
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
|
} // namespace OpenRCT2::Scripting
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,6 @@
|
|||||||
using namespace OpenRCT2;
|
using namespace OpenRCT2;
|
||||||
using namespace OpenRCT2::Scripting;
|
using namespace OpenRCT2::Scripting;
|
||||||
|
|
||||||
static std::string Stringify(duk_context* ctx, duk_idx_t idx);
|
|
||||||
|
|
||||||
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 1;
|
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 1;
|
||||||
|
|
||||||
DukContext::DukContext()
|
DukContext::DukContext()
|
||||||
@@ -335,7 +333,7 @@ void ScriptEngine::ProcessREPL()
|
|||||||
}
|
}
|
||||||
else if (duk_get_type(_context, -1) != DUK_TYPE_UNDEFINED)
|
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);
|
_console.WriteLine(result);
|
||||||
}
|
}
|
||||||
duk_pop(_context);
|
duk_pop(_context);
|
||||||
@@ -365,17 +363,126 @@ void ScriptEngine::AddNetworkPlugin(const std::string_view& code)
|
|||||||
LoadPlugin(plugin);
|
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);
|
// the machine epsilon has to be scaled to the magnitude of the values used
|
||||||
if (type == DUK_TYPE_OBJECT && !duk_is_function(ctx, idx))
|
// 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)
|
||||||
{
|
{
|
||||||
return duk_json_encode(ctx, idx);
|
if (depth >= 3)
|
||||||
|
return "...";
|
||||||
|
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
return duk_safe_to_string(ctx, idx);
|
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()
|
bool OpenRCT2::Scripting::IsGameStateMutable()
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ namespace OpenRCT2::Scripting
|
|||||||
|
|
||||||
bool IsGameStateMutable();
|
bool IsGameStateMutable();
|
||||||
void ThrowIfGameStateNotMutable();
|
void ThrowIfGameStateNotMutable();
|
||||||
|
std::string Stringify(const DukValue& value, int32_t depth = 0);
|
||||||
|
|
||||||
} // namespace OpenRCT2::Scripting
|
} // namespace OpenRCT2::Scripting
|
||||||
|
|
||||||
|
|||||||
1
src/thirdparty/dukglue/register_property.h
vendored
1
src/thirdparty/dukglue/register_property.h
vendored
@@ -115,6 +115,7 @@ void dukglue_register_property(duk_context* ctx,
|
|||||||
|
|
||||||
duk_uint_t flags = DUK_DEFPROP_HAVE_GETTER
|
duk_uint_t flags = DUK_DEFPROP_HAVE_GETTER
|
||||||
| DUK_DEFPROP_HAVE_SETTER
|
| DUK_DEFPROP_HAVE_SETTER
|
||||||
|
| DUK_DEFPROP_SET_ENUMERABLE
|
||||||
| DUK_DEFPROP_HAVE_CONFIGURABLE /* set not configurable (from JS) */
|
| DUK_DEFPROP_HAVE_CONFIGURABLE /* set not configurable (from JS) */
|
||||||
| DUK_DEFPROP_FORCE /* allow overriding built-ins and previously defined properties */;
|
| DUK_DEFPROP_FORCE /* allow overriding built-ins and previously defined properties */;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user