Merge e25ce2240639bd23764c492efd3654ce5d38252d into 1cbbba38f991e25027c52c4243afa1794aca59db

This commit is contained in:
leftibot 2026-04-17 07:46:29 +10:00 committed by GitHub
commit 74e8896c1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 187 additions and 62 deletions

View File

@ -448,6 +448,10 @@ if(BUILD_TESTING)
target_link_libraries(threading_config_test ${LIBS})
add_test(NAME Threading_Config_Test COMMAND threading_config_test)
add_executable(string_type_param_test unittests/string_type_param_test.cpp)
target_link_libraries(string_type_param_test ${LIBS})
add_test(NAME String_Type_Param_Test COMMAND string_type_param_test)
install(TARGETS test_module RUNTIME DESTINATION bin LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/chaiscript")
endif()
endif()

View File

@ -820,20 +820,24 @@
#include "language/chaiscript_parser.hpp"
namespace chaiscript {
class ChaiScript : public ChaiScript_Basic {
template<typename StringType>
class ChaiScript_Impl : public ChaiScript_Basic {
public:
ChaiScript(std::vector<std::string> t_modulepaths = {},
ChaiScript_Impl(std::vector<std::string> t_modulepaths = {},
std::vector<std::string> t_usepaths = {},
std::vector<Options> t_opts = chaiscript::default_options(),
std::vector<Library_Options> t_lib_opts = {})
: ChaiScript_Basic(chaiscript::Std_Lib::library(t_lib_opts),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>(),
: ChaiScript_Basic(chaiscript::Std_Lib::library<StringType>(t_lib_opts),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default, StringType>>(),
std::move(t_modulepaths),
std::move(t_usepaths),
std::move(t_opts),
std::find(t_lib_opts.begin(), t_lib_opts.end(), Library_Options::No_IO) != t_lib_opts.end()) {
}
};
using ChaiScript = ChaiScript_Impl<std::string>;
using ChaiScript_WString = ChaiScript_Impl<std::wstring>;
} // namespace chaiscript
#endif /* CHAISCRIPT_HPP_ */

View File

@ -27,8 +27,8 @@
#include "language/chaiscript_parser.hpp"
ChaiScript_Basic chai(
chaiscript::Std_Lib::library(),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default>>());
chaiscript::Std_Lib::library<std::string>(),
std::make_unique<parser::ChaiScript_Parser<eval::Noop_Tracer, optimizer::Optimizer_Default, std::string>>());
*/

View File

@ -38,6 +38,7 @@
namespace chaiscript {
class Std_Lib {
public:
template<typename StringType>
[[nodiscard]] static ModulePtr library(const std::vector<Library_Options> &t_opts = {}) {
if (std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Stdlib) != t_opts.end()) {
return std::make_shared<Module>();
@ -49,10 +50,10 @@ namespace chaiscript {
const bool no_prelude = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Prelude) != t_opts.end();
const bool no_json = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_JSON) != t_opts.end();
bootstrap::Bootstrap::bootstrap(*lib, no_io);
bootstrap::Bootstrap::bootstrap<StringType>(*lib, no_io);
bootstrap::standard_library::vector_type<std::vector<Boxed_Value>>("Vector", *lib);
bootstrap::standard_library::string_type<std::string>("string", *lib);
bootstrap::standard_library::string_type<StringType>("string", *lib);
bootstrap::standard_library::map_type<std::map<std::string, Boxed_Value>>("Map", *lib);
bootstrap::standard_library::pair_type<std::pair<Boxed_Value, Boxed_Value>>("Pair", *lib);

View File

@ -10,6 +10,8 @@
#ifndef CHAISCRIPT_BOOTSTRAP_HPP_
#define CHAISCRIPT_BOOTSTRAP_HPP_
#include <type_traits>
#include "../utility/utility.hpp"
#include "register_function.hpp"
@ -269,6 +271,7 @@ namespace chaiscript::bootstrap {
/// \brief perform all common bootstrap functions for std::string, void and POD types
/// \param[in,out] m Module to add bootstrapped functions to
/// \param[in] t_no_io If true, skip registering print_string and println_string
template<typename StringType>
static void bootstrap(Module &m, const bool t_no_io = false) {
m.add(user_type<void>(), "void");
m.add(user_type<bool>(), "bool");
@ -393,13 +396,27 @@ namespace chaiscript::bootstrap {
operators::equal<bool>(m);
operators::not_equal<bool>(m);
m.add(fun([](const std::string &s) { return s; }), "to_string");
m.add(fun([](const bool b) { return std::string(b ? "true" : "false"); }), "to_string");
m.add(fun([](const StringType &s) { return s; }), "to_string");
m.add(fun([](const bool b) -> StringType {
if constexpr (std::is_same_v<StringType, std::string>) {
return b ? "true" : "false";
} else {
const auto s = std::string(b ? "true" : "false");
return StringType(s.begin(), s.end());
}
}), "to_string");
m.add(fun(&unknown_assign), "=");
m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw");
m.add(fun([](const char c) { return std::string(1, c); }), "to_string");
m.add(fun(&Boxed_Number::to_string), "to_string");
m.add(fun([](const typename StringType::value_type c) -> StringType { return StringType(1, c); }), "to_string");
if constexpr (std::is_same_v<StringType, std::string>) {
m.add(fun(&Boxed_Number::to_string), "to_string");
} else {
m.add(fun([](const Boxed_Number &n) -> StringType {
const auto s = n.to_string();
return StringType(s.begin(), s.end());
}), "to_string");
}
bootstrap_pod_type<double>("double", m);
bootstrap_pod_type<long double>("long_double", m);

View File

@ -17,6 +17,7 @@
#include <memory>
#include <sstream>
#include <string>
#include <type_traits>
#include <vector>
#include "../dispatchkit/boxed_value.hpp"
@ -101,9 +102,31 @@ namespace chaiscript {
return Char_Parser_Helper<std::true_type>::u8str_from_ll(val);
}
};
template<typename S>
int stoi_for_string(const S &s, std::size_t *pos, int base) {
if constexpr (std::is_same_v<S, std::string>) {
return std::stoi(s, pos, base);
} else if constexpr (std::is_same_v<S, std::wstring>) {
return std::stoi(std::wstring(s.begin(), s.end()), pos, base);
} else {
return std::stoi(std::string(s.begin(), s.end()), pos, base);
}
}
template<typename S>
long long stoll_for_string(const S &s, std::size_t *pos, int base) {
if constexpr (std::is_same_v<S, std::string>) {
return std::stoll(s, pos, base);
} else if constexpr (std::is_same_v<S, std::wstring>) {
return std::stoll(std::wstring(s.begin(), s.end()), pos, base);
} else {
return std::stoll(std::string(s.begin(), s.end()), pos, base);
}
}
} // namespace detail
template<typename Tracer, typename Optimizer, std::size_t Parse_Depth = 512>
template<typename Tracer, typename Optimizer, typename StringType, std::size_t Parse_Depth = 512>
class ChaiScript_Parser final : public ChaiScript_Parser_Base {
void *get_tracer_ptr() noexcept override { return &m_tracer; }
@ -812,6 +835,17 @@ namespace chaiscript {
#endif
}
template<typename S>
static std::string to_narrow(const S &s) {
if constexpr (std::is_same_v<S, std::string>) {
return s;
} else if constexpr (std::is_convertible_v<S, std::string_view>) {
return std::string(s);
} else {
return std::string(s.begin(), s.end());
}
}
template<typename T, typename... Param>
std::unique_ptr<eval::AST_Node_Impl<Tracer>>
make_node(std::string_view t_match, const int t_prev_line, const int t_prev_col, Param &&...param) {
@ -1100,8 +1134,8 @@ namespace chaiscript {
void process_hex() {
if (!hex_matches.empty()) {
auto val = stoll(hex_matches, nullptr, 16);
match.push_back(char_type(val));
const auto val = detail::stoll_for_string(hex_matches, nullptr, 16);
match.push_back(static_cast<char_type>(val));
}
hex_matches.clear();
is_escaped = false;
@ -1110,8 +1144,8 @@ namespace chaiscript {
void process_octal() {
if (!octal_matches.empty()) {
auto val = stoll(octal_matches, nullptr, 8);
match.push_back(char_type(val));
const auto val = detail::stoll_for_string(octal_matches, nullptr, 8);
match.push_back(static_cast<char_type>(val));
}
octal_matches.clear();
is_escaped = false;
@ -1119,14 +1153,13 @@ namespace chaiscript {
}
void process_unicode() {
const auto ch = static_cast<uint32_t>(std::stoi(hex_matches, nullptr, 16));
const auto ch = static_cast<uint32_t>(detail::stoi_for_string<string_type>(hex_matches, nullptr, 16));
const auto match_size = hex_matches.size();
hex_matches.clear();
is_escaped = false;
const auto u_size = unicode_size;
unicode_size = 0;
char buf[4];
if (u_size != match_size) {
throw exception::eval_error("Incomplete unicode escape sequence");
}
@ -1134,26 +1167,44 @@ namespace chaiscript {
throw exception::eval_error("Invalid 16 bit universal character");
}
if (ch < 0x80) {
match += static_cast<char>(ch);
} else if (ch < 0x800) {
buf[0] = static_cast<char>(0xC0 | (ch >> 6));
buf[1] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 2);
} else if (ch < 0x10000) {
buf[0] = static_cast<char>(0xE0 | (ch >> 12));
buf[1] = static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
buf[2] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 3);
} else if (ch < 0x200000) {
buf[0] = static_cast<char>(0xF0 | (ch >> 18));
buf[1] = static_cast<char>(0x80 | ((ch >> 12) & 0x3F));
buf[2] = static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
buf[3] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 4);
if constexpr (sizeof(char_type) >= 4) {
if (ch < 0x200000) {
match.push_back(static_cast<char_type>(ch));
} else {
throw exception::eval_error("Invalid 32 bit universal character");
}
} else if constexpr (sizeof(char_type) >= 2) {
if (ch < 0x10000) {
match.push_back(static_cast<char_type>(ch));
} else if (ch < 0x110000) {
const auto adjusted = ch - 0x10000;
match.push_back(static_cast<char_type>(0xD800 + (adjusted >> 10)));
match.push_back(static_cast<char_type>(0xDC00 + (adjusted & 0x3FF)));
} else {
throw exception::eval_error("Invalid 32 bit universal character");
}
} else {
// this must be an invalid escape sequence?
throw exception::eval_error("Invalid 32 bit universal character");
char buf[4];
if (ch < 0x80) {
match += static_cast<char>(ch);
} else if (ch < 0x800) {
buf[0] = static_cast<char>(0xC0 | (ch >> 6));
buf[1] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 2);
} else if (ch < 0x10000) {
buf[0] = static_cast<char>(0xE0 | (ch >> 12));
buf[1] = static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
buf[2] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 3);
} else if (ch < 0x200000) {
buf[0] = static_cast<char>(0xF0 | (ch >> 18));
buf[1] = static_cast<char>(0x80 | ((ch >> 12) & 0x3F));
buf[2] = static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
buf[3] = static_cast<char>(0x80 | (ch & 0x3F));
match.append(buf, 4);
} else {
throw exception::eval_error("Invalid 32 bit universal character");
}
}
}
@ -1280,11 +1331,11 @@ namespace chaiscript {
const auto start = m_position;
if (Quoted_String_()) {
std::string match;
StringType match;
const auto prev_stack_top = m_match_stack.size();
bool is_interpolated = [&]() -> bool {
Char_Parser<std::string> cparser(match, true);
Char_Parser<StringType> cparser(match, true);
auto s = start + 1, end = m_position - 1;
@ -1293,7 +1344,7 @@ namespace chaiscript {
if (*s == '{') {
// We've found an interpolation point
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(match)));
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(to_narrow(match), start.line, start.col, const_var(match)));
if (cparser.is_interpolated) {
// If we've seen previous interpolation, add on instead of making a new one
@ -1351,7 +1402,7 @@ namespace chaiscript {
return cparser.is_interpolated;
}();
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(match)));
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(to_narrow(match), start.line, start.col, const_var(match)));
if (is_interpolated) {
build_match<eval::Binary_Operator_AST_Node<Tracer>>(prev_stack_top, "+");
@ -1439,7 +1490,7 @@ namespace chaiscript {
close_seq += '"';
// Extract raw content up to closing sequence
std::string match;
StringType match;
auto end = m_position; // m_position is already past the closing sequence
// Content is from s up to (end - close_seq.size())
@ -1456,11 +1507,11 @@ namespace chaiscript {
break;
}
}
match.push_back(*s);
match.push_back(static_cast<typename StringType::value_type>(*s));
++s;
}
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(match)));
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(to_narrow(match), start.line, start.col, const_var(match)));
return true;
}
return false;
@ -1501,11 +1552,11 @@ namespace chaiscript {
const auto start = m_position;
if (Single_Quoted_String_()) {
std::string match;
StringType match;
{
// scope for cparser destructor
Char_Parser<std::string> cparser(match, false);
Char_Parser<StringType> cparser(match, false);
for (auto s = start + 1, end = m_position - 1; s != end; ++s) {
cparser.parse(*s, start.line, start.col, *m_filename);
@ -1518,7 +1569,8 @@ namespace chaiscript {
*m_filename);
}
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(match, start.line, start.col, const_var(char(match.at(0)))));
m_match_stack.push_back(make_node<eval::Constant_AST_Node<Tracer>>(to_narrow(match), start.line, start.col,
const_var(static_cast<typename StringType::value_type>(match.at(0)))));
return true;
} else {
return false;
@ -2971,7 +3023,7 @@ namespace chaiscript {
}
AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override {
ChaiScript_Parser<Tracer, Optimizer> parser(m_tracer, m_optimizer);
ChaiScript_Parser<Tracer, Optimizer, StringType> parser(m_tracer, m_optimizer);
return parser.parse_internal(t_input, t_fname);
}

View File

@ -14,7 +14,7 @@
#endif
CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_chaiscript_stdlib() {
return chaiscript::Std_Lib::library();
return chaiscript::Std_Lib::library<std::string>();
}
#ifdef __llvm__

View File

@ -2,5 +2,5 @@
#include "chaiscript_parser.hpp"
std::unique_ptr<chaiscript::parser::ChaiScript_Parser_Base> create_chaiscript_parser() {
return std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default>>();
return std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default, std::string>>();
}

View File

@ -2,5 +2,5 @@
#include "chaiscript_stdlib.hpp"
std::shared_ptr<chaiscript::Module> create_chaiscript_stdlib() {
return chaiscript::Std_Lib::library();
return chaiscript::Std_Lib::library<std::string>();
}

View File

@ -1017,9 +1017,9 @@ struct Count_Tracer {
};
TEST_CASE("Test count tracer") {
using Parser_Type = chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Tracer<Count_Tracer>, chaiscript::optimizer::Optimizer_Default>;
using Parser_Type = chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Tracer<Count_Tracer>, chaiscript::optimizer::Optimizer_Default, std::string>;
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library(), std::make_unique<Parser_Type>());
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library<std::string>(), std::make_unique<Parser_Type>());
Parser_Type &parser = dynamic_cast<Parser_Type &>(chai.get_parser());
@ -1386,7 +1386,7 @@ TEST_CASE("Test if non copyable/movable types can be registered") {
// Tests through ChaiScript_Basic (library options passed explicitly to Std_Lib::library)
TEST_CASE("ChaiScript_Basic No_Stdlib option disables all standard library functions") {
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_Stdlib}),
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library<std::string>({chaiscript::Library_Options::No_Stdlib}),
create_chaiscript_parser(),
{},
{},
@ -1400,7 +1400,7 @@ TEST_CASE("ChaiScript_Basic No_Stdlib option disables all standard library funct
}
TEST_CASE("ChaiScript_Basic No_IO option uses null handler by default") {
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_IO}),
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library<std::string>({chaiscript::Library_Options::No_IO}),
create_chaiscript_parser(),
{},
{},
@ -1422,7 +1422,7 @@ TEST_CASE("ChaiScript_Basic No_IO option uses null handler by default") {
}
TEST_CASE("ChaiScript_Basic No_Prelude option disables prelude functions") {
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_Prelude}),
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library<std::string>({chaiscript::Library_Options::No_Prelude}),
create_chaiscript_parser(),
{},
{},
@ -1435,7 +1435,7 @@ TEST_CASE("ChaiScript_Basic No_Prelude option disables prelude functions") {
}
TEST_CASE("ChaiScript_Basic No_JSON option disables JSON support") {
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library({chaiscript::Library_Options::No_JSON}),
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library<std::string>({chaiscript::Library_Options::No_JSON}),
create_chaiscript_parser(),
{},
{},
@ -1447,7 +1447,7 @@ TEST_CASE("ChaiScript_Basic No_JSON option disables JSON support") {
}
TEST_CASE("ChaiScript_Basic default library has all functions") {
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library(),
chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library<std::string>(),
create_chaiscript_parser(),
{},
{},

View File

@ -5,8 +5,8 @@
Multi_Test_Chai::Multi_Test_Chai()
: m_chai(new chaiscript::ChaiScript_Basic(
chaiscript::Std_Lib::library(),
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default>>())) {
chaiscript::Std_Lib::library<std::string>(),
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default, std::string>>())) {
}
std::shared_ptr<chaiscript::ChaiScript_Basic> Multi_Test_Chai::get_chai() {

View File

@ -67,7 +67,7 @@ int main() {
// For this test we are going to load the dynamic stdlib
// to make sure it continues to work
chaiscript::ChaiScript_Basic chai(
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default>>(),
std::make_unique<chaiscript::parser::ChaiScript_Parser<chaiscript::eval::Noop_Tracer, chaiscript::optimizer::Optimizer_Default, std::string>>(),
modulepaths,
usepaths);
#endif

View File

@ -0,0 +1,47 @@
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4062 4242 4566 4640 4702 6330 28251)
#endif
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wparentheses"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#endif
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript_basic.hpp>
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE("String type can be parameterized to wstring") {
chaiscript::ChaiScript_WString chai;
SECTION("String literals produce std::wstring") {
auto result = chai.eval<std::wstring>("\"hello\"");
CHECK(result == L"hello");
}
SECTION("String concatenation works with wstring") {
auto result = chai.eval<std::wstring>("\"hello\" + \" world\"");
CHECK(result == L"hello world");
}
SECTION("to_string works for numbers with wstring") {
auto result = chai.eval<std::wstring>("to_string(42)");
CHECK(result == L"42");
}
SECTION("String interpolation works with wstring") {
auto result = chai.eval<std::wstring>("var x = 5; \"value: ${x}\"");
CHECK(result == L"value: 5");
}
SECTION("Default ChaiScript still uses std::string") {
chaiscript::ChaiScript default_chai;
auto result = default_chai.eval<std::string>("\"hello\"");
CHECK(result == "hello");
}
}