Browse Source

:tada: initial project structure

Felix Bytow 2 năm trước cách đây
commit
efaf59962c

+ 85 - 0
.clang-format

@@ -0,0 +1,85 @@
+---
+Language: Cpp
+BasedOnStyle: LLVM
+AccessModifierOffset: -4
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignOperands: AlignAfterOperator
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: false
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortLambdasOnASingleLine: Empty
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+  AfterCaseLabel: true
+  AfterClass: true
+  AfterControlStatement: true
+  AfterEnum: true
+  AfterFunction: true
+  AfterNamespace: false
+  AfterStruct: true
+  AfterUnion: true
+  AfterExternBlock: false
+  BeforeCatch: true
+  BeforeElse: true
+  BeforeLambdaBody: true
+  IndentBraces: false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: false
+  SplitEmptyNamespace: false
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeColon
+ColumnLimit: 120
+CommentPragmas: "^ parasoft"
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ContinuationIndentWidth: 4
+FixNamespaceComments: true
+IncludeBlocks: Preserve
+IncludeCategories:
+  - Regex: '^"[A-Za-z0-9_]+\.(cu)?h(pp)?'
+    Priority: 1
+  - Regex: '^"[A-Za-z0-9_]+(\/[A-Za-z0-9_]+)+\.(cu)?h(pp)?"'
+    Priority: 2
+  - Regex: "^<(cstdlib|csignal|csetjmp|cstdarg|typeinfo|typeindex|type_traits|bitset|functional|utility|ctime|chrono|cstddef|initializer_list|tuple|any|new|memory|scoped_allocator|climits|cfloat|cstdint|cinttypes|limits|exception|stdexcept|cassert|system_error|cerrno|cctype|cwctype|cstring|cwchar|cuchar|string|array|vector|deque|list|forward_list|set|map|unordered_set|unordered_map|stack|queue|iterator|algorithm|cmath|complex|valarray|random|numeric|ratio|cfenv|locale|clocale|codecvt|iosfwd|ios|istream|ostream|iostream|fstream|sstream|strstream|iomanip|streambuf|cstdio|regex|atomic|thread|mutex|shared_mutex|future|condition_variable)>"
+    Priority: 4
+  - Regex: "^<.*>"
+    Priority: 5
+IncludeIsMainRegex: "([-_](test|unittest))?$"
+IndentCaseBlocks: true
+IndentCaseLabels: true
+IndentPPDirectives: None
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: None
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Right
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCtorInitializerColon: false
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: c++14
+TabWidth: 4
+UseTab: Never
+UseCRLF: true

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
.clang-tidy


+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+# CLion
+*.iml
+.idea/
+
+# MacOS
+.DS_Store
+
+# cmake build
+*build*/

+ 26 - 0
CMakeLists.txt

@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.23)
+cmake_policy(VERSION 3.23)
+project(forth_kata)
+
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+include(FetchContent)
+FetchContent_Declare(
+    GoogleTest
+    GIT_REPOSITORY https://github.com/google/googletest.git
+    GIT_TAG 58d77fa8070e8cec2dc1ed015d66b454c8d78850 # release-1.12.1
+)
+FetchContent_Declare(
+    GSL
+    GIT_REPOSITORY https://github.com/microsoft/GSL.git
+    GIT_TAG a3534567187d2edc428efd3f13466ff75fe5805c # v4.0.0
+)
+FetchContent_MakeAvailable(GoogleTest GSL)
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
+include(ProjectHelpers)
+
+enable_testing()
+add_subdirectory(projects)

+ 27 - 0
README.md

@@ -0,0 +1,27 @@
+# Forth Kata
+
+## Instructions
+
+Implement an evaluator for a very simple subset of Forth.
+
+[Forth](https://en.wikipedia.org/wiki/Forth_%28programming_language%29) is a stack-based
+programming language. Implement a very basic evaluator for a small subset of Forth.
+
+Your evaluator has to support the following words:
+
+ * `+`, `-`, `*`, `/` (integer arithmetic)
+ * `DUP`, `DROP`, `SWAP`, `OVER` (stack manipulation)
+
+Your evaluator also has to support defining new words using the customary syntax:
+
+```
+: word-name definition ;
+```
+
+To keep things simple the only data type you need to support is signed integers of at least 16 bits size.
+
+You should use the following rules for the syntax: a number is a sequence of one or more (ASCII) digits,
+a word is a sequence of one or more letters, digits, symbols or punctuation that is not a number.
+(Forth probably uses slightly different rules, but this is close enough.)
+
+Words are case-insensitive.

+ 48 - 0
cmake/ProjectHelpers.cmake

@@ -0,0 +1,48 @@
+set(thisDir "${CMAKE_CURRENT_LIST_DIR}")
+
+function(make_macro_name NAME OUT_VAR)
+  string(STRIP "${NAME}" name)
+  string(TOUPPER "${name}" name)
+  string(REPLACE "-" "_" name "${name}")
+  string(REGEX REPLACE "(^[0-9]+)|[^A-Z_0-9]" "" name "${name}")
+  set(${OUT_VAR} "${name}" PARENT_SCOPE)
+endfunction()
+
+function(add_library_project NAME)
+  set(options SHARED STATIC)
+  set(oneValueArgs)
+  set(multiValueArgs SOURCES TEST_SOURCES DEPENDENCIES)
+  cmake_parse_arguments(PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+  make_macro_name("${NAME}" macroName)
+  set(testDependencies ${PARAM_DEPENDENCIES})
+  list(TRANSFORM testDependencies APPEND _Test)
+
+  # static is the default, but we also allow static it explicitly
+  set(libType STATIC)
+  if (PARAM_SHARED AND PARAM_STATIC)
+    message(FATAL_ERROR "Library project ${NAME} can only be SHARED or STATIC, but not both!")
+  elseif (PARAM_SHARED)
+    set(libType SHARED)
+  endif ()
+
+  add_library("${NAME}" ${libType} ${PARAM_SOURCES})
+  target_link_libraries("${NAME}" PUBLIC ${PARAM_DEPENDENCIES})
+  target_include_directories("${NAME}" PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include")
+
+  if (PARAM_SHARED)
+    target_compile_definitions("${NAME}" PRIVATE -D${macroName}_BUILD_SHARED)
+    target_compile_definitions("${NAME}" INTERFACE -D${macroName}_USE_SHARED)
+  endif ()
+
+  add_library("${NAME}_Test" STATIC ${PARAM_TEST_SOURCES})
+  target_link_libraries("${NAME}_Test" PUBLIC "${NAME}" gtest gmock ${testDependencies})
+
+  add_executable("${NAME}_Tests" ${PARAM_TEST_SOURCES})
+  target_link_libraries("${NAME}_Tests" PRIVATE "${NAME}" gtest gmock gmock_main ${testDependencies})
+  add_test(NAME "${NAME}" COMMAND "${NAME}_Tests")
+endfunction()
+
+function(add_executable_project)
+  # TODO
+endfunction()

+ 7 - 0
cmake/TestMain.cpp

@@ -0,0 +1,7 @@
+#include <gmock/gmock.h>
+
+int main(int argc, char **argv)
+{
+    testing::InitGoogleMock(&argc, argv);
+    return RUN_ALL_TESTS();
+}

+ 1 - 0
projects/CMakeLists.txt

@@ -0,0 +1 @@
+add_subdirectory(forth-kata)

+ 7 - 0
projects/forth-kata/CMakeLists.txt

@@ -0,0 +1,7 @@
+add_library_project(forth-kata STATIC
+    SOURCES
+    src/Token.cpp include/Token.h
+    TEST_SOURCES
+    src/TokenTests.cpp
+    )
+target_link_libraries(forth-kata PUBLIC Microsoft.GSL::GSL)

+ 144 - 0
projects/forth-kata/include/Token.h

@@ -0,0 +1,144 @@
+#ifndef FORTH_KATA_TOKEN_H
+#define FORTH_KATA_TOKEN_H
+
+#include <iosfwd>
+#include <stdexcept>
+#include <string>
+#include <gsl/assert>
+
+namespace forth {
+
+namespace token {
+
+/**
+ * Structure representing a number token (An integer literal).
+ */
+struct Number final
+{
+    int value; ///< The numeric value of the token.
+
+    bool operator==(Number const &rhs) const noexcept;
+
+    bool operator!=(Number const &rhs) const noexcept;
+};
+
+/**
+ * Overloaded output operator for Number tokens.
+ * @param os The output stream.
+ * @param number The number.
+ * @return The output stream.
+ */
+std::ostream &operator<<(std::ostream &os, Number const &number);
+
+/**
+ * Structure representing a keyword token (Special words with fixed meaning).
+ */
+struct Keyword final
+{
+    std::string value;
+
+    bool operator==(Keyword const &rhs) const noexcept;
+
+    bool operator!=(Keyword const &rhs) const noexcept;
+};
+
+/**
+ * Overloaded output operator for Keyword tokens.
+ * @param os The output stream.
+ * @param keyword The keyword.
+ * @return The output stream.
+ */
+std::ostream &operator<<(std::ostream &os, Keyword const &keyword);
+
+/**
+ * Structure representing a word token (Words that are neither keywords nor numbers).
+ */
+struct Word final
+{
+    std::string value;
+
+    bool operator==(Word const &rhs) const noexcept;
+
+    bool operator!=(Word const &rhs) const noexcept;
+};
+
+/**
+ * Overloaded output operator for Word tokens.
+ * @param os The output stream.
+ * @param word The word.
+ * @return The output stream.
+ */
+std::ostream &operator<<(std::ostream &os, Word const &word);
+
+} // namespace token
+
+struct WrongTokenException : std::runtime_error
+{
+    using std::runtime_error::runtime_error;
+    using std::runtime_error::operator=;
+};
+
+class Token final
+{
+    enum class Type
+    {
+        Number,
+        Keyword,
+        Word,
+    };
+
+public:
+    Token(token::Number value) noexcept;
+    Token(token::Keyword value) noexcept;
+    Token(token::Word value) noexcept;
+
+    Token(Token const &src);
+    Token(Token &&src) noexcept;
+
+    Token &operator=(Token const &src);
+    Token &operator=(Token &&src) noexcept;
+
+    ~Token() noexcept;
+
+    bool IsNumber() const noexcept;
+    bool IsKeyword() const noexcept;
+    bool IsWord() const noexcept;
+
+    token::Number const &GetNumber() const;
+    token::Keyword const &GetKeyword() const;
+    token::Word const &GetWord() const;
+
+    bool operator==(Token const &rhs) const noexcept;
+    bool operator!=(Token const &rhs) const noexcept;
+
+private:
+    void destroy() noexcept;
+
+    template<typename TType>
+    void destroy(TType *p)
+    {
+        Expects(p != nullptr);
+        p->~TType();
+    }
+
+    Type type;
+
+    union
+    {
+        token::Number number;
+        token::Keyword keyword;
+        token::Word word;
+    };
+};
+
+/**
+ * Overloaded output operator for Tokens.
+ * @param os The output stream.
+ * @param token The token.
+ * @return The output stream.
+ */
+std::ostream &operator<<(std::ostream &os, Token const &token);
+
+} // namespace forth
+
+#endif // !FORTH_KATA_TOKEN_H

+ 245 - 0
projects/forth-kata/src/Token.cpp

@@ -0,0 +1,245 @@
+#include "Token.h"
+
+#include <ostream>
+#include <utility>
+
+namespace forth {
+
+namespace token {
+
+std::ostream &operator<<(std::ostream &os, Number const &number)
+{
+    return os << "Number[" << number.value << "]";
+}
+
+std::ostream &operator<<(std::ostream &os, Keyword const &keyword)
+{
+    return os << "Keyword[\"" << keyword.value << "\"]";
+}
+
+std::ostream &operator<<(std::ostream &os, Word const &word)
+{
+    return os << "Word[\"" << word.value << "\"]";
+}
+
+bool Number::operator==(Number const &rhs) const noexcept
+{
+    return value == rhs.value;
+}
+
+bool Number::operator!=(Number const &rhs) const noexcept
+{
+    return !operator==(rhs);
+}
+
+bool Keyword::operator==(Keyword const &rhs) const noexcept
+{
+    return value == rhs.value;
+}
+
+bool Keyword::operator!=(Keyword const &rhs) const noexcept
+{
+    return !operator==(rhs);
+}
+
+bool Word::operator==(Word const &rhs) const noexcept
+{
+    return value == rhs.value;
+}
+
+bool Word::operator!=(Word const &rhs) const noexcept
+{
+    return !operator==(rhs);
+}
+
+} // namespace token
+
+Token::Token(token::Number value) noexcept: type{Type::Number}
+{
+    number = value;
+}
+
+Token::Token(token::Keyword value) noexcept: type{Type::Keyword}
+{
+    keyword = std::move(value);
+}
+
+Token::Token(token::Word value) noexcept: type{Type::Word}
+{
+    word = std::move(value);
+}
+
+Token::Token(Token const &src): type{src.type}
+{
+    switch (type)
+    {
+        case Type::Number:
+            number = src.number;
+            break;
+        case Type::Keyword:
+            keyword = src.keyword;
+            break;
+        case Type::Word:
+            word = src.word;
+    }
+}
+
+Token::Token(Token &&src) noexcept: type{src.type}
+{
+
+    switch (type)
+    {
+        case Type::Number:
+            number = src.number;
+            break;
+        case Type::Keyword:
+            keyword = std::move(src.keyword);
+            break;
+        case Type::Word:
+            word = std::move(src.word);
+    }
+}
+void Token::destroy() noexcept
+{
+    switch (type)
+    {
+        case Type::Number:
+            destroy(&number);
+            break;
+        case Type::Keyword:
+            destroy(&keyword);
+            break;
+        case Type::Word:
+            destroy(&word);
+    }
+}
+
+Token &Token::operator=(Token const &src)
+{
+    if (std::addressof(src) != this)
+    {
+        destroy();
+        type = src.type;
+        switch (type)
+        {
+            case Type::Number:
+                number = src.number;
+                break;
+            case Type::Keyword:
+                keyword = src.keyword;
+                break;
+            case Type::Word:
+                word = src.word;
+        }
+    }
+    return *this;
+}
+
+Token &Token::operator=(Token &&src) noexcept
+{
+    if (std::addressof(src) != this)
+    {
+        destroy();
+        type = src.type;
+        switch (type)
+        {
+            case Type::Number:
+                number = src.number;
+                break;
+            case Type::Keyword:
+                keyword = std::move(src.keyword);
+                break;
+            case Type::Word:
+                word = std::move(src.word);
+        }
+    }
+    return *this;
+}
+
+Token::~Token() noexcept
+{
+    destroy();
+}
+
+bool Token::IsNumber() const noexcept
+{
+    return type == Type::Number;
+}
+
+bool Token::IsKeyword() const noexcept
+{
+    return type == Type::Keyword;
+}
+
+bool Token::IsWord() const noexcept
+{
+    return type == Type::Word;
+}
+
+token::Number const &Token::GetNumber() const
+{
+    if (!IsNumber())
+    {
+        throw WrongTokenException{"Token is not a Number"};
+    }
+    return number;
+}
+
+token::Keyword const &Token::GetKeyword() const
+{
+    if (!IsKeyword())
+    {
+        throw WrongTokenException{"Token is not a Keyword"};
+    }
+    return keyword;
+}
+
+token::Word const &Token::GetWord() const
+{
+    if (!IsWord())
+    {
+        throw WrongTokenException{"Token is not a Word"};
+    }
+    return word;
+}
+
+bool Token::operator==(Token const &rhs) const noexcept
+{
+    return !operator!=(rhs);
+}
+
+bool Token::operator!=(Token const &rhs) const noexcept
+{
+    if (type != rhs.type)
+    {
+        return true;
+    }
+
+    switch (type)
+    {
+        case Type::Number:
+            return number != rhs.number;
+        case Type::Keyword:
+            return keyword != rhs.keyword;
+        case Type::Word:
+            return word != rhs.word;
+    }
+}
+
+std::ostream &operator<<(std::ostream &os, Token const &token)
+{
+    if (token.IsNumber())
+    {
+        return os << token.GetNumber();
+    }
+    else if (token.IsKeyword())
+    {
+        return os << token.GetKeyword();
+    }
+    else
+    {
+        return os << token.GetWord();
+    }
+}
+
+} // namespace forth

+ 143 - 0
projects/forth-kata/src/TokenTests.cpp

@@ -0,0 +1,143 @@
+#include "Token.h"
+
+#include <gtest/gtest.h>
+
+#include <sstream>
+#include <string>
+using namespace std::literals::string_literals;
+
+namespace forth {
+
+TEST(TokenTests, NumberToken)
+{
+    auto const testValue = 42;
+
+    Token const tok{token::Number{testValue}};
+    EXPECT_TRUE(tok.IsNumber());
+    EXPECT_FALSE(tok.IsKeyword());
+    EXPECT_FALSE(tok.IsWord());
+
+    EXPECT_EQ(tok.GetNumber().value, testValue);
+    EXPECT_THROW(tok.GetKeyword(), WrongTokenException);
+    EXPECT_THROW(tok.GetWord(), WrongTokenException);
+
+    Token const tok2{tok};
+    EXPECT_EQ(tok, tok2);
+
+    std::ostringstream strm;
+    strm << tok;
+    EXPECT_EQ(strm.str(), "Number[42]"s);
+}
+
+TEST(TokenTests, KeywordToken)
+{
+    auto const testValue = ":"s;
+
+    Token const tok{token::Keyword{testValue}};
+    EXPECT_FALSE(tok.IsNumber());
+    EXPECT_TRUE(tok.IsKeyword());
+    EXPECT_FALSE(tok.IsWord());
+
+    EXPECT_THROW(tok.GetNumber(), WrongTokenException);
+    EXPECT_EQ(tok.GetKeyword().value, testValue);
+    EXPECT_THROW(tok.GetWord(), WrongTokenException);
+
+    Token const tok2{tok};
+    EXPECT_EQ(tok, tok2);
+
+    std::ostringstream strm;
+    strm << tok;
+    EXPECT_EQ(strm.str(), "Keyword[\":\"]"s);
+}
+
+TEST(TokenTests, WordToken)
+{
+    auto const testValue = "DUP"s;
+
+    Token const tok{token::Word{testValue}};
+    EXPECT_FALSE(tok.IsNumber());
+    EXPECT_FALSE(tok.IsKeyword());
+    EXPECT_TRUE(tok.IsWord());
+
+    EXPECT_THROW(tok.GetNumber(), WrongTokenException);
+    EXPECT_THROW(tok.GetKeyword(), WrongTokenException);
+    EXPECT_EQ(tok.GetWord().value, testValue);
+
+    Token const tok2{tok};
+    EXPECT_EQ(tok, tok2);
+
+    std::ostringstream strm;
+    strm << tok;
+    EXPECT_EQ(strm.str(), "Word[\"DUP\"]"s);
+}
+
+TEST(TokenTests, SelfAssignments)
+{
+    auto const testValue = 42;
+
+    Token tok{token::Number{testValue}};
+    tok = tok;
+    EXPECT_EQ(tok.GetNumber().value, testValue);
+    tok = std::move(tok);
+    EXPECT_EQ(tok.GetNumber().value, testValue);
+}
+
+TEST(TokenTests, MoveConstructions)
+{
+    auto const numVal = 42;
+    Token numTok{token::Number{numVal}};
+    Token const numTok2{std::move(numTok)};
+    EXPECT_EQ(numTok, numTok2);
+
+    auto const keywordVal = ":"s;
+    Token keywordTok{token::Keyword{keywordVal}};
+    Token const keywordTok2{std::move(keywordTok)};
+    EXPECT_EQ(keywordTok.GetKeyword().value, ""s);
+    EXPECT_EQ(keywordTok2.GetKeyword().value, keywordVal);
+
+    auto const wordVal = "DUP"s;
+    Token wordTok{token::Word{wordVal}};
+    Token const wordTok2{std::move(wordTok)};
+    EXPECT_EQ(wordTok.GetWord().value, ""s);
+    EXPECT_EQ(wordTok2.GetWord().value, wordVal);
+}
+
+TEST(TokenTests, Assignments)
+{
+    Token target{token::Number{0}};
+
+    auto const numVal = 42;
+    Token numTok{token::Number{numVal}};
+    target = numTok;
+    EXPECT_EQ(target, numTok);
+    target = std::move(numTok);
+    EXPECT_EQ(target, numTok);
+
+    auto const keywordVal = ":"s;
+    Token keywordTok{token::Keyword{keywordVal}};
+    target = keywordTok;
+    EXPECT_EQ(target, keywordTok);
+    target = std::move(keywordTok);
+    EXPECT_EQ(keywordTok.GetKeyword().value, ""s);
+    EXPECT_EQ(target.GetKeyword().value, keywordVal);
+
+    auto const wordVal = "DUP"s;
+    Token wordTok{token::Word{wordVal}};
+    target = wordTok;
+    EXPECT_EQ(target, wordTok);
+    target = std::move(wordTok);
+    EXPECT_EQ(wordTok.GetWord().value, ""s);
+    EXPECT_EQ(target.GetWord().value, wordVal);
+}
+
+TEST(TokenTests, DifferentTypeComparison)
+{
+    Token const numTok{token::Number{42}};
+    Token const keywordTok{token::Keyword{":"s}};
+    Token const wordTok{token::Word{"DUP"s}};
+    EXPECT_NE(numTok, keywordTok);
+    EXPECT_NE(keywordTok, wordTok);
+    EXPECT_NE(wordTok, numTok);
+}
+
+} // namespace forth

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác