Skip to content

Commit 6714671

Browse files
committed
Project initialized.
0 parents  commit 6714671

8 files changed

+240
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/cmake-build-debug
2+
/bin
3+
.idea

CMakeLists.txt

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
CMAKE_MINIMUM_REQUIRED(VERSION 3.28)
2+
PROJECT(Expressions VERSION 1.0.0 LANGUAGES CXX)
3+
4+
SET(CMAKE_CXX_STANDARD 23)
5+
SET(CMAKE_CXX_STANDARD_REQUIRED True)
6+
7+
OPTION(BUILD_TESTS "Build tests" ON)
8+
9+
INCLUDE(FetchContent)
10+
11+
INCLUDE_DIRECTORIES(lib/headers)
12+
13+
FILE(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/lib/sources/*.cpp")
14+
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cc")
15+
16+
ADD_LIBRARY(expressions STATIC ${SOURCES})
17+
18+
TARGET_INCLUDE_DIRECTORIES(expressions PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/lib/headers)
19+
20+
INSTALL(TARGETS expressions DESTINATION lib)
21+
INSTALL(DIRECTORY src/headers/ DESTINATION include)
22+
23+
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
24+
25+
if (BUILD_TESTS)
26+
FETCHCONTENT_DECLARE(
27+
googletest
28+
URL https://github.com/google/googletest/archive/b514bdc898e2951020cbdca1304b75f5950d1f59.zip
29+
)
30+
SET(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
31+
FETCHCONTENT_MAKEAVAILABLE(googletest)
32+
ENABLE_TESTING()
33+
ADD_EXECUTABLE(tests ${TESTS} ${SOURCES})
34+
TARGET_LINK_LIBRARIES(tests GTest::gtest_main)
35+
INCLUDE(GoogleTest)
36+
GTEST_DISCOVER_TESTS(tests)
37+
endif()
38+

lib/headers/expression.hpp

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <vector>
5+
#include <memory>
6+
#include <regex>
7+
8+
#include <expression_result.hpp>
9+
10+
class expression : public std::enable_shared_from_this<expression> {
11+
std::string regex_;
12+
std::vector<std::string> arguments_{};
13+
14+
public:
15+
/**
16+
* Expression constructor
17+
*
18+
* @param regex
19+
* @param arguments
20+
*/
21+
expression(
22+
std::string regex,
23+
std::vector<std::string> arguments
24+
);
25+
26+
/**
27+
* Get the regex
28+
*
29+
* @return std::string
30+
*/
31+
std::string
32+
get_regex() const;
33+
34+
/**
35+
* Get the arguments
36+
*
37+
* @return std::vector<std::string>
38+
*/
39+
std::vector<std::string>
40+
get_arguments() const;
41+
42+
/**
43+
* Query the expression
44+
*
45+
* @param input
46+
* @return
47+
*/
48+
std::shared_ptr<expression_result>
49+
query(const std::string &input);
50+
51+
/**
52+
* Creates a expression from strings
53+
*
54+
* @param input
55+
* @return std::shared_ptr<expression>
56+
*/
57+
static std::shared_ptr<expression>
58+
from_string(const std::string &input);
59+
};

lib/headers/expression_result.hpp

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include <memory>
4+
#include <unordered_map>
5+
6+
class expression_result : public std::enable_shared_from_this<expression_result> {
7+
bool _matches = false;
8+
std::unordered_map<std::string, std::string> _bindings;
9+
10+
public:
11+
/**
12+
* Expression result constructor
13+
*
14+
* @param matches
15+
* @param bindings
16+
*/
17+
expression_result(
18+
bool matches,
19+
std::unordered_map<std::string, std::string> bindings
20+
);
21+
22+
/**
23+
* Get matches
24+
*
25+
* @return
26+
*/
27+
bool
28+
matches() const;
29+
30+
/**
31+
* Get bindings
32+
*
33+
* @return
34+
*/
35+
std::unordered_map<std::string, std::string>
36+
bindings() const;
37+
};

lib/sources/expression.cpp

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include <expression.hpp>
2+
3+
expression::expression(std::string regex, std::vector<std::string> arguments)
4+
: regex_(std::move(regex)), arguments_(std::move(arguments)) {
5+
}
6+
7+
std::string expression::get_regex() const {
8+
return regex_;
9+
}
10+
11+
std::vector<std::string> expression::get_arguments() const {
12+
return arguments_;
13+
}
14+
15+
std::shared_ptr<expression_result> expression::query(const std::string &input) {
16+
std::unordered_map<std::string, std::string> _bindings;
17+
const std::regex _pattern(regex_);
18+
bool _matches = false;
19+
if (std::smatch _match; std::regex_match(input, _match, _pattern)) {
20+
_matches = true;
21+
auto _iterator = _match.begin();
22+
++_iterator;
23+
for (auto &_key: arguments_) {
24+
_bindings[_key] = *_iterator;
25+
++_iterator;
26+
}
27+
}
28+
return std::make_shared<expression_result>(_matches, _bindings);
29+
}
30+
31+
std::shared_ptr<expression> expression::from_string(const std::string &input) {
32+
std::size_t _open = input.find('{');
33+
std::size_t _close = input.find('}');
34+
std::size_t _position = 0;
35+
36+
std::vector<std::string> _arguments;
37+
std::string _regex;
38+
39+
if (_open == std::string::npos && _close == std::string::npos)
40+
return std::make_shared<expression>(std::string{input.data()}, _arguments);
41+
42+
while (_open != std::string::npos && _close != std::string::npos) {
43+
_regex.append(input.substr(_position, _open - _position));
44+
std::string _value{input.substr(_open + 1, _close - _open - 1)};
45+
46+
if (std::find(_arguments.begin(), _arguments.end(), _value) != _arguments.end())
47+
throw std::runtime_error("groups can't be repeated ... ");
48+
49+
_regex.append(R"(([a-zA-Z0-9\-_]+))");
50+
_arguments.emplace_back(_value);
51+
52+
_position = _close + 1;
53+
_open = input.find('{', _close);
54+
_close = input.find('}', _open);
55+
56+
if (_open == std::string::npos && _close == std::string::npos && _position != input.size())
57+
_regex.append(input.substr(_position, input.size() - _position));
58+
}
59+
60+
return std::make_shared<expression>(_regex, _arguments);
61+
}

lib/sources/expression_result.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <expression_result.hpp>
2+
3+
expression_result::expression_result(const bool matches, std::unordered_map<std::string, std::string> bindings)
4+
: _matches(matches), _bindings(std::move(bindings)) {
5+
}
6+
7+
bool expression_result::matches() const {
8+
return _matches;
9+
}
10+
11+
std::unordered_map<std::string, std::string> expression_result::bindings() const {
12+
return _bindings;
13+
}

tests/base_test.cc

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <gtest/gtest.h>
2+
3+
TEST(Base, Assertions) {
4+
EXPECT_EQ(true, true);
5+
EXPECT_NE(true, false);
6+
}

tests/expression_test.cc

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include <gtest/gtest.h>
2+
#include <expression.hpp>
3+
4+
TEST(Expression, Assertions) {
5+
const auto _non_empty_expression = expression::from_string("/users/{user}/details");
6+
ASSERT_FALSE(_non_empty_expression->get_arguments().empty());
7+
ASSERT_EQ(_non_empty_expression->get_arguments().size(), 1);
8+
ASSERT_EQ(_non_empty_expression->get_arguments().at(0), "user");
9+
10+
const auto _empty_expression = expression::from_string("/ping");
11+
ASSERT_TRUE(_empty_expression->get_arguments().empty());
12+
ASSERT_EQ(_empty_expression->get_regex(), "/ping");
13+
14+
const auto _non_empty_string_expression_result = _non_empty_expression->query("/users/80bdc6d1-524e-411a-b316-976a65a3ed3c/details");
15+
ASSERT_TRUE(_non_empty_string_expression_result->matches());
16+
ASSERT_FALSE(_non_empty_string_expression_result->bindings().empty());
17+
ASSERT_EQ(_non_empty_string_expression_result->bindings().at("user"), "80bdc6d1-524e-411a-b316-976a65a3ed3c");
18+
19+
const auto _non_empty_integer_expression_result = _non_empty_expression->query("/users/1337/details");
20+
ASSERT_TRUE(_non_empty_integer_expression_result->matches());
21+
ASSERT_FALSE(_non_empty_integer_expression_result->bindings().empty());
22+
ASSERT_EQ(_non_empty_integer_expression_result->bindings().at("user"), "1337");
23+
}

0 commit comments

Comments
 (0)