Skip to content
Open
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
70 changes: 70 additions & 0 deletions include/chaiscript/extras/regex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* @file ChaiScript Regex Support
*
* Adds std::regex support to ChaiScript:
*
* regex(string pattern)
* regex(string pattern, regex_constants flag)
* regex_search(string subject, regex pattern)
* regex_search(string subject, regex pattern, smatch results)
* regex_match(string subject, regex pattern)
* regex_replace(string subject, regex pattern, string replacement)
* smatch::size()
* smatch[index]
* regex_icase()
*/

#ifndef CHAISCRIPT_EXTRAS_REGEX_HPP_
#define CHAISCRIPT_EXTRAS_REGEX_HPP_

#include <regex>
#include <string>
#include <vector>
#include <chaiscript/chaiscript.hpp>

namespace chaiscript {
namespace extras {
namespace regex {

ModulePtr bootstrap(ModulePtr m = std::make_shared<Module>())
{
m->add(user_type<std::regex>(), "regex");
m->add(user_type<std::smatch>(), "smatch");

m->add(constructor<std::regex(const std::string &)>(), "regex");
m->add(constructor<std::regex(const std::string &, std::regex_constants::syntax_option_type)>(), "regex");
m->add(constructor<std::smatch()>(), "smatch");

m->add(fun([](){ return std::regex_constants::icase; }), "regex_icase");

m->add(fun([](const std::string &subject, const std::regex &pattern) {
return std::regex_search(subject, pattern);
}), "regex_search");

m->add(fun([](const std::string &subject, const std::regex &pattern, std::smatch &results) {
return std::regex_search(subject, results, pattern);
}), "regex_search");

m->add(fun([](const std::string &subject, const std::regex &pattern) {
return std::regex_match(subject, pattern);
}), "regex_match");

m->add(fun([](const std::string &subject, const std::regex &pattern, const std::string &replacement) {
return std::regex_replace(subject, pattern, replacement);
}), "regex_replace");

m->add(fun([](const std::smatch &results) {
return static_cast<int>(results.size());
}), "size");

m->add(fun([](const std::smatch &results, int index) {
return results[index].str();
}), "[]");

return m;
}
}
}
}

#endif /* CHAISCRIPT_EXTRAS_REGEX_HPP_ */
6 changes: 6 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,9 @@ add_executable(string_methods_test string_methods.cpp)
target_link_libraries(string_methods_test ${LIBS})
target_include_directories(string_methods_test PUBLIC "${chaiscript_SOURCE_DIR}/include")
ADD_CATCH_TESTS(string_methods_test)

# Regex
add_executable(regex_test regex.cpp)
target_link_libraries(regex_test ${LIBS})
target_include_directories(regex_test PUBLIC "${chaiscript_SOURCE_DIR}/include")
ADD_CATCH_TESTS(regex_test)
61 changes: 61 additions & 0 deletions tests/regex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <string>
#include <vector>
#include "catch.hpp"

#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript_stdlib.hpp>
#include "../include/chaiscript/extras/regex.hpp"

TEST_CASE( "regex functions work", "[regex]" ) {
// Create the ChaiScript environment with stdlib available.
auto stdlib = chaiscript::Std_Lib::library();
chaiscript::ChaiScript chai(stdlib);

// Add the regex module.
auto regexlib = chaiscript::extras::regex::bootstrap();
chai.add(chaiscript::bootstrap::standard_library::vector_type<std::vector<std::string>>("StringVector"));
chai.add(regexlib);

// regex_search - match anywhere in string
CHECK(chai.eval<bool>("regex_search(\"Hello World 123\", regex(\"[0-9]+\"))") == true);
CHECK(chai.eval<bool>("regex_search(\"Hello World\", regex(\"[0-9]+\"))") == false);

// regex_match - match entire string
CHECK(chai.eval<bool>("regex_match(\"123\", regex(\"[0-9]+\"))") == true);
CHECK(chai.eval<bool>("regex_match(\"Hello 123\", regex(\"[0-9]+\"))") == false);

// regex_replace
CHECK(chai.eval<std::string>("regex_replace(\"Hello World 123\", regex(\"[0-9]+\"), \"456\")") == "Hello World 456");
CHECK(chai.eval<std::string>("regex_replace(\"abc 123 def 456\", regex(\"[0-9]+\"), \"NUM\")") == "abc NUM def NUM");

// smatch - capture groups via regex_search
CHECK(chai.eval<std::string>(R"chai(
var m = smatch()
regex_search("Hello 123 World", regex("([0-9]+)"), m)
m[0]
)chai") == "123");

CHECK(chai.eval<std::string>(R"chai(
var m2 = smatch()
regex_search("2024-01-15", regex("([0-9]{4})-([0-9]{2})-([0-9]{2})"), m2)
m2[1]
)chai") == "2024");

CHECK(chai.eval<std::string>(R"chai(
var m3 = smatch()
regex_search("2024-01-15", regex("([0-9]{4})-([0-9]{2})-([0-9]{2})"), m3)
m3[2]
)chai") == "01");

// smatch size
CHECK(chai.eval<int>(R"chai(
var m4 = smatch()
regex_search("2024-01-15", regex("([0-9]{4})-([0-9]{2})-([0-9]{2})"), m4)
m4.size()
)chai") == 4);

// regex with flags - icase
CHECK(chai.eval<bool>("regex_search(\"HELLO\", regex(\"hello\", regex_icase()))") == true);
CHECK(chai.eval<bool>("regex_search(\"HELLO\", regex(\"hello\"))") == false);
}