From 07a116a467d3d5075ef22ff9d2047d1e5208c184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=9E=E6=A5=BD=E5=9D=82=E5=B8=95=E7=90=AA?= Date: Tue, 17 Nov 2020 20:15:11 +0800 Subject: [PATCH] Dev (#1) version 0.1.0 --- .clang-format | 9 + CMakeLists.txt | 65 ++++ README.md | 115 +++++++ cablin_examples/test.yaml | 66 ++++ cablin_examples/test2.yaml | 39 +++ cablin_examples/test3.yaml | 41 +++ cablin_examples/test4.yaml | 24 ++ cablin_examples/test5-1.yaml | 16 + cablin_examples/test5.yaml | 34 ++ cablin_examples/test6.yaml | 33 ++ cablin_examples/test7.yaml | 21 ++ cablin_examples/test8.yaml | 14 + cablin_examples/test9.yaml | 16 + example/CMakeLists.txt | 11 + example/example1.cpp | 24 ++ example/example2.cpp | 77 +++++ include/mukyu/cablin/command/assign.hpp | 33 ++ include/mukyu/cablin/command/block.hpp | 33 ++ include/mukyu/cablin/command/call.hpp | 35 ++ include/mukyu/cablin/command/factory.hpp | 25 ++ include/mukyu/cablin/command/flow.hpp | 44 +++ include/mukyu/cablin/command/globalvar.hpp | 34 ++ include/mukyu/cablin/command/if.hpp | 33 ++ include/mukyu/cablin/command/loopflow.hpp | 39 +++ include/mukyu/cablin/command/return.hpp | 38 +++ include/mukyu/cablin/command/var.hpp | 34 ++ include/mukyu/cablin/command/while.hpp | 33 ++ include/mukyu/cablin/common/defer.hpp | 40 +++ include/mukyu/cablin/common/string.hpp | 30 ++ include/mukyu/cablin/common/yamlutil.hpp | 54 +++ include/mukyu/cablin/core/command.hpp | 27 ++ include/mukyu/cablin/core/controller.hpp | 56 ++++ include/mukyu/cablin/core/error.hpp | 48 +++ include/mukyu/cablin/core/expr.hpp | 27 ++ include/mukyu/cablin/core/function.hpp | 32 ++ include/mukyu/cablin/core/package.hpp | 33 ++ include/mukyu/cablin/core/script.hpp | 43 +++ include/mukyu/cablin/core/value.hpp | 236 +++++++++++++ include/mukyu/cablin/expr/call.hpp | 35 ++ include/mukyu/cablin/expr/const.hpp | 42 +++ include/mukyu/cablin/expr/factory.hpp | 19 ++ include/mukyu/cablin/expr/getvar.hpp | 34 ++ .../mukyu/cablin/function/binary_operator.hpp | 125 +++++++ include/mukyu/cablin/function/cast.hpp | 86 +++++ include/mukyu/cablin/function/functor.hpp | 44 +++ include/mukyu/cablin/function/node.hpp | 39 +++ include/mukyu/cablin/function/print.hpp | 36 ++ .../mukyu/cablin/function/unary_operator.hpp | 72 ++++ include/mukyu/cablin/package/builtin_cast.hpp | 28 ++ include/mukyu/cablin/package/builtin_io.hpp | 28 ++ include/mukyu/cablin/package/builtin_op.hpp | 28 ++ include/mukyu/cablin/package/factory.hpp | 20 ++ include/mukyu/cablin/package/userdefine.hpp | 34 ++ interpreter/CMakeLists.txt | 7 + interpreter/main.cpp | 23 ++ src/command/assign.cpp | 68 ++++ src/command/block.cpp | 52 +++ src/command/call.cpp | 71 ++++ src/command/factory.cpp | 82 +++++ src/command/globalvar.cpp | 57 ++++ src/command/if.cpp | 62 ++++ src/command/var.cpp | 54 +++ src/command/while.cpp | 63 ++++ src/core/controller.cpp | 312 ++++++++++++++++++ src/core/script.cpp | 148 +++++++++ src/expr/call.cpp | 44 +++ src/expr/factory.cpp | 49 +++ src/expr/getvar.cpp | 45 +++ src/function/node.cpp | 120 +++++++ src/function/print.cpp | 85 +++++ src/package/builtin_cast.cpp | 55 +++ src/package/builtin_io.cpp | 53 +++ src/package/builtin_op.cpp | 67 ++++ src/package/factory.cpp | 46 +++ src/package/userdefine.cpp | 119 +++++++ test/CMakeLists.txt | 29 ++ test/src/command/test_if.cpp | 62 ++++ test/src/command/test_scope.cpp | 43 +++ test/src/command/test_var.cpp | 41 +++ test/src/command/test_while.cpp | 58 ++++ test/src/expr/test_const.cpp | 31 ++ test/src/function/test_functor.cpp | 26 ++ test/src/function/test_node.cpp | 89 +++++ 83 files changed, 4343 insertions(+) create mode 100644 .clang-format create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 cablin_examples/test.yaml create mode 100644 cablin_examples/test2.yaml create mode 100644 cablin_examples/test3.yaml create mode 100644 cablin_examples/test4.yaml create mode 100644 cablin_examples/test5-1.yaml create mode 100644 cablin_examples/test5.yaml create mode 100644 cablin_examples/test6.yaml create mode 100644 cablin_examples/test7.yaml create mode 100644 cablin_examples/test8.yaml create mode 100644 cablin_examples/test9.yaml create mode 100644 example/CMakeLists.txt create mode 100644 example/example1.cpp create mode 100644 example/example2.cpp create mode 100644 include/mukyu/cablin/command/assign.hpp create mode 100644 include/mukyu/cablin/command/block.hpp create mode 100644 include/mukyu/cablin/command/call.hpp create mode 100644 include/mukyu/cablin/command/factory.hpp create mode 100644 include/mukyu/cablin/command/flow.hpp create mode 100644 include/mukyu/cablin/command/globalvar.hpp create mode 100644 include/mukyu/cablin/command/if.hpp create mode 100644 include/mukyu/cablin/command/loopflow.hpp create mode 100644 include/mukyu/cablin/command/return.hpp create mode 100644 include/mukyu/cablin/command/var.hpp create mode 100644 include/mukyu/cablin/command/while.hpp create mode 100644 include/mukyu/cablin/common/defer.hpp create mode 100644 include/mukyu/cablin/common/string.hpp create mode 100644 include/mukyu/cablin/common/yamlutil.hpp create mode 100644 include/mukyu/cablin/core/command.hpp create mode 100644 include/mukyu/cablin/core/controller.hpp create mode 100644 include/mukyu/cablin/core/error.hpp create mode 100644 include/mukyu/cablin/core/expr.hpp create mode 100644 include/mukyu/cablin/core/function.hpp create mode 100644 include/mukyu/cablin/core/package.hpp create mode 100644 include/mukyu/cablin/core/script.hpp create mode 100644 include/mukyu/cablin/core/value.hpp create mode 100644 include/mukyu/cablin/expr/call.hpp create mode 100644 include/mukyu/cablin/expr/const.hpp create mode 100644 include/mukyu/cablin/expr/factory.hpp create mode 100644 include/mukyu/cablin/expr/getvar.hpp create mode 100644 include/mukyu/cablin/function/binary_operator.hpp create mode 100644 include/mukyu/cablin/function/cast.hpp create mode 100644 include/mukyu/cablin/function/functor.hpp create mode 100644 include/mukyu/cablin/function/node.hpp create mode 100644 include/mukyu/cablin/function/print.hpp create mode 100644 include/mukyu/cablin/function/unary_operator.hpp create mode 100644 include/mukyu/cablin/package/builtin_cast.hpp create mode 100644 include/mukyu/cablin/package/builtin_io.hpp create mode 100644 include/mukyu/cablin/package/builtin_op.hpp create mode 100644 include/mukyu/cablin/package/factory.hpp create mode 100644 include/mukyu/cablin/package/userdefine.hpp create mode 100644 interpreter/CMakeLists.txt create mode 100644 interpreter/main.cpp create mode 100644 src/command/assign.cpp create mode 100644 src/command/block.cpp create mode 100644 src/command/call.cpp create mode 100644 src/command/factory.cpp create mode 100644 src/command/globalvar.cpp create mode 100644 src/command/if.cpp create mode 100644 src/command/var.cpp create mode 100644 src/command/while.cpp create mode 100644 src/core/controller.cpp create mode 100644 src/core/script.cpp create mode 100644 src/expr/call.cpp create mode 100644 src/expr/factory.cpp create mode 100644 src/expr/getvar.cpp create mode 100644 src/function/node.cpp create mode 100644 src/function/print.cpp create mode 100644 src/package/builtin_cast.cpp create mode 100644 src/package/builtin_io.cpp create mode 100644 src/package/builtin_op.cpp create mode 100644 src/package/factory.cpp create mode 100644 src/package/userdefine.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/src/command/test_if.cpp create mode 100644 test/src/command/test_scope.cpp create mode 100644 test/src/command/test_var.cpp create mode 100644 test/src/command/test_while.cpp create mode 100644 test/src/expr/test_const.cpp create mode 100644 test/src/function/test_functor.cpp create mode 100644 test/src/function/test_node.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..d47e80a --- /dev/null +++ b/.clang-format @@ -0,0 +1,9 @@ +BasedOnStyle: Google +IndentWidth: 4 +AllowShortFunctionsOnASingleLine: None + +AccessModifierOffset: -4 +AlignTrailingComments: true +MaxEmptyLinesToKeep: 2 + +IndentCaseLabels: false \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..eb5e449 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.6.2) +project(mukyu_cablin) + +find_package(yaml-cpp REQUIRED) + +include_directories(${YAML_CPP_INCLUDE_DIRS}) + +include_directories(include) + +set(CMAKE_CXX_STANDARD 17) + +set(cablin_core_src + src/core/controller.cpp + "src/core/script.cpp") + +set(cablin_command_src + "src/command/factory.cpp" + "src/command/if.cpp" + "src/command/block.cpp" + "src/command/call.cpp" + "src/command/var.cpp" + "src/command/globalvar.cpp" + "src/command/while.cpp" + "src/command/assign.cpp") + +set(cablin_function_src + src/function/print.cpp + src/function/node.cpp) + +set(cablin_expr_src + src/expr/call.cpp + src/expr/getvar.cpp + src/expr/factory.cpp) + +set(cablin_package_src + src/package/builtin_io.cpp + src/package/builtin_op.cpp + src/package/builtin_cast.cpp + src/package/factory.cpp + src/package/userdefine.cpp) + +add_library(cablin + ${cablin_core_src} + ${cablin_command_src} + ${cablin_function_src} + ${cablin_package_src} + ${cablin_expr_src}) + +target_link_libraries(cablin + ${YAML_CPP_LIBRARIES}) + +# ---------- +# Interpreter + +add_subdirectory(interpreter) + +# ---------- +# Example + +add_subdirectory(example) + +# ---------- +# Unit Test + +add_subdirectory(test) diff --git a/README.md b/README.md new file mode 100644 index 0000000..9619902 --- /dev/null +++ b/README.md @@ -0,0 +1,115 @@ +# Cablin README + +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) + + +A Cablin interpreter written in c-plus-plus. + +## How to build and execute examples + +```bash +# Build +mkdir build +cd build +cmake .. + +# Execute interpreter +interpreter/interpreter ../cablin_examples/test.yaml + +# Execute example +example/example1 +example/example2 + +``` + +## Use Cablin as an Embedding Script + +### Hello world + +Load the script and execute the `hello_world` function defined in script. + +```cpp +#include + +namespace mccore = mukyu::cablin::core; + +int main(int argc, char** argv) { + std::string body = R"( +- import: io + +- func: + name: hello_world + body: + - call: + name: io::print + params: + - const: + type: string + value: Hello world! +)"; + + auto node = YAML::Load(body); + mccore::Script script(node); + script.callFunction("hello_world", {}); + return 0; +} +``` + +### Call c++ function from Cablin script + +[➡️ Complete code](example/example2.cpp) + +```cpp +namespace mccore = mukyu::cablin::core; +namespace mcfunc = mukyu::cablin::function; + +const std::string MYPACKAGE_NAME = "my"; + +class MyPackage : public mccore::Package { +public: + MyPackage() = default; + ~MyPackage() = default; + + // The prepare function register all the function and variable need in package + void prepare(mccore::Controller* controller) { + // Register package name + controller->addPackage(MYPACKAGE_NAME); + + auto printFunc = [](mccore::ValueList params) { + // ... + }; + + // Register print function + controller->addFunction( + MYPACKAGE_NAME, + std::make_shared("print", printFunc)); + } +}; + + +int main(int argc, char** argv) { + std::string body = R"( +- import: my + +- func: + name: hello_world + body: + - call: + name: my::print + params: + - const: + type: string + value: Hello world call from script! +)"; + + auto node = YAML::Load(body); + + // Prepare self-define package + mccore::Script::PackagePtrMap packages = { + {MYPACKAGE_NAME, std::make_shared()}}; + + mccore::Script script(node, packages); + script.callFunction("hello_world", {}); + return 0; +} +``` \ No newline at end of file diff --git a/cablin_examples/test.yaml b/cablin_examples/test.yaml new file mode 100644 index 0000000..144ea8f --- /dev/null +++ b/cablin_examples/test.yaml @@ -0,0 +1,66 @@ +- import: op +- import: io + +- func: + name: main + body: + # var a = 1 + - var: + type: int + name: a + default_value: 1 + + # var b = 2 + - var: + type: int + name: b + default_value: 2 + + # print(a) + - call: + name: io::print + params: + - get: a + + # print(b) + - call: + name: io::print + params: + - get: b + + # var c + - var: + type: bool + name: c + default_value: false + + # a = a + b + - assign: + target: a + source: + # should be command:call + call: + name: op::plus + params: + - get: a + - get: b + + # print(a) + - call: + name: io::print + params: + - get: a + + - if: + condition: + get: c + then: + - call: + name: io::print + params: + - get: a + else: + - call: + name: io::print + params: + - get: b diff --git a/cablin_examples/test2.yaml b/cablin_examples/test2.yaml new file mode 100644 index 0000000..2877788 --- /dev/null +++ b/cablin_examples/test2.yaml @@ -0,0 +1,39 @@ +- import: io +- import: op + +- func: + name: main + body: + # var a = 10 + - var: + type: int + name: a + default_value: 10 + + - while: + condition: + call: + name: op::greater + params: + - get: a + - const: + type: int + value: 1 + body: + # print(a) + - call: + name: io::print + params: + - get: a + + # a = a - one + - assign: + target: a + source: + call: + name: op::minus + params: + - get: a + - const: + type: int + value: 1 diff --git a/cablin_examples/test3.yaml b/cablin_examples/test3.yaml new file mode 100644 index 0000000..bddb7a7 --- /dev/null +++ b/cablin_examples/test3.yaml @@ -0,0 +1,41 @@ +- import: op +- import: io + +- func: + name: main + body: + # var a = 10 + - var: + type: int + name: a + default_value: 10 + + - while: + condition: + call: + name: op::greater + params: + - get: a + - const: + type: int + value: 1 + body: + # print(a) + - call: + name: io::print + params: + - get: a + + # a = a - one + - assign: + target: a + source: + call: + name: op::minus + params: + - get: a + - const: + type: int + value: 1 + + - break: diff --git a/cablin_examples/test4.yaml b/cablin_examples/test4.yaml new file mode 100644 index 0000000..b578879 --- /dev/null +++ b/cablin_examples/test4.yaml @@ -0,0 +1,24 @@ +- import: io + +- func: + name: myFunc + params: + - name: a + type: int + body: + - call: + name: io::print + params: + - get: a + +- func: + name: main + body: + # myFunc(10) + - call: + name: myFunc + params: + - const: + type: int + value: 10 + diff --git a/cablin_examples/test5-1.yaml b/cablin_examples/test5-1.yaml new file mode 100644 index 0000000..c6177ed --- /dev/null +++ b/cablin_examples/test5-1.yaml @@ -0,0 +1,16 @@ +- import: test5 + +- func: + name: main + body: + # var a = 10 + - var: + type: int + name: a + default_value: 30 + + # test5::myFunc(a) + - call: + name: test5::myFunc + params: + - get: a diff --git a/cablin_examples/test5.yaml b/cablin_examples/test5.yaml new file mode 100644 index 0000000..103df5d --- /dev/null +++ b/cablin_examples/test5.yaml @@ -0,0 +1,34 @@ +- import: io + +- var: + type: int + name: b + default_value: 10 + +- func: + name: myFunc + params: + - name: a + type: int + body: + - call: + name: io::print + params: + - get: a + - get: b + +- func: + name: main + body: + # var a = 10 + - var: + type: int + name: a + default_value: 10 + + # myFunc(a) + - call: + name: myFunc + params: + - get: a + diff --git a/cablin_examples/test6.yaml b/cablin_examples/test6.yaml new file mode 100644 index 0000000..cc96fd1 --- /dev/null +++ b/cablin_examples/test6.yaml @@ -0,0 +1,33 @@ +- import: io + +- func: + name: myFunc + body: + - var: + name: a + type: int + default_value: 50 + + - return: + get: a + +- func: + name: main + body: + # var a = 10 + - var: + type: int + name: a + default_value: 10 + + # myFunc(a) + - assign: + target: a + source: + call: + name: myFunc + + - call: + name: io::print + params: + - get: a diff --git a/cablin_examples/test7.yaml b/cablin_examples/test7.yaml new file mode 100644 index 0000000..ccb5a3f --- /dev/null +++ b/cablin_examples/test7.yaml @@ -0,0 +1,21 @@ +- import: io + +- func: + name: main + body: + - if: + condition: + const: + type: bool + value: true + then: + - var: + type: int + name: b + default_value: 234 + + # should throw exception: b not found + - call: + name: io::print + params: + - get: b diff --git a/cablin_examples/test8.yaml b/cablin_examples/test8.yaml new file mode 100644 index 0000000..4bef066 --- /dev/null +++ b/cablin_examples/test8.yaml @@ -0,0 +1,14 @@ +- import: io + +- func: + name: main + body: + - var: + type: string + name: a + default_value: 1哈哈1 + + - call: + name: io::print + params: + - get: a diff --git a/cablin_examples/test9.yaml b/cablin_examples/test9.yaml new file mode 100644 index 0000000..a47cdfc --- /dev/null +++ b/cablin_examples/test9.yaml @@ -0,0 +1,16 @@ +- import: io + +- func: + name: main + body: + # Parse Error + - var1: 1 + var: + type: string + name: a + default_value: 1哈哈1 + + - call: + name: io::print + params: + - get: a diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..08e3cc3 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.6.2) + +include_directories(../include/) + +add_executable(example1 example1.cpp) +add_dependencies(example1 cablin) +target_link_libraries(example1 cablin) + +add_executable(example2 example2.cpp) +add_dependencies(example2 cablin) +target_link_libraries(example2 cablin) \ No newline at end of file diff --git a/example/example1.cpp b/example/example1.cpp new file mode 100644 index 0000000..dfbed55 --- /dev/null +++ b/example/example1.cpp @@ -0,0 +1,24 @@ +#include + +namespace mccore = mukyu::cablin::core; + +int main(int argc, char** argv) { + std::string body = R"( +- import: io + +- func: + name: hello_world + body: + - call: + name: io::print + params: + - const: + type: string + value: Hello world! +)"; + + auto node = YAML::Load(body); + mccore::Script script(node); + script.callFunction("hello_world", {}); + return 0; +} \ No newline at end of file diff --git a/example/example2.cpp b/example/example2.cpp new file mode 100644 index 0000000..f2b8cdc --- /dev/null +++ b/example/example2.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +#include + +namespace mccore = mukyu::cablin::core; +namespace mcfunc = mukyu::cablin::function; + +const std::string MYPACKAGE_NAME = "my"; + +class MyPackage : public mccore::Package { +public: + MyPackage() = default; + ~MyPackage() = default; + + void prepare(mccore::Controller* controller) { + controller->addPackage(MYPACKAGE_NAME); + + auto printFunc = [](mccore::ValueList params) { + for (const auto& param : params) { + switch (param.type()) { + case mccore::ValueType::BOOL: + if (param.as()) { + std::cout << "true\n"; + } else { + std::cout << "false\n"; + } + break; + case mccore::ValueType::INT: + std::cout << param.as() << "\n"; + break; + case mccore::ValueType::STRING: + std::cout << param.as() << "\n"; + break; + case mccore::ValueType::NONE: + std::cout << "none\n"; + break; + default: + std::cout << "??\n"; + } + } + return 0; + }; + controller->addFunction( + MYPACKAGE_NAME, + std::make_shared("print", printFunc)); + } +}; + + +int main(int argc, char** argv) { + std::string body = R"( +- import: my + +- func: + name: hello_world + body: + - call: + name: my::print + params: + - const: + type: string + value: Hello world call from script! +)"; + + auto node = YAML::Load(body); + + mccore::Script::PackagePtrMap packages = { + {MYPACKAGE_NAME, std::make_shared()}}; + + mccore::Script script(node, packages); + script.callFunction("hello_world", {}); + return 0; +} \ No newline at end of file diff --git a/include/mukyu/cablin/command/assign.hpp b/include/mukyu/cablin/command/assign.hpp new file mode 100644 index 0000000..eb13119 --- /dev/null +++ b/include/mukyu/cablin/command/assign.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDASSIGN_KEY = "assign"; + +class CommandAssign : public mukyu::cablin::core::Command { +public: + CommandAssign(const std::string& package, const YAML::Node& node); + ~CommandAssign(); + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/block.hpp b/include/mukyu/cablin/command/block.hpp new file mode 100644 index 0000000..5e73e4b --- /dev/null +++ b/include/mukyu/cablin/command/block.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDBLOCK_KEY = "block"; + +class CommandBlock : public mukyu::cablin::core::Command { +public: + CommandBlock(const std::string& package, const YAML::Node& node); + ~CommandBlock(); + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/call.hpp b/include/mukyu/cablin/command/call.hpp new file mode 100644 index 0000000..4a48297 --- /dev/null +++ b/include/mukyu/cablin/command/call.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDCALL_KEY = "call"; + +class CommandCall : public mukyu::cablin::core::Command { +public: + CommandCall(const std::string& package, const YAML::Node& node); + + ~CommandCall(); + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/factory.hpp b/include/mukyu/cablin/command/factory.hpp new file mode 100644 index 0000000..7fa280d --- /dev/null +++ b/include/mukyu/cablin/command/factory.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +mukyu::cablin::core::CommandPtr createCommand(const std::string& package, + const YAML::Node& node); + +std::vector createCommandList( + const std::string& package, const YAML::Node& node); + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/flow.hpp b/include/mukyu/cablin/command/flow.hpp new file mode 100644 index 0000000..a9a33a7 --- /dev/null +++ b/include/mukyu/cablin/command/flow.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +class LoopFlowControlException : public std::exception { +public: + LoopFlowControlException() = default; + virtual ~LoopFlowControlException() = default; +}; + +class BreakException : public LoopFlowControlException { +public: + BreakException() = default; + ~BreakException() = default; +}; + +class ContinueException : public LoopFlowControlException { +public: + ContinueException() = default; + ~ContinueException() = default; +}; + +class ReturnException : public std::exception { +public: + mukyu::cablin::core::Value returnValue; + + ReturnException(mukyu::cablin::core::Value value) : returnValue(value) { + } + + ~ReturnException() = default; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/globalvar.hpp b/include/mukyu/cablin/command/globalvar.hpp new file mode 100644 index 0000000..acbd0df --- /dev/null +++ b/include/mukyu/cablin/command/globalvar.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDGLOBALVAR_KEY = "var"; + +class CommandGlobalVar : public mukyu::cablin::core::Command { +public: + CommandGlobalVar(const std::string& package, const YAML::Node& node); + + ~CommandGlobalVar(); + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/if.hpp b/include/mukyu/cablin/command/if.hpp new file mode 100644 index 0000000..6b478f0 --- /dev/null +++ b/include/mukyu/cablin/command/if.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDIF_KEY = "if"; + +class CommandIf : public mukyu::cablin::core::Command { +public: + CommandIf(const std::string& package, const YAML::Node& node); + ~CommandIf(); + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/loopflow.hpp b/include/mukyu/cablin/command/loopflow.hpp new file mode 100644 index 0000000..6539914 --- /dev/null +++ b/include/mukyu/cablin/command/loopflow.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDBREAK_KEY = "break"; + +class CommandBreak : public mukyu::cablin::core::Command { +public: + CommandBreak() = default; + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller) { + throw BreakException(); + } +}; + +const std::string COMMANDCONTINUE_KEY = "continue"; + +class CommandContinue : public mukyu::cablin::core::Command { +public: + CommandContinue() = default; + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller) { + throw ContinueException(); + } +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/return.hpp b/include/mukyu/cablin/command/return.hpp new file mode 100644 index 0000000..e887c55 --- /dev/null +++ b/include/mukyu/cablin/command/return.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDRETURN_KEY = "return"; + +class CommandReturn : public mukyu::cablin::core::Command { +public: + CommandReturn(const std::string& package, const YAML::Node& node) + : expr_(mukyu::cablin::expr::createExpr(package, node)) { + } + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller) { + throw ReturnException(expr_->compute(controller)); + } + +private: + mukyu::cablin::core::ExprPtr expr_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/var.hpp b/include/mukyu/cablin/command/var.hpp new file mode 100644 index 0000000..d07508c --- /dev/null +++ b/include/mukyu/cablin/command/var.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDVAR_KEY = "var"; + +class CommandVar : public mukyu::cablin::core::Command { +public: + CommandVar(const YAML::Node& node); + + ~CommandVar(); + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/command/while.hpp b/include/mukyu/cablin/command/while.hpp new file mode 100644 index 0000000..0199b1a --- /dev/null +++ b/include/mukyu/cablin/command/while.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +const std::string COMMANDWHILE_KEY = "while"; + +class CommandWhile : public mukyu::cablin::core::Command { +public: + CommandWhile(const std::string& package, const YAML::Node& node); + ~CommandWhile(); + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/common/defer.hpp b/include/mukyu/cablin/common/defer.hpp new file mode 100644 index 0000000..97d65ee --- /dev/null +++ b/include/mukyu/cablin/common/defer.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace common { + + +class Defer { +public: + explicit Defer(const std::function& func) : func_(func) { + } + + Defer(Defer&&) = delete; + Defer(const Defer&) = delete; + Defer& operator=(Defer&&) = delete; + Defer& operator=(const Defer&) = delete; + + ~Defer() { + try { + func_(); + } catch (const std::exception& ex) { + std::cerr << "Defer::~Defer: " << ex.what() << std::endl; + } catch (...) { + std::cerr << "Defer::~Defer: unknown exception" << std::endl; + } + } + +private: + std::function func_; +}; + + +} // namespace common +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/common/string.hpp b/include/mukyu/cablin/common/string.hpp new file mode 100644 index 0000000..a717841 --- /dev/null +++ b/include/mukyu/cablin/common/string.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace common { + + +// splitPackage parse: +// +// :: => (, ) +// +// => ("", name) +inline std::pair splitPackage( + std::string_view source) { + auto pos = source.find("::"); + if (pos == std::string::npos) { + return {{}, source}; + } + + return {source.substr(0, pos), source.substr(pos + 2)}; +} + + +} // namespace common +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/common/yamlutil.hpp b/include/mukyu/cablin/common/yamlutil.hpp new file mode 100644 index 0000000..3b7d411 --- /dev/null +++ b/include/mukyu/cablin/common/yamlutil.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace common { + + +inline std::optional getSingleKey(const YAML::Node& node) { + if (!node.IsMap() || node.size() != 1) { + return {}; + } + + for (const auto& it : node) { + return it.first.as(); + } + + return {}; +} + +inline mukyu::cablin::core::Value valueNodeToValue( + mukyu::cablin::core::ValueType type, const YAML::Node& valueNode) { + if (valueNode == nullptr) { + // return zero value of the type + return mukyu::cablin::core::Value(type); + } + + switch (type) { + case mukyu::cablin::core::ValueType::BOOL: + return valueNode.as(); + case mukyu::cablin::core::ValueType::FLOAT: + return valueNode.as(); + case mukyu::cablin::core::ValueType::INT: + return valueNode.as(); + case mukyu::cablin::core::ValueType::INT64: + return valueNode.as(); + case mukyu::cablin::core::ValueType::STRING: + return valueNode.as(); + case mukyu::cablin::core::ValueType::NONE: + return {}; + } +} + + +} // namespace common +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/command.hpp b/include/mukyu/cablin/core/command.hpp new file mode 100644 index 0000000..e2f62bf --- /dev/null +++ b/include/mukyu/cablin/core/command.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +class Controller; + +class Command { +public: + Command() = default; + virtual ~Command() = default; + virtual Value execute(Controller*) = 0; +}; + +using CommandPtr = std::unique_ptr; + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/controller.hpp b/include/mukyu/cablin/core/controller.hpp new file mode 100644 index 0000000..558888d --- /dev/null +++ b/include/mukyu/cablin/core/controller.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +class Function; + +class Controller final { +public: + Controller(); + ~Controller(); + + void addPackage(const std::string& package); + + void addPackageVar(const std::string& package, const std::string& name, + const Value& value); + + void setPackageVar(const std::string& package, const std::string& name, + const Value& value); + + const Value& getPackageVar(const std::string& package, + const std::string& name); + + void addLocalVar(const std::string& name, const Value& value); + + void setLocalVar(const std::string& name, const Value& value); + + const Value& getLocalVar(const std::string& name) const; + + void addFunction(const std::string& package, + std::shared_ptr function); + + Value callFunction(const std::string& package, const std::string& name, + const std::vector& params); + + void pushLocalBlock(); + + void popLocalBlock(); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/error.hpp b/include/mukyu/cablin/core/error.hpp new file mode 100644 index 0000000..4ee0965 --- /dev/null +++ b/include/mukyu/cablin/core/error.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +class CablinParsingException : public std::runtime_error { +public: + CablinParsingException(const std::string& msg, int line, int pos, + int column) + : std::runtime_error(msg), column(column), line(line), pos(pos) { + } + ~CablinParsingException() = default; + + int line; + int pos; + int column; +}; + +class CablinRuntimeException : public std::runtime_error { +public: + CablinRuntimeException(const std::string& msg) : std::runtime_error(msg) { + } +}; + +class CablinIdentifierNotFoundException : public std::out_of_range { +public: + explicit CablinIdentifierNotFoundException(const std::string& msg) + : std::out_of_range(msg) { + } +}; + +inline CablinParsingException makeParsingException(const std::string& msg, + const YAML::Mark& mark) { + return CablinParsingException(msg, mark.line, mark.pos, mark.column); +} + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/expr.hpp b/include/mukyu/cablin/core/expr.hpp new file mode 100644 index 0000000..902a92b --- /dev/null +++ b/include/mukyu/cablin/core/expr.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +class Controller; + +class Expr { +public: + Expr() = default; + virtual ~Expr() = default; + virtual Value compute(Controller* controller) = 0; +}; + +using ExprPtr = std::unique_ptr; + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/function.hpp b/include/mukyu/cablin/core/function.hpp new file mode 100644 index 0000000..5fa8513 --- /dev/null +++ b/include/mukyu/cablin/core/function.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +class Controller; + +using ValueList = std::vector; + +class Function { +public: + Function() = default; + virtual ~Function() = default; + + virtual const std::string& getName() const = 0; + + virtual Value execute(Controller* controller, ValueList params) = 0; +}; + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/package.hpp b/include/mukyu/cablin/core/package.hpp new file mode 100644 index 0000000..b16726a --- /dev/null +++ b/include/mukyu/cablin/core/package.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +namespace mukyu { +namespace cablin { +namespace core { + + +class Controller; + +class Package { +public: + Package() = default; + virtual ~Package() = default; + + // prepare package's functions and variable + virtual void prepare(Controller*) = 0; + + // usePackages return imported package + virtual std::vector usePackages() const { + return {}; + }; +}; + +using PackagePtr = std::shared_ptr; + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/script.hpp b/include/mukyu/cablin/core/script.hpp new file mode 100644 index 0000000..b8da18e --- /dev/null +++ b/include/mukyu/cablin/core/script.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +class Script final { +public: + using PackagePtrMap = std::unordered_map; + + Script(const std::string& filename); + Script(const std::string& filename, PackagePtrMap packages); + + Script(const YAML::Node& root); + Script(const YAML::Node& root, PackagePtrMap packages); + + ~Script(); + + int main(const std::vector& argv); + + Value callFunction(const std::string& name, std::vector params); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/core/value.hpp b/include/mukyu/cablin/core/value.hpp new file mode 100644 index 0000000..666983d --- /dev/null +++ b/include/mukyu/cablin/core/value.hpp @@ -0,0 +1,236 @@ +#pragma once + +#include +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +enum class ValueType { + NONE, + BOOL, + INT, + INT64, + FLOAT, + STRING, +}; + +const std::unordered_map STR_VALUETYPE_MAP = { + {"none", ValueType::NONE}, {"bool", ValueType::BOOL}, + {"int", ValueType::INT}, {"int64", ValueType::INT64}, + {"float", ValueType::FLOAT}, {"string", ValueType::STRING}, +}; + +const std::unordered_map VALUETYPE_STR_MAP = { + {ValueType::NONE, "none"}, {ValueType::BOOL, "bool"}, + {ValueType::INT, "int"}, {ValueType::INT64, "int64"}, + {ValueType::FLOAT, "float"}, {ValueType::STRING, "string"}, +}; + +class Value final { +public: + Value(int v) noexcept : iv_(v), type_(ValueType::INT) { + } + + Value(bool v) noexcept : bv_(v), type_(ValueType::BOOL) { + } + + Value(int64_t v) noexcept : i64v_(v), type_(ValueType::INT64) { + } + + Value(float v) noexcept : fv_(v), type_(ValueType::FLOAT) { + } + + Value(const std::string& v) : strv_(v), type_(ValueType::STRING) { + } + + Value(ValueType type) noexcept : type_(type) { + } + + Value() noexcept = default; + ~Value() noexcept = default; + + ValueType type() const noexcept { + return type_; + } + + template + T as() const; + + template + Value cast() const; + +private: + ValueType type_ = ValueType::NONE; + bool bv_ = false; + int iv_ = 0; + int64_t i64v_ = 0; + float fv_ = 0; + std::string strv_; +}; + +template <> +inline int Value::as() const { + if (type_ != ValueType::INT) { + throw CablinRuntimeException("Value::as: type is not int"); + } + return iv_; +} + +template <> +inline int64_t Value::as() const { + if (type_ != ValueType::INT64) { + throw CablinRuntimeException("Value::as: type is not int64"); + } + return i64v_; +} + +template <> +inline float Value::as() const { + if (type_ != ValueType::FLOAT) { + throw CablinRuntimeException("Value::as: type is not float"); + } + return fv_; +} + +template <> +inline bool Value::as() const { + if (type_ != ValueType::BOOL) { + throw CablinRuntimeException("Value::as: type is not bool"); + } + return bv_; +} + +template <> +inline std::string Value::as() const { + if (type_ != ValueType::STRING) { + throw CablinRuntimeException("Value::as: type is not string"); + } + return strv_; +} + +template <> +inline Value Value::cast() const { + switch (type_) { + case ValueType::INT: + return *this; + case ValueType::INT64: + return static_cast(this->i64v_); + case ValueType::FLOAT: + return static_cast(this->fv_); + default: + throw CablinRuntimeException("Value::cast: type is not numerical"); + } +} + +template <> +inline Value Value::cast() const { + switch (type_) { + case ValueType::INT: + return static_cast(this->iv_); + case ValueType::INT64: + return *this; + case ValueType::FLOAT: + return static_cast(this->fv_); + default: + throw CablinRuntimeException( + "Value::cast: type is not numerical"); + } +} + +template <> +inline Value Value::cast() const { + switch (type_) { + case ValueType::INT: + return static_cast(this->iv_); + case ValueType::INT64: + return static_cast(this->i64v_); + case ValueType::FLOAT: + return *this; + default: + throw CablinRuntimeException( + "Value::cast: type is not numerical"); + } +} + +inline Value operator+(const Value& v1, const Value& v2) { + return v1.as() + v2.as(); +} + +inline Value operator-(const Value& v1, const Value& v2) { + return v1.as() - v2.as(); +} + +inline Value operator*(const Value& v1, const Value& v2) { + return v1.as() * v2.as(); +} + +inline Value operator/(const Value& v1, const Value& v2) { + return v1.as() / v2.as(); +} + +inline Value operator>(const Value& v1, const Value& v2) { + return v1.as() > v2.as(); +} + +inline Value operator>=(const Value& v1, const Value& v2) { + return v1.as() >= v2.as(); +} + +inline Value operator<(const Value& v1, const Value& v2) { + return v1.as() < v2.as(); +} + +inline Value operator<=(const Value& v1, const Value& v2) { + return v1.as() <= v2.as(); +} + +inline Value operator==(const Value& v1, const Value& v2) noexcept { + if (v1.type() != v2.type()) { + return false; + } + + switch (v1.type()) { + case ValueType::NONE: + return true; + case ValueType::BOOL: + return v1.as() == v2.as(); + case ValueType::INT: + return v1.as() == v2.as(); + case ValueType::INT64: + return v1.as() == v2.as(); + case ValueType::FLOAT: + return v1.as() == v2.as(); + case ValueType::STRING: + return v1.as() == v2.as(); + } + + return false; +} + +inline Value operator%(const Value& v1, const Value& v2) { + return v1.as() % v2.as(); +} + +inline Value operator&&(const Value& v1, const Value& v2) { + return v1.as() && v2.as(); +} + +inline Value operator||(const Value& v1, const Value& v2) { + return v1.as() || v2.as(); +} + +inline Value operator!(const Value& v1) { + return !v1.as(); +} + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/expr/call.hpp b/include/mukyu/cablin/expr/call.hpp new file mode 100644 index 0000000..4a06d95 --- /dev/null +++ b/include/mukyu/cablin/expr/call.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace expr { + + +const std::string EXPRCALL_NAME = "call"; + +class ExprCall : public mukyu::cablin::core::Expr { +public: + ExprCall(const std::string& package, const YAML::Node& node); + ~ExprCall(); + + mukyu::cablin::core::Value compute( + mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace expr +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/expr/const.hpp b/include/mukyu/cablin/expr/const.hpp new file mode 100644 index 0000000..0ee4613 --- /dev/null +++ b/include/mukyu/cablin/expr/const.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace expr { + + +const std::string EXPRCONST_NAME = "const"; + +class ExprConst : public mukyu::cablin::core::Expr { +public: + // value = None + ExprConst() = default; + + ExprConst(const YAML::Node& node) { + auto type = mukyu::cablin::core::STR_VALUETYPE_MAP.at( + node["type"].as()); + + v = mukyu::cablin::common::valueNodeToValue(type, node["value"]); + } + + ~ExprConst() = default; + + mukyu::cablin::core::Value compute(mukyu::cablin::core::Controller*) { + return v; + } + +private: + mukyu::cablin::core::Value v; +}; + + +} // namespace expr +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/expr/factory.hpp b/include/mukyu/cablin/expr/factory.hpp new file mode 100644 index 0000000..80af277 --- /dev/null +++ b/include/mukyu/cablin/expr/factory.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace expr { + + +mukyu::cablin::core::ExprPtr createExpr(const std::string& package, + const YAML::Node& node); + + +} // namespace expr +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/expr/getvar.hpp b/include/mukyu/cablin/expr/getvar.hpp new file mode 100644 index 0000000..98bb40a --- /dev/null +++ b/include/mukyu/cablin/expr/getvar.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace expr { + + +const std::string EXPRGET_NAME = "get"; + +class ExprGet : public mukyu::cablin::core::Expr { +public: + ExprGet(const std::string& package, const YAML::Node& node); + + ~ExprGet(); + + mukyu::cablin::core::Value compute( + mukyu::cablin::core::Controller* controller); + +private: + const std::string& package_; + std::string packageName_; + std::string varName_; +}; + + +} // namespace expr +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/function/binary_operator.hpp b/include/mukyu/cablin/function/binary_operator.hpp new file mode 100644 index 0000000..ebf89f4 --- /dev/null +++ b/include/mukyu/cablin/function/binary_operator.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +enum class BinaryOperatorType { + PLUS, + MINUS, + MULTIPLY, + DIVIDE, + MOD, + AND, + OR, + EQUAL, + LESS, + GREATER, + LESS_EQUAL, + GREATER_EQUAL, +}; + +const std::unordered_map + FUNCTION_BINARY_OPERATOR_NAME_MAP = { + {BinaryOperatorType::PLUS, "plus"}, + {BinaryOperatorType::MINUS, "minus"}, + {BinaryOperatorType::MULTIPLY, "multiply"}, + {BinaryOperatorType::DIVIDE, "divide"}, + {BinaryOperatorType::MOD, "mod"}, + {BinaryOperatorType::AND, "and"}, + {BinaryOperatorType::OR, "or"}, + {BinaryOperatorType::EQUAL, "equal"}, + {BinaryOperatorType::LESS, "less"}, + {BinaryOperatorType::GREATER, "greater"}, + {BinaryOperatorType::LESS_EQUAL, "less_equal"}, + {BinaryOperatorType::GREATER_EQUAL, "greater_equal"}, +}; + +using BinaryOperatorFunctionType = std::function; + +const std::unordered_map + FUNCTION_BINARY_OPERATOR_FUNC_MAP = { + {BinaryOperatorType::PLUS, mukyu::cablin::core::operator+ }, + {BinaryOperatorType::MINUS, mukyu::cablin::core::operator- }, + {BinaryOperatorType::MULTIPLY, mukyu::cablin::core::operator* }, + {BinaryOperatorType::DIVIDE, mukyu::cablin::core::operator/ }, + {BinaryOperatorType::MOD, mukyu::cablin::core::operator% }, + {BinaryOperatorType::AND, mukyu::cablin::core::operator&& }, + {BinaryOperatorType::OR, mukyu::cablin::core::operator|| }, + {BinaryOperatorType::EQUAL, mukyu::cablin::core::operator== }, + {BinaryOperatorType::LESS, mukyu::cablin::core::operator<}, + {BinaryOperatorType::GREATER, mukyu::cablin::core::operator> }, + {BinaryOperatorType::LESS_EQUAL, + mukyu::cablin::core::operator<= }, + {BinaryOperatorType::GREATER_EQUAL, + mukyu::cablin::core::operator>= }, }; + +template +class FunctionBinaryOperator : public mukyu::cablin::core::Function { +public: + FunctionBinaryOperator() + : name_(FUNCTION_BINARY_OPERATOR_NAME_MAP.at(type)), + func_(FUNCTION_BINARY_OPERATOR_FUNC_MAP.at(type)) { + } + + ~FunctionBinaryOperator() = default; + + const std::string& getName() const { + return name_; + } + + mukyu::cablin::core::Value execute(mukyu::cablin::core::Controller*, + mukyu::cablin::core::ValueList params) { + if (params.size() != 2) { + throw mukyu::cablin::core::CablinRuntimeException( + "FunctionBinaryOperator<" + name_ + + ">::execute: |params| should be 2"); + } + + return func_(params[0], params[1]); + } + +private: + std::string name_; + BinaryOperatorFunctionType func_; +}; + +using FunctionOperatorPlus = FunctionBinaryOperator; +using FunctionOperatorMinus = FunctionBinaryOperator; +using FunctionOperatorMultiply = + FunctionBinaryOperator; +using FunctionOperatorDivide = + FunctionBinaryOperator; +using FunctionOperatorMod = FunctionBinaryOperator; + +using FunctionOperatorAnd = FunctionBinaryOperator; +using FunctionOperatorOr = FunctionBinaryOperator; + +using FunctionOperatorEqual = FunctionBinaryOperator; +using FunctionOperatorLess = FunctionBinaryOperator; +using FunctionOperatorGreater = + FunctionBinaryOperator; +using FunctionOperatorLessEqual = + FunctionBinaryOperator; +using FunctionOperatorGreaterEqual = + FunctionBinaryOperator; + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/function/cast.hpp b/include/mukyu/cablin/function/cast.hpp new file mode 100644 index 0000000..76d734f --- /dev/null +++ b/include/mukyu/cablin/function/cast.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +class FunctionInt : public mukyu::cablin::core::Function { +public: + FunctionInt() = default; + ~FunctionInt() = default; + + const std::string& getName() const { + return name_; + } + + mukyu::cablin::core::Value execute(mukyu::cablin::core::Controller*, + mukyu::cablin::core::ValueList params) { + if (params.size() != 1) { + throw mukyu::cablin::core::CablinRuntimeException( + "FunctionInt: params size should be 1"); + } + return params[0].cast(); + } + +private: + std::string name_ = "int"; +}; + +class FunctionInt64 : public mukyu::cablin::core::Function { +public: + FunctionInt64() = default; + ~FunctionInt64() = default; + + const std::string& getName() const { + return name_; + } + + mukyu::cablin::core::Value execute(mukyu::cablin::core::Controller*, + mukyu::cablin::core::ValueList params) { + if (params.size() != 1) { + throw mukyu::cablin::core::CablinRuntimeException( + "FunctionInt64: params size should be 1"); + } + return params[0].cast(); + } + +private: + std::string name_ = "int64"; +}; + +class FunctionFloat : public mukyu::cablin::core::Function { +public: + FunctionFloat() = default; + ~FunctionFloat() = default; + + const std::string& getName() const { + return name_; + } + + mukyu::cablin::core::Value execute(mukyu::cablin::core::Controller*, + mukyu::cablin::core::ValueList params) { + if (params.size() != 1) { + throw mukyu::cablin::core::CablinRuntimeException( + "FunctionFloat: params size should be 1"); + } + return params[0].cast(); + } + +private: + std::string name_ = "float"; +}; + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/function/functor.hpp b/include/mukyu/cablin/function/functor.hpp new file mode 100644 index 0000000..83eebc7 --- /dev/null +++ b/include/mukyu/cablin/function/functor.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +class FunctionFunctor : public mukyu::cablin::core::Function { +public: + using FunctorType = std::function; + + FunctionFunctor(const std::string& name, FunctorType functor) + : name_(name), functor_(functor) { + } + + ~FunctionFunctor() = default; + + const std::string& getName() const { + return name_; + } + + mukyu::cablin::core::Value execute(mukyu::cablin::core::Controller*, + mukyu::cablin::core::ValueList params) { + return functor_(params); + } + +private: + std::string name_; + FunctorType functor_; +}; + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/function/node.hpp b/include/mukyu/cablin/function/node.hpp new file mode 100644 index 0000000..6ccc330 --- /dev/null +++ b/include/mukyu/cablin/function/node.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +class FunctionNode : public mukyu::cablin::core::Function { +public: + FunctionNode(const std::string& package, const YAML::Node& node); + + ~FunctionNode(); + + const std::string& getName() const; + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller, + mukyu::cablin::core::ValueList params); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/function/print.hpp b/include/mukyu/cablin/function/print.hpp new file mode 100644 index 0000000..a48c5d4 --- /dev/null +++ b/include/mukyu/cablin/function/print.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +class FunctionPrint : public mukyu::cablin::core::Function { +public: + FunctionPrint(); + ~FunctionPrint(); + + const std::string& getName() const; + + mukyu::cablin::core::Value execute( + mukyu::cablin::core::Controller* controller, + mukyu::cablin::core::ValueList params); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/function/unary_operator.hpp b/include/mukyu/cablin/function/unary_operator.hpp new file mode 100644 index 0000000..a8d8276 --- /dev/null +++ b/include/mukyu/cablin/function/unary_operator.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +enum class UnaryOperatorType { + NOT, +}; + +const std::unordered_map + FUNCTION_UNARY_OPERATOR_NAME_MAP = { + {UnaryOperatorType::NOT, "not"}, +}; + +using UnaryOperatorFunctionType = std::function; + +const std::unordered_map + FUNCTION_UNARY_OPERATOR_FUNC_MAP = { + {UnaryOperatorType::NOT, mukyu::cablin::core::operator!}, }; + +template +class FunctionUnaryOperator : public mukyu::cablin::core::Function { +public: + FunctionUnaryOperator() + : name_(FUNCTION_UNARY_OPERATOR_NAME_MAP.at(type)), + func_(FUNCTION_UNARY_OPERATOR_FUNC_MAP.at(type)) { + } + + ~FunctionUnaryOperator() = default; + + const std::string& getName() const { + return name_; + } + + mukyu::cablin::core::Value execute(mukyu::cablin::core::Controller*, + mukyu::cablin::core::ValueList params) { + if (params.size() != 1) { + throw mukyu::cablin::core::CablinRuntimeException( + "FunctionUnaryOperator<" + name_ + + ">::execute: |params| should be 1"); + } + + return func_(params[0]); + } + +private: + std::string name_; + UnaryOperatorFunctionType func_; +}; + +using FunctionOperatorNot = FunctionUnaryOperator; + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/package/builtin_cast.hpp b/include/mukyu/cablin/package/builtin_cast.hpp new file mode 100644 index 0000000..402b631 --- /dev/null +++ b/include/mukyu/cablin/package/builtin_cast.hpp @@ -0,0 +1,28 @@ +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +const std::string BUILTIN_CAST_NAME = "cast"; + +class BuiltinCastPackage : public mukyu::cablin::core::Package { +public: + BuiltinCastPackage(); + ~BuiltinCastPackage(); + + void prepare(mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/package/builtin_io.hpp b/include/mukyu/cablin/package/builtin_io.hpp new file mode 100644 index 0000000..4b05d9e --- /dev/null +++ b/include/mukyu/cablin/package/builtin_io.hpp @@ -0,0 +1,28 @@ +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +const std::string BUILTIN_IO_NAME = "io"; + +class BuiltinIOPackage : public mukyu::cablin::core::Package { +public: + BuiltinIOPackage(); + ~BuiltinIOPackage(); + + void prepare(mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/package/builtin_op.hpp b/include/mukyu/cablin/package/builtin_op.hpp new file mode 100644 index 0000000..46eb723 --- /dev/null +++ b/include/mukyu/cablin/package/builtin_op.hpp @@ -0,0 +1,28 @@ +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +const std::string BUILTIN_OP_NAME = "op"; + +class BuiltinOperatorPackage : public mukyu::cablin::core::Package { +public: + BuiltinOperatorPackage(); + ~BuiltinOperatorPackage(); + + void prepare(mukyu::cablin::core::Controller* controller); + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/package/factory.hpp b/include/mukyu/cablin/package/factory.hpp new file mode 100644 index 0000000..681a63d --- /dev/null +++ b/include/mukyu/cablin/package/factory.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +mukyu::cablin::core::PackagePtr createPackage(const std::string& name, + const std::string& dir); + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/include/mukyu/cablin/package/userdefine.hpp b/include/mukyu/cablin/package/userdefine.hpp new file mode 100644 index 0000000..124ddf0 --- /dev/null +++ b/include/mukyu/cablin/package/userdefine.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +class UserPackage : public mukyu::cablin::core::Package { +public: + UserPackage(const std::string& name, const std::string& filename); + + UserPackage(const std::string& name, const YAML::Node& root); + + ~UserPackage(); + + void prepare(mukyu::cablin::core::Controller* controller); + std::vector usePackages() const; + +private: + class Impl; + std::unique_ptr impl_; +}; + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/interpreter/CMakeLists.txt b/interpreter/CMakeLists.txt new file mode 100644 index 0000000..14b848c --- /dev/null +++ b/interpreter/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.6.2) + +include_directories(../include/) + +add_executable(interpreter main.cpp) +add_dependencies(interpreter cablin) +target_link_libraries(interpreter cablin) diff --git a/interpreter/main.cpp b/interpreter/main.cpp new file mode 100644 index 0000000..3558b2f --- /dev/null +++ b/interpreter/main.cpp @@ -0,0 +1,23 @@ +#include + +#include + +#include +#include + +namespace mccore = mukyu::cablin::core; + +int main(int argc, char** argv) { + try { + mccore::Script script(argv[1]); + return script.main({}); + } catch (const mccore::CablinParsingException& ex) { + std::cerr << ex.what() << std::endl; + std::cerr << "Line: " << ex.line << ", Column: " << ex.column + << std::endl; + return 1; + } catch (const std::exception& ex) { + std::cerr << ex.what() << std::endl; + return 1; + } +} \ No newline at end of file diff --git a/src/command/assign.cpp b/src/command/assign.cpp new file mode 100644 index 0000000..26263b3 --- /dev/null +++ b/src/command/assign.cpp @@ -0,0 +1,68 @@ +#include + +#include + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; +namespace mcexpr = mukyu::cablin::expr; + + +class CommandAssign::Impl { +public: + Impl(const std::string& package, const YAML::Node& node) + : source_(mcexpr::createExpr(package, node["source"])), + package_(package) { + // parsing :: + auto target = node["target"].as(); + std::tie(varPackage_, target_) = mccommon::splitPackage(target); + } + + mccore::Value execute(mccore::Controller* controller) { + mccore::Value value = source_->compute(controller); + if (!varPackage_.empty()) { + controller->setPackageVar(varPackage_, target_, value); + return value; + } + + try { + controller->setLocalVar(target_, value); + } catch (const std::out_of_range&) { + controller->setPackageVar(std::string(package_), target_, value); + } + + return value; + } + +private: + const std::string& package_; + std::string varPackage_; + std::string target_; + mccore::ExprPtr source_; +}; + +CommandAssign::CommandAssign(const std::string& package, const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +CommandAssign::~CommandAssign() = default; + +mccore::Value CommandAssign::execute(mccore::Controller* controller) { + return impl_->execute(controller); +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/command/block.cpp b/src/command/block.cpp new file mode 100644 index 0000000..0594cf1 --- /dev/null +++ b/src/command/block.cpp @@ -0,0 +1,52 @@ +#include + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; +namespace mcexpr = mukyu::cablin::expr; + + +class CommandBlock::Impl { +public: + Impl(const std::string& package, const YAML::Node& node) + : body_(createCommandList(package, node)) { + } + + mccore::Value execute(mccore::Controller* controller) { + controller->pushLocalBlock(); + mccommon::Defer defer([controller]() { controller->popLocalBlock(); }); + for (const auto& cmd : body_) { + cmd->execute(controller); + } + return {}; + } + +private: + std::vector body_; +}; + +CommandBlock::CommandBlock(const std::string& package, const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +CommandBlock::~CommandBlock() = default; + +mccore::Value CommandBlock::execute(mccore::Controller* controller) { + return impl_->execute(controller); +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/command/call.cpp b/src/command/call.cpp new file mode 100644 index 0000000..5a52b48 --- /dev/null +++ b/src/command/call.cpp @@ -0,0 +1,71 @@ +#include + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; +namespace mcexpr = mukyu::cablin::expr; + + +class CommandCall::Impl { +public: + Impl(const std::string& package, const YAML::Node& node) + : package_(package) { + const auto& paramsNode = node["params"]; + if (paramsNode != nullptr) { + for (const auto& item : paramsNode) { + params_.push_back(mcexpr::createExpr(package, item)); + } + } + + // parsing :: + auto name = node["name"].as(); + std::tie(varPackage_, name_) = mccommon::splitPackage(name); + } + + ~Impl() = default; + + mccore::Value execute(mccore::Controller* controller) { + std::vector params(params_.size()); + for (size_t i = 0; i < params_.size(); ++i) { + params[i] = params_[i]->compute(controller); + } + + if (!varPackage_.empty()) { + return controller->callFunction(varPackage_, name_, params); + } else { + return controller->callFunction(package_, name_, params); + } + } + +private: + const std::string& package_; + std::string varPackage_; + std::string name_; + std::vector params_; +}; + +CommandCall::CommandCall(const std::string& package, const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +CommandCall::~CommandCall() = default; + +mccore::Value CommandCall::execute(mccore::Controller* controller) { + return impl_->execute(controller); +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/command/factory.cpp b/src/command/factory.cpp new file mode 100644 index 0000000..c00965e --- /dev/null +++ b/src/command/factory.cpp @@ -0,0 +1,82 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; + + +mccore::CommandPtr createCommand(const std::string& package, + const YAML::Node& node) { + auto key = mccommon::getSingleKey(node); + if (!key) { + throw mccore::makeParsingException( + "createCommand: node must be single-key-map", node.Mark()); + } + + const auto& nodeValue = node[key.value()]; + + if (key == COMMANDVAR_KEY) { + return std::make_unique(nodeValue); + } else if (key == COMMANDCALL_KEY) { + return std::make_unique(package, nodeValue); + } else if (key == COMMANDASSIGN_KEY) { + return std::make_unique(package, nodeValue); + } else if (key == COMMANDBLOCK_KEY) { + return std::make_unique(package, nodeValue); + } else if (key == COMMANDIF_KEY) { + return std::make_unique(package, nodeValue); + } else if (key == COMMANDWHILE_KEY) { + return std::make_unique(package, nodeValue); + } else if (key == COMMANDBREAK_KEY) { + return std::make_unique(); + } else if (key == COMMANDCONTINUE_KEY) { + return std::make_unique(); + } else if (key == COMMANDRETURN_KEY) { + return std::make_unique(package, nodeValue); + } + + throw mccore::makeParsingException( + "createCommand: cannot specify command: " + key.value(), node.Mark()); +} + +std::vector createCommandList(const std::string& package, + const YAML::Node& node) { + if (node == nullptr) { + return {}; + } + + size_t sz = node.size(); + std::vector ret; + + for (size_t i = 0; i < sz; ++i) { + ret.emplace_back(createCommand(package, node[i])); + } + return ret; +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/command/globalvar.cpp b/src/command/globalvar.cpp new file mode 100644 index 0000000..a4d00d9 --- /dev/null +++ b/src/command/globalvar.cpp @@ -0,0 +1,57 @@ +#include + +#include + +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; + + +class CommandGlobalVar::Impl { +public: + Impl(const std::string& package, const YAML::Node& node) + : name_(node["name"].as()), package_(package) { + const auto& valueNode = node["default_value"]; + type_ = mccore::STR_VALUETYPE_MAP.at(node["type"].as()); + defaultValue_ = mccommon::valueNodeToValue(type_, valueNode); + } + + ~Impl() = default; + + mccore::Value execute(mccore::Controller* controller) { + controller->addPackageVar(package_, name_, defaultValue_); + return defaultValue_; + } + +private: + const std::string& package_; + std::string name_; + mccore::ValueType type_; + mccore::Value defaultValue_; +}; + +CommandGlobalVar::CommandGlobalVar(const std::string& package, + const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +CommandGlobalVar::~CommandGlobalVar() = default; + +mccore::Value CommandGlobalVar::execute(mccore::Controller* controller) { + return impl_->execute(controller); +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/command/if.cpp b/src/command/if.cpp new file mode 100644 index 0000000..49aeb56 --- /dev/null +++ b/src/command/if.cpp @@ -0,0 +1,62 @@ +#include + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; +namespace mcexpr = mukyu::cablin::expr; + + +class CommandIf::Impl { +public: + Impl(const std::string& package, const YAML::Node& node) + : cond_(mcexpr::createExpr(package, node["condition"])), + thenBody_(createCommandList(package, node["then"])), + elseBody_(createCommandList(package, node["else"])) { + } + + mccore::Value execute(mccore::Controller* controller) { + controller->pushLocalBlock(); + mccommon::Defer defer([controller]() { controller->popLocalBlock(); }); + if (cond_->compute(controller).as()) { + for (const auto& cmd : thenBody_) { + cmd->execute(controller); + } + } else { + for (const auto& cmd : elseBody_) { + cmd->execute(controller); + } + } + return {}; + } + +private: + mccore::ExprPtr cond_; + std::vector thenBody_; + std::vector elseBody_; +}; + +CommandIf::CommandIf(const std::string& package, const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +CommandIf::~CommandIf() = default; + +mccore::Value CommandIf::execute(mccore::Controller* controller) { + return impl_->execute(controller); +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/command/var.cpp b/src/command/var.cpp new file mode 100644 index 0000000..398897e --- /dev/null +++ b/src/command/var.cpp @@ -0,0 +1,54 @@ +#include + +#include + +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; + + +class CommandVar::Impl { +public: + Impl(const YAML::Node& node) : name_(node["name"].as()) { + const auto& valueNode = node["default_value"]; + type_ = mccore::STR_VALUETYPE_MAP.at(node["type"].as()); + defaultValue_ = mccommon::valueNodeToValue(type_, valueNode); + } + + ~Impl() = default; + + mccore::Value execute(mccore::Controller* controller) { + controller->addLocalVar(name_, defaultValue_); + return defaultValue_; + } + +private: + std::string name_; + mccore::ValueType type_; + mccore::Value defaultValue_; +}; + +CommandVar::CommandVar(const YAML::Node& node) + : impl_(std::make_unique(node)) { +} + +CommandVar::~CommandVar() = default; + +mccore::Value CommandVar::execute(mccore::Controller* controller) { + return impl_->execute(controller); +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/command/while.cpp b/src/command/while.cpp new file mode 100644 index 0000000..ed1c6ee --- /dev/null +++ b/src/command/while.cpp @@ -0,0 +1,63 @@ +#include + +#include +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace command { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; +namespace mcexpr = mukyu::cablin::expr; + + +class CommandWhile::Impl { +public: + Impl(const std::string& package, const YAML::Node& node) + : cond_(mcexpr::createExpr(package, node["condition"])), + body_(createCommandList(package, node["body"])) { + } + + mccore::Value execute(mccore::Controller* controller) { + controller->pushLocalBlock(); + mccommon::Defer defer([controller]() { controller->popLocalBlock(); }); + while (cond_->compute(controller).as()) { + try { + for (const auto& cmd : body_) { + cmd->execute(controller); + } + } catch (BreakException) { + break; + } catch (ContinueException) { + continue; + } + } + return {}; + } + +private: + mccore::ExprPtr cond_; + std::vector body_; +}; + +CommandWhile::CommandWhile(const std::string& package, const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +CommandWhile::~CommandWhile() = default; + +mccore::Value CommandWhile::execute(mccore::Controller* controller) { + return impl_->execute(controller); +} + + +} // namespace command +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/core/controller.cpp b/src/core/controller.cpp new file mode 100644 index 0000000..11246fc --- /dev/null +++ b/src/core/controller.cpp @@ -0,0 +1,312 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +namespace { + + +using VariableMap = std::unordered_map; +using FunctionMap = std::unordered_map>; + +class PackageController { +public: + PackageController() = default; + ~PackageController() = default; + + void addVar(const std::string& name, const Value& value) { + if (globalVars_.find(name) != globalVars_.end()) { + throw CablinRuntimeException("PackageController::addVar: " + name + + " redefine found"); + } + + globalVars_[name] = value; + } + + void setVar(const std::string& name, const Value& value) { + auto varIt = globalVars_.find(name); + if (varIt == globalVars_.end()) { + throw CablinIdentifierNotFoundException( + "PackageController::setVar: " + name + " not found"); + } + + if (varIt->second.type() != value.type()) { + throw CablinRuntimeException( + "PackageController::setVar: type not equal"); + } + + varIt->second = value; + } + + const Value& getVar(const std::string& name) const { + if (globalVars_.find(name) == globalVars_.end()) { + throw CablinIdentifierNotFoundException( + "PackageController::getVar: " + name + " not found"); + } + + return globalVars_.at(name); + } + + void addFunction(std::shared_ptr function) { + const auto& name = function->getName(); + if (funcs_.find(name) != funcs_.end()) { + throw CablinRuntimeException( + "PackageController::addFunction: " + name + " redefine found"); + } + + funcs_[name] = std::move(function); + } + + Value callFunction(Controller* controller, const std::string& name, + std::vector params) { + const auto& node = funcs_.find(name); + if (node == funcs_.end()) { + throw CablinIdentifierNotFoundException( + "PackageController::callFunction: " + name + " not found"); + } + + return node->second->execute(controller, params); + } + + +private: + VariableMap globalVars_; + FunctionMap funcs_; +}; + + +class LocalVariableController { +public: + using BlockStack = std::vector; + + LocalVariableController() { + // XXX: A function block is prepared default. Is it make sense ??? + funcStack_.push(BlockStack(1)); + } + ~LocalVariableController() = default; + + void addVar(const std::string& name, const Value& value) { + auto& vars = funcStack_.top().back(); + + if (vars.find(name) != vars.end()) { + throw CablinRuntimeException( + "LocalVariableController::addVar: " + name + " redefine found"); + } + + vars[name] = value; + } + + void setVar(const std::string& name, const Value& value) { + auto& bStk = funcStack_.top(); + + for (auto it = bStk.rbegin(); it != bStk.rend(); ++it) { + auto varIt = it->find(name); + if (varIt == it->end()) { + continue; + } + + if (varIt->second.type() != value.type()) { + throw CablinRuntimeException( + "LocalVariableController::setVar: type not equal"); + } + + varIt->second = value; + return; + } + + throw CablinIdentifierNotFoundException( + "LocalVariableController::setVar: " + name + " not found"); + } + + const Value& getVar(const std::string& name) const { + auto& bStk = funcStack_.top(); + + for (auto it = bStk.crbegin(); it != bStk.crend(); ++it) { + if (it->find(name) != it->end()) { + return it->at(name); + } + } + + throw CablinIdentifierNotFoundException( + "LocalVariableController::getVar: " + name + " not found"); + } + + void pushFunctionBlock() { + funcStack_.push(BlockStack(1)); + } + + void popFunctionBlock() { + funcStack_.pop(); + } + + void pushLocalBlock() { + funcStack_.top().push_back({}); + } + + void popLocalBlock() { + funcStack_.top().pop_back(); + } + +private: + std::stack funcStack_; +}; + + +} // namespace + + +class Controller::Impl { +public: + Impl() = default; + ~Impl() = default; + + void addPackage(const std::string& package) { + if (packages.find(package) != packages.end()) { + throw CablinRuntimeException("Impl::addPackage: package " + + package + " redefine"); + } + packages[package] = {}; + } + + void addPackageVar(const std::string& package, const std::string& name, + const Value& value) { + if (packages.find(package) == packages.end()) { + throw CablinIdentifierNotFoundException( + "Impl::addPackageVar: package " + package + " not found"); + } + packages.at(package).addVar(name, value); + } + + void setPackageVar(const std::string& package, const std::string& name, + const Value& value) { + if (packages.find(package) == packages.end()) { + throw CablinIdentifierNotFoundException( + "Impl::setPackageVar: package " + package + " not found"); + } + packages.at(package).setVar(name, value); + } + + const Value& getPackageVar(const std::string& package, + const std::string& name) { + if (packages.find(package) == packages.end()) { + throw CablinIdentifierNotFoundException( + "Impl::getPackageVar: package " + package + " not found"); + } + return packages.at(package).getVar(name); + } + + void addLocalVar(const std::string& name, const Value& value) { + localVarContr_.addVar(name, value); + } + + void setLocalVar(const std::string& name, const Value& value) { + localVarContr_.setVar(name, value); + } + + const Value& getLocalVar(const std::string& name) const { + return localVarContr_.getVar(name); + } + + void addFunction(const std::string& package, + std::shared_ptr function) { + packages.at(package).addFunction(std::move(function)); + } + + Value callFunction(Controller* controller, const std::string& packageName, + const std::string& name, std::vector params) { + auto& package = packages.at(packageName); + + localVarContr_.pushFunctionBlock(); + mukyu::cablin::common::Defer deferFunc( + [this]() { this->localVarContr_.popFunctionBlock(); }); + + return package.callFunction(controller, name, params); + } + + void pushLocalBlock() { + localVarContr_.pushLocalBlock(); + } + + void popLocalBlock() { + localVarContr_.popLocalBlock(); + } + +private: + LocalVariableController localVarContr_; + std::unordered_map packages; +}; + + +Controller::Controller() : impl_(std::make_unique()) { +} + +Controller::~Controller() = default; + + +void Controller::addPackage(const std::string& package) { + impl_->addPackage(package); +} + +void Controller::addPackageVar(const std::string& package, + const std::string& name, const Value& value) { + impl_->addPackageVar(package, name, value); +} + +void Controller::setPackageVar(const std::string& package, + const std::string& name, const Value& value) { + impl_->setPackageVar(package, name, value); +} + +const Value& Controller::getPackageVar(const std::string& package, + const std::string& name) { + return impl_->getPackageVar(package, name); +} + +void Controller::addLocalVar(const std::string& name, const Value& value) { + impl_->addLocalVar(name, value); +} + +void Controller::setLocalVar(const std::string& name, const Value& value) { + impl_->setLocalVar(name, value); +} + +const Value& Controller::getLocalVar(const std::string& name) const { + return impl_->getLocalVar(name); +} + +void Controller::addFunction(const std::string& package, + std::shared_ptr function) { + impl_->addFunction(package, std::move(function)); +} + +Value Controller::callFunction(const std::string& package, + const std::string& name, + const std::vector& params) { + return impl_->callFunction(this, package, name, params); +} + +void Controller::pushLocalBlock() { + impl_->pushLocalBlock(); +} + +void Controller::popLocalBlock() { + impl_->popLocalBlock(); +} + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/core/script.cpp b/src/core/script.cpp new file mode 100644 index 0000000..e1fa258 --- /dev/null +++ b/src/core/script.cpp @@ -0,0 +1,148 @@ +#include + +#include + +#include +#include + + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace core { + + +namespace mcpkg = mukyu::cablin::package; + + +const std::string MAINPACKAGE = "main"; +const std::string MAINFUNCTION_NAME = "main"; +const std::string YAML_FILEEXT = ".yaml"; + +class Script::Impl { +public: + Impl(const YAML::Node& root, PackagePtrMap packages) + : prePackageMap_(std::move(packages)) { + auto mainPkg = std::make_shared(MAINPACKAGE, root); + loadMainPackage(std::move(mainPkg), ""); + } + + Impl(const std::string& filename, PackagePtrMap packages) + : prePackageMap_(std::move(packages)) { + // BFS all used packages + auto mainPkg = + std::make_shared(MAINPACKAGE, filename); + auto mainDir = std::filesystem::path(filename).parent_path(); + loadMainPackage(std::move(mainPkg), mainDir); + } + + Impl(const YAML::Node& root) { + auto mainPkg = std::make_shared(MAINPACKAGE, root); + loadMainPackage(std::move(mainPkg), ""); + } + + Impl(const std::string& filename) { + // BFS all used packages + auto mainPkg = + std::make_shared(MAINPACKAGE, filename); + auto mainDir = std::filesystem::path(filename).parent_path(); + loadMainPackage(std::move(mainPkg), mainDir); + } + + ~Impl() = default; + + int main(const std::vector& argv) { + // default return 0 in only main function + auto exitCode = + controller_.callFunction(MAINPACKAGE, MAINFUNCTION_NAME, {}); + + if (exitCode.type() == ValueType::INT) { + return exitCode.as(); + } + + return 0; + } + + Value callFunction(const std::string& name, std::vector params) { + return controller_.callFunction(MAINPACKAGE, name, params); + } + +private: + void loadMainPackage(PackagePtr mainPackage, + const std::string& mainDirStr) { + packageMap_.emplace(MAINPACKAGE, mainPackage); + mainPackage->prepare(&controller_); + + std::queue toLoad; + for (const auto& pName : mainPackage->usePackages()) { + toLoad.push(pName); + } + + auto mainDir = std::filesystem::path(mainDirStr); + + while (!toLoad.empty()) { + auto pName = toLoad.front(); + toLoad.pop(); + + if (packageMap_.find(pName) != packageMap_.end()) { + continue; + } + + PackagePtr newPkg; + + if (prePackageMap_.find(pName) != prePackageMap_.end()) { + newPkg = prePackageMap_.at(pName); + } else { + newPkg = mcpkg::createPackage(pName, mainDir); + } + + packageMap_.emplace(pName, std::move(newPkg)); + + auto& p = packageMap_.at(pName); + + p->prepare(&controller_); + for (const auto& opName : p->usePackages()) { + toLoad.push(opName); + } + } + } + +private: + Controller controller_; + PackagePtrMap prePackageMap_; + PackagePtrMap packageMap_; +}; + +Script::Script(const std::string& filename) + : impl_(std::make_unique(filename)) { +} + +Script::Script(const std::string& filename, PackagePtrMap packages) + : impl_(std::make_unique(filename, packages)) { +} + +Script::Script(const YAML::Node& root) : impl_(std::make_unique(root)) { +} + +Script::Script(const YAML::Node& root, PackagePtrMap packages) + : impl_(std::make_unique(root, packages)) { +} + +Script::~Script() = default; + +int Script::main(const std::vector& argv) { + return impl_->main(argv); +} + +Value Script::callFunction(const std::string& name, std::vector argv) { + return impl_->callFunction(name, argv); +} + + +} // namespace core +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/expr/call.cpp b/src/expr/call.cpp new file mode 100644 index 0000000..74d5cae --- /dev/null +++ b/src/expr/call.cpp @@ -0,0 +1,44 @@ +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace expr { + + +namespace mccmd = mukyu::cablin::command; +namespace mccore = mukyu::cablin::core; + + +class ExprCall::Impl { +public: + Impl(const std::string& package, const YAML::Node& node) + : callCmd_(std::make_unique(package, node)) { + } + + ~Impl() = default; + + mccore::Value compute(mccore::Controller* controller) { + return callCmd_->execute(controller); + } + +private: + mccore::CommandPtr callCmd_; +}; + +ExprCall::ExprCall(const std::string& package, const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +ExprCall::~ExprCall() = default; + +mccore::Value ExprCall::compute(mccore::Controller* controller) { + return impl_->compute(controller); +} + + +} // namespace expr +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/expr/factory.cpp b/src/expr/factory.cpp new file mode 100644 index 0000000..03748d9 --- /dev/null +++ b/src/expr/factory.cpp @@ -0,0 +1,49 @@ +#include + +#include + +#include + +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace expr { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; +namespace mcexpr = mukyu::cablin::expr; + + +mccore::ExprPtr createExpr(const std::string& package, const YAML::Node& node) { + auto key = mccommon::getSingleKey(node); + if (!key.has_value()) { + if (node.IsMap() && node.size() == 0) { + return std::make_unique(); + } + throw mccore::makeParsingException( + "createExpr: should be single-key-map", node.Mark()); + } + + const auto& obj = node[key.value()]; + + if (key == mcexpr::EXPRCONST_NAME) { + return std::make_unique(obj); + } else if (key == mcexpr::EXPRGET_NAME) { + return std::make_unique(package, obj); + } else if (key == mcexpr::EXPRCALL_NAME) { + return std::make_unique(package, obj); + } + + throw mccore::makeParsingException( + "createExpr: " + key.value() + " is not an expr type", node.Mark()); +} + + +} // namespace expr +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/expr/getvar.cpp b/src/expr/getvar.cpp new file mode 100644 index 0000000..c03f615 --- /dev/null +++ b/src/expr/getvar.cpp @@ -0,0 +1,45 @@ +#include + +#include +#include + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace expr { + + +namespace mccore = mukyu::cablin::core; +namespace mccommon = mukyu::cablin::common; + + +ExprGet::ExprGet(const std::string& package, const YAML::Node& node) + : package_(package) { + auto varName = node.as(); + std::tie(packageName_, varName_) = mccommon::splitPackage(varName); +} + +ExprGet::~ExprGet() = default; + +mccore::Value ExprGet::compute(mccore::Controller* controller) { + if (!packageName_.empty()) { + return controller->getPackageVar(packageName_, varName_); + } + + try { + return controller->getLocalVar(varName_); + } catch (const std::out_of_range& ex) { + return controller->getPackageVar(package_, varName_); + } +} + + +} // namespace expr +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/function/node.cpp b/src/function/node.cpp new file mode 100644 index 0000000..cbe8fd1 --- /dev/null +++ b/src/function/node.cpp @@ -0,0 +1,120 @@ +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +namespace mccore = mukyu::cablin::core; +namespace mccmd = mukyu::cablin::command; + + +class FunctionNode::Impl { +private: + struct Parameter { + std::string name; + mccore::ValueType type; + }; + +public: + Impl(const std::string& package, const YAML::Node& node) + : name_(node["name"].as()), + body_(mccmd::createCommandList(package, node["body"])) { + const auto& params = node["params"]; + if (params != nullptr) { + for (const auto& item : params) { + Parameter param; + param.name = item["name"].as(); + param.type = mccore::STR_VALUETYPE_MAP.at( + item["type"].as()); + + params_.push_back(std::move(param)); + } + } + + const auto& returnType = node["return_type"]; + if (returnType != nullptr) { + returnType_ = + mccore::STR_VALUETYPE_MAP.at(returnType.as()); + } + } + + ~Impl() = default; + + const std::string& getName() const { + return name_; + } + + mccore::Value execute(mccore::Controller* controller, + mccore::ValueList params) { + if (params.size() != params_.size()) { + throw mccore::CablinRuntimeException( + "FunctionNode::Impl: number of parameters not equal"); + } + + for (size_t i = 0; i < params_.size(); ++i) { + const auto& p = params_[i]; + if (p.type != params[i].type()) { + throw mccore::CablinRuntimeException( + "FunctionNode::Impl: " + std::to_string(i) + + "parameters type not equal"); + } + + controller->addLocalVar(p.name, params[i]); + } + + try { + for (const auto& cmdPtr : body_) { + cmdPtr->execute(controller); + } + } catch (const mccmd::ReturnException& rex) { + return rex.returnValue; + } catch (mccmd::LoopFlowControlException) { + throw mccore::CablinRuntimeException( + "FunctionNode::Impl: catch loop flow control"); + } + + // return zero-value; + mccore::Value var(returnType_); + return var; + } + +private: + mccore::ValueType returnType_; + std::string name_; + std::vector body_; + std::vector params_; +}; + + +FunctionNode::FunctionNode(const std::string& package, const YAML::Node& node) + : impl_(std::make_unique(package, node)) { +} + +FunctionNode::~FunctionNode() = default; + +const std::string& FunctionNode::getName() const { + return impl_->getName(); +} + +mccore::Value FunctionNode::execute(mccore::Controller* controller, + mccore::ValueList params) { + return impl_->execute(controller, params); +} + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/function/print.cpp b/src/function/print.cpp new file mode 100644 index 0000000..d124fd6 --- /dev/null +++ b/src/function/print.cpp @@ -0,0 +1,85 @@ +#include + +#include + +#include +#include +#include +#include + + +namespace mukyu { +namespace cablin { +namespace function { + + +namespace mccore = mukyu::cablin::core; + + +namespace { + + +const std::string FUNCTION_PRINT_NAME = "print"; + + +} + + +class FunctionPrint::Impl { +public: + Impl() = default; + ~Impl() = default; + + const std::string& getName() const { + return FUNCTION_PRINT_NAME; + } + + mccore::Value execute(mccore::Controller* controller, + mccore::ValueList params) { + for (const auto& param : params) { + switch (param.type()) { + case mccore::ValueType::BOOL: + if (param.as()) { + std::cout << "true\n"; + } else { + std::cout << "false\n"; + } + break; + case mccore::ValueType::INT: + std::cout << param.as() << "\n"; + break; + case mccore::ValueType::STRING: + std::cout << param.as() << "\n"; + break; + case mccore::ValueType::NONE: + std::cout << "none\n"; + break; + default: + throw mccore::CablinRuntimeException( + "Impl::execute: " + + mccore::VALUETYPE_STR_MAP.at(param.type()) + + "type not support"); + } + } + return static_cast(params.size()); + } +}; + +FunctionPrint::FunctionPrint() : impl_(std::make_unique()) { +} + +FunctionPrint::~FunctionPrint() = default; + +const std::string& FunctionPrint::getName() const { + return impl_->getName(); +} + +mccore::Value FunctionPrint::execute(mccore::Controller* controller, + mccore::ValueList params) { + return impl_->execute(controller, params); +} + + +} // namespace function +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/package/builtin_cast.cpp b/src/package/builtin_cast.cpp new file mode 100644 index 0000000..f1058d3 --- /dev/null +++ b/src/package/builtin_cast.cpp @@ -0,0 +1,55 @@ +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +namespace mccore = mukyu::cablin::core; +namespace mcfunc = mukyu::cablin::function; + + +class BuiltinCastPackage::Impl { +public: + Impl() { + func_ = { + std::make_shared(), + std::make_shared(), + std::make_shared(), + }; + } + + ~Impl() = default; + + void prepare(mccore::Controller* controller) { + controller->addPackage(name_); + + for (const auto& func : func_) { + controller->addFunction(name_, func); + } + } + +private: + const std::string name_ = BUILTIN_CAST_NAME; + std::vector> func_; +}; + + +BuiltinCastPackage::BuiltinCastPackage() : impl_(std::make_unique()) { +} + +BuiltinCastPackage::~BuiltinCastPackage() = default; + +void BuiltinCastPackage::prepare(mccore::Controller* controller) { + impl_->prepare(controller); +} + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/package/builtin_io.cpp b/src/package/builtin_io.cpp new file mode 100644 index 0000000..6addca5 --- /dev/null +++ b/src/package/builtin_io.cpp @@ -0,0 +1,53 @@ +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +namespace mccore = mukyu::cablin::core; +namespace mcfunc = mukyu::cablin::function; + + +class BuiltinIOPackage::Impl { +public: + Impl() { + func_ = { + std::make_shared(), + }; + } + + ~Impl() = default; + + void prepare(mccore::Controller* controller) { + controller->addPackage(name_); + + for (const auto& func : func_) { + controller->addFunction(name_, func); + } + } + +private: + const std::string name_ = BUILTIN_IO_NAME; + std::vector> func_; +}; + + +BuiltinIOPackage::BuiltinIOPackage() : impl_(std::make_unique()) { +} + +BuiltinIOPackage::~BuiltinIOPackage() = default; + +void BuiltinIOPackage::prepare(mccore::Controller* controller) { + impl_->prepare(controller); +} + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/package/builtin_op.cpp b/src/package/builtin_op.cpp new file mode 100644 index 0000000..056bac7 --- /dev/null +++ b/src/package/builtin_op.cpp @@ -0,0 +1,67 @@ +#include + +#include + +#include +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +namespace mccore = mukyu::cablin::core; +namespace mcfunc = mukyu::cablin::function; + + +class BuiltinOperatorPackage::Impl { +public: + Impl() { + func_ = { + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + std::make_shared(), + }; + } + + ~Impl() = default; + + void prepare(mccore::Controller* controller) { + controller->addPackage(name_); + + for (const auto& func : func_) { + controller->addFunction(name_, func); + } + } + +private: + const std::string name_ = BUILTIN_OP_NAME; + std::vector> func_; +}; + + +BuiltinOperatorPackage::BuiltinOperatorPackage() + : impl_(std::make_unique()) { +} + +BuiltinOperatorPackage::~BuiltinOperatorPackage() = default; + +void BuiltinOperatorPackage::prepare(mccore::Controller* controller) { + impl_->prepare(controller); +} + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/package/factory.cpp b/src/package/factory.cpp new file mode 100644 index 0000000..be23085 --- /dev/null +++ b/src/package/factory.cpp @@ -0,0 +1,46 @@ +#include + +#include + +#include +#include +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +namespace mccore = mukyu::cablin::core; +namespace mcpkg = mukyu::cablin::package; + + +const std::string YAML_FILEEXT = ".yaml"; + +mccore::PackagePtr createPackage(const std::string& name, + const std::string& dir) { + if (name == mcpkg::BUILTIN_IO_NAME) { + return std::make_shared(); + } else if (name == mcpkg::BUILTIN_OP_NAME) { + return std::make_shared(); + } else if (name == mcpkg::BUILTIN_CAST_NAME) { + return std::make_shared(); + } else if (!dir.empty()) { + auto pathDir = std::filesystem::path(dir); + auto fullPath = (pathDir / (name + YAML_FILEEXT)).string(); + return std::make_shared(name, fullPath); + } + + throw mccore::CablinIdentifierNotFoundException("craetePackage: " + name + + " not found"); +} + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/src/package/userdefine.cpp b/src/package/userdefine.cpp new file mode 100644 index 0000000..bbc7f59 --- /dev/null +++ b/src/package/userdefine.cpp @@ -0,0 +1,119 @@ +#include + +#include +#include + +#include + +#include + +#include + +#include + + +namespace mukyu { +namespace cablin { +namespace package { + + +namespace mccore = mukyu::cablin::core; +namespace mccmd = mukyu::cablin::command; +namespace mccommon = mukyu::cablin::common; +namespace mcfunc = mukyu::cablin::function; + + +class UserPackage::Impl { +public: + Impl(const std::string& name, const YAML::Node& root) : name_(name) { + loadNode(root); + } + + Impl(const std::string& name, const std::string& filename) : name_(name) { + auto root = YAML::LoadFile(filename); + loadNode(root); + } + + + ~Impl() = default; + + void prepare(mccore::Controller* controller) { + controller->addPackage(name_); + + for (const auto& cmd : globalCommand_) { + cmd->execute(controller); + } + + for (const auto& func : userFunc_) { + controller->addFunction(name_, func); + } + } + + std::vector usePackages() const { + return usedPackages_; + } + +private: + void loadNode(const YAML::Node& root) { + if (!root.IsSequence()) { + throw mccore::makeParsingException( + "Impl::loadNode: should be a list", root.Mark()); + } + + for (const auto& it : root) { + auto key = mccommon::getSingleKey(it); + if (!key) { + throw mccore::makeParsingException( + "Impl::loadNode: node must be single-key-map", it.Mark()); + } + + const auto& obj = it[key.value()]; + + if (key == "func") { + userFunc_.push_back( + std::make_shared(name_, obj)); + } else if (key == mccmd::COMMANDGLOBALVAR_KEY) { + globalCommand_.push_back( + std::make_unique(name_, obj)); + } else if (key == "import") { + usedPackages_.push_back(obj.as()); + } else { + throw mccore::makeParsingException( + "Impl::loadNode: " + key.value() + + "not match any pacakge command", + it.Mark()); + } + } + } + +private: + std::string name_; + + std::vector usedPackages_; + + std::vector globalCommand_; + std::vector> userFunc_; +}; + +UserPackage::UserPackage(const std::string& name, const std::string& filename) + : impl_(std::make_unique(name, filename)) { +} + +UserPackage::UserPackage(const std::string& name, const YAML::Node& root) + : impl_(std::make_unique(name, root)) { +} + +UserPackage::~UserPackage() = default; + +void UserPackage::prepare(mccore::Controller* controller) { + impl_->prepare(controller); +} + +std::vector UserPackage::usePackages() const { + return impl_->usePackages(); +} + + +} // namespace package +} // namespace cablin +} // namespace mukyu \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..4e32b20 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.6.2) + +enable_testing() + +find_package(GTest REQUIRED) +include_directories(${GTEST_INCLUDE_DIR}) + +include_directories(../include/) + +set(cablin_test_command_src + "src/command/test_if.cpp" + "src/command/test_scope.cpp" + "src/command/test_while.cpp" + "src/command/test_var.cpp") + +set(cablin_test_expr_src + "src/expr/test_const.cpp") + +set(cablin_test_function_src + "src/function/test_functor.cpp" + "src/function/test_node.cpp") + +add_executable(test + ${cablin_test_command_src} + ${cablin_test_expr_src} + ${cablin_test_function_src}) + +add_dependencies(test cablin) +target_link_libraries(test cablin ${GTEST_BOTH_LIBRARIES}) diff --git a/test/src/command/test_if.cpp b/test/src/command/test_if.cpp new file mode 100644 index 0000000..593b799 --- /dev/null +++ b/test/src/command/test_if.cpp @@ -0,0 +1,62 @@ +#include + +#include +#include + +namespace mccmd = mukyu::cablin::command; +namespace mccore = mukyu::cablin::core; + +TEST(COMMAND_IF, COMMAND_IF_WORK_THEN) { + std::string body = R"( +condition: + const: + type: bool + value: true +then: + - assign: + target: work + source: + const: + type: bool + value: true +)"; + auto node = YAML::Load(body); + std::string package = "main"; + + auto commandIf = mccmd::CommandIf(package, node); + + mccore::Controller controller; + controller.addLocalVar("work", false); + commandIf.execute(&controller); + + ASSERT_TRUE( + (controller.getLocalVar("work") == mccore::Value(true)).as()); +} + +TEST(COMMAND_IF, COMMAND_IF_WORK_ELSE) { + std::string body = R"( +condition: + const: + type: bool + value: false +else: + - assign: + target: work + source: + const: + type: bool + value: true +)"; + auto node = YAML::Load(body); + + std::string package = "main"; + + auto commandIf = mccmd::CommandIf(package, node); + + mccore::Controller controller; + controller.addLocalVar("work", false); + commandIf.execute(&controller); + + ASSERT_TRUE( + (controller.getLocalVar("work") == mccore::Value(true)).as()); +} diff --git a/test/src/command/test_scope.cpp b/test/src/command/test_scope.cpp new file mode 100644 index 0000000..d8060ac --- /dev/null +++ b/test/src/command/test_scope.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include +#include + +namespace mccmd = mukyu::cablin::command; +namespace mccore = mukyu::cablin::core; + +TEST(COMMAND_BLOCK, COMMAND_BLOCK) { + std::string body = R"( +- var: + name: var1 + type: int + default_value: 1 +- block: + - var: + name: var1 + type: int + default_value: 2 + - assign: + target: a + source: + get: var1 +)"; + auto node = YAML::Load(body); + + std::string package = "main"; + + auto commandBlock = mccmd::CommandBlock(package, node); + return; + + mccore::Controller controller; + controller.addPackage(package); + controller.addPackageVar(package, "a", 0); + + commandBlock.execute(&controller); + + auto ans = mccore::Value(2); + auto res = controller.getPackageVar(package, "a"); + + ASSERT_TRUE((res == ans).as()); +} diff --git a/test/src/command/test_var.cpp b/test/src/command/test_var.cpp new file mode 100644 index 0000000..cc81d99 --- /dev/null +++ b/test/src/command/test_var.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include + +namespace mccmd = mukyu::cablin::command; +namespace mccore = mukyu::cablin::core; + +TEST(COMMAND_VAR, COMMAND_VAR_TESTINT_1) { + auto node = YAML::Load( + "type: int\n" + "name: a\n" + "default_value: 123\n"); + + auto commandVar = mccmd::CommandVar(node); + + mccore::Controller controller; + commandVar.execute(&controller); + + auto ans = mccore::Value(123); + auto res = controller.getLocalVar("a"); + + ASSERT_TRUE((res == ans).as()); +} + +TEST(COMMAND_VAR, COMMAND_VAR_TESTINT_2) { + auto node = YAML::Load( + "type: bool\n" + "name: a\n" + "default_value: false\n"); + + auto commandVar = mccmd::CommandVar(node); + + mccore::Controller controller; + commandVar.execute(&controller); + + auto ans = mccore::Value(false); + auto res = controller.getLocalVar("a"); + + ASSERT_TRUE((res == ans).as()); +} diff --git a/test/src/command/test_while.cpp b/test/src/command/test_while.cpp new file mode 100644 index 0000000..7b7641c --- /dev/null +++ b/test/src/command/test_while.cpp @@ -0,0 +1,58 @@ +#include + +#include +#include + +namespace mccmd = mukyu::cablin::command; +namespace mccore = mukyu::cablin::core; + +TEST(COMMAND_WHILE, COMMAND_WHILE_WORK) { + std::string body = R"( +condition: + get: run +body: + - assign: + target: run + source: + const: + type: bool + value: false +)"; + auto node = YAML::Load(body); + + std::string package = "main"; + + auto commandIf = mccmd::CommandWhile(package, node); + + mccore::Controller controller; + controller.addLocalVar("run", true); + commandIf.execute(&controller); + + ASSERT_TRUE((controller.getLocalVar("run") == false).as()); +} + +TEST(COMMAND_WHILE, COMMAND_WHILE_BREAK) { + std::string body = R"( +condition: + get: run +body: + - break: + - assign: + target: run + source: + const: + type: bool + value: false +)"; + auto node = YAML::Load(body); + + std::string package = "main"; + + auto commandIf = mccmd::CommandWhile(package, node); + + mccore::Controller controller; + controller.addLocalVar("run", true); + commandIf.execute(&controller); + + ASSERT_TRUE((controller.getLocalVar("run") == true).as()); +} diff --git a/test/src/expr/test_const.cpp b/test/src/expr/test_const.cpp new file mode 100644 index 0000000..03457ba --- /dev/null +++ b/test/src/expr/test_const.cpp @@ -0,0 +1,31 @@ +#include + +#include +#include + +namespace mcexpr = mukyu::cablin::expr; +namespace mccore = mukyu::cablin::core; + +TEST(EXPR_CONST, EXPR_CONST_BOOL) { + auto node = YAML::Load( + "type: bool\n" + "value: false\n"); + + auto exprConst = mcexpr::ExprConst(node); + + mccore::Controller controller; + + auto value = exprConst.compute(&controller); + + ASSERT_TRUE((value == mccore::Value(false)).as()); +} + +TEST(EXPR_CONST, EXPR_CONST_NONE) { + auto exprConst = mcexpr::ExprConst(); + + mccore::Controller controller; + + auto value = exprConst.compute(&controller); + + ASSERT_EQ(value.type(), mccore::ValueType::NONE); +} diff --git a/test/src/function/test_functor.cpp b/test/src/function/test_functor.cpp new file mode 100644 index 0000000..541339f --- /dev/null +++ b/test/src/function/test_functor.cpp @@ -0,0 +1,26 @@ +#include + +#include +#include + +namespace mcfunc = mukyu::cablin::function; +namespace mccore = mukyu::cablin::core; + +TEST(FUNCTION_FUNCTOR, FUNCTION_FUNCTOR_WORK) { + std::string package = "main"; + bool flag = false; + auto func = [&flag](mccore::ValueList) { + flag = true; + return mccore::Value(1); + }; + + auto funcNode = mcfunc::FunctionFunctor("functor", func); + + mccore::Controller controller; + controller.addPackage(package); + controller.addPackageVar(package, "run", false); + + funcNode.execute(&controller, {}); + + ASSERT_TRUE(flag); +} diff --git a/test/src/function/test_node.cpp b/test/src/function/test_node.cpp new file mode 100644 index 0000000..0d396d3 --- /dev/null +++ b/test/src/function/test_node.cpp @@ -0,0 +1,89 @@ +#include + +#include +#include + +namespace mcfunc = mukyu::cablin::function; +namespace mccore = mukyu::cablin::core; + +TEST(FUNCTION_NODE, FUNCTION_NODE_WORK) { + std::string body = R"( +name: myFunc +params: [] +body: + - assign: + target: run + source: + const: + type: bool + value: true +)"; + auto node = YAML::Load(body); + + std::string package = "main"; + + auto funcNode = mcfunc::FunctionNode(package, node); + + mccore::Controller controller; + controller.addPackage(package); + controller.addPackageVar(package, "run", false); + + funcNode.execute(&controller, {}); + + auto result = controller.getPackageVar(package, "run") == true; + ASSERT_TRUE(result.as()); +} + +TEST(FUNCTION_NODE, FUNCTION_NODE_PARAM) { + std::string body = R"( +name: myFunc +params: + - type: bool + name: ok +body: + - assign: + target: run + source: + get: ok +)"; + auto node = YAML::Load(body); + + std::string package = "main"; + + auto funcNode = mcfunc::FunctionNode(package, node); + + mccore::Controller controller; + controller.addPackage(package); + controller.addPackageVar(package, "run", false); + + funcNode.execute(&controller, {true}); + + auto result = controller.getPackageVar(package, "run") == true; + ASSERT_TRUE(result.as()); +} + +TEST(FUNCTION_NODE, FUNCTION_NODE_RETURN) { + std::string body = R"( +name: myFunc +params: [] +body: + - return: + const: + type: int + value: 123 +)"; + auto node = YAML::Load(body); + + std::string package = "main"; + + auto funcNode = mcfunc::FunctionNode(package, node); + + mccore::Controller controller; + controller.addPackage(package); + controller.addPackageVar(package, "run", false); + + auto returnValue = funcNode.execute(&controller, {}); + + auto result = returnValue == 123; + ASSERT_TRUE(result.as()); +}