From 43c41282f33f7d8640ac6059dfc824c7386358ed Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Sat, 23 Dec 2023 19:03:13 +0100 Subject: [PATCH] simple secd parser --- .idea/vcs.xml | 6 ++ src/vm/CMakeLists.txt | 6 +- src/vm/includes/Parser.h | 46 +++++++++++++++ src/vm/src/Parser.cpp | 105 +++++++++++++++++++++++++++++++++++ test/vm/CMakeLists.txt | 13 ++++- test/vm/VMWithParserTest.cpp | 30 ++++++++++ 6 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 .idea/vcs.xml create mode 100644 src/vm/includes/Parser.h create mode 100644 src/vm/src/Parser.cpp create mode 100644 test/vm/VMWithParserTest.cpp diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 7c846f0..522c655 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -1,3 +1,7 @@ -add_library(vm src/VM.cpp src/Cell.cpp) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_library(vm src/VM.cpp src/Cell.cpp + src/Parser.cpp) target_include_directories(vm PUBLIC includes) diff --git a/src/vm/includes/Parser.h b/src/vm/includes/Parser.h new file mode 100644 index 0000000..66c5707 --- /dev/null +++ b/src/vm/includes/Parser.h @@ -0,0 +1,46 @@ +// +// Created by Stepan Usatiuk on 23.12.2023. +// + +#ifndef PSIL_PARSER_H +#define PSIL_PARSER_H + +#include +#include +#include + +class VM; + +struct Cell; + +class Parser { +public: + explicit Parser(VM &vm); + + void loadSecd(std::string_view input); + +private: + + void compileBody(const std::function &sink); + + VM &_vm; + + class Tokenizer { + public: + void load(std::string_view input); + + std::string getNext(); + + std::string_view peek() const; + + bool empty() const; + + private: + std::queue _tokens; + }; + + Tokenizer _tokenizer; +}; + + +#endif //PSIL_PARSER_H diff --git a/src/vm/src/Parser.cpp b/src/vm/src/Parser.cpp new file mode 100644 index 0000000..7ff7fbf --- /dev/null +++ b/src/vm/src/Parser.cpp @@ -0,0 +1,105 @@ +// +// Created by Stepan Usatiuk on 23.12.2023. +// + +#include "Parser.h" + +#include +#include + +#include "VM.h" + +Parser::Parser(VM &vm) : _vm(vm) {} + +void Parser::loadSecd(std::string_view input) { + _tokenizer.load(input); + std::stack out; + + compileBody([&out](Cell *cmd) { out.emplace(cmd); }); + + while (!out.empty()) { + this->_vm.appendCommand(out.top()); + out.pop(); + } +} + +void Parser::compileBody(const std::function &sink) { + auto token = _tokenizer.getNext(); + if (token != "(") throw std::invalid_argument("Expected ("); + + while (token != ")") { + token = _tokenizer.getNext(); + + if (token == "NIL") { + sink(_vm.makeCell(CommandCell::CommandNum::NIL)); + } else if (token == "LDC") { + sink(_vm.makeCell(CommandCell::CommandNum::LDC)); + sink(_vm.makeCell(std::stoi(_tokenizer.getNext()))); + } else if (token == "LD") { + sink(_vm.makeCell(CommandCell::CommandNum::LD)); + if (_tokenizer.getNext() != "(") throw std::invalid_argument("Expected ("); + int64_t frame = std::stoi(_tokenizer.getNext()); + if (_tokenizer.getNext() != ".") throw std::invalid_argument("Expected ."); + int64_t loc = std::stoi(_tokenizer.getNext()); + if (_tokenizer.getNext() != ")") throw std::invalid_argument("Expected )"); + sink(_vm.makeCell(_vm.makeCell(frame), _vm.makeCell(loc))); + } else if (token == "LDF") { + std::stack out; + + compileBody([&out](Cell *cmd) { out.emplace(cmd); }); + + if (out.empty()) throw std::invalid_argument("Function body empty"); + + ConsCell *fntop = _vm.makeCell(out.top()); + out.pop(); + + while (!out.empty()) { + _vm.push(fntop, out.top()); + out.pop(); + } + sink(_vm.makeCell(CommandCell::CommandNum::LDF)); + sink(fntop); + } else if (token == "CONS") { + sink(_vm.makeCell(CommandCell::CommandNum::CONS)); + } else if (token == "AP") { + sink(_vm.makeCell(CommandCell::CommandNum::AP)); + } else if (token == "ADD") { + sink(_vm.makeCell(CommandCell::CommandNum::ADD)); + } else if (token == "RET") { + sink(_vm.makeCell(CommandCell::CommandNum::RET)); + } else if (token == "PUTCHAR") { + sink(_vm.makeCell(CommandCell::CommandNum::PUTCHAR)); + } else if (token == "PUTNUM") { + sink(_vm.makeCell(CommandCell::CommandNum::PUTNUM)); + } else if (token == "STOP") { + sink(_vm.makeCell(CommandCell::CommandNum::STOP)); + } else { + if (token != ")")throw std::invalid_argument("Unknown token " + token); + } + } +} + +std::string Parser::Tokenizer::getNext() { + std::string ret = std::move(_tokens.front()); + _tokens.pop(); + return ret; +} + +std::string_view Parser::Tokenizer::peek() const { + return _tokens.back(); +} + +void Parser::Tokenizer::load(std::string_view input) { + for (const auto &w: input + | std::views::split(' ') + | std::views::transform([](auto &&rng) { + return std::string_view(&*rng.begin(), std::ranges::distance(rng)); + })) { + _tokens.emplace(w); + } +} + +bool Parser::Tokenizer::empty() const { + return _tokens.empty(); +} + diff --git a/test/vm/CMakeLists.txt b/test/vm/CMakeLists.txt index 46236c9..75a50a4 100644 --- a/test/vm/CMakeLists.txt +++ b/test/vm/CMakeLists.txt @@ -8,5 +8,16 @@ target_link_libraries( GTest::gtest_main ) +add_executable( + VMWithParserTest + VMWithParserTest.cpp +) +target_link_libraries( + VMWithParserTest + vm + GTest::gtest_main +) + include(GoogleTest) -gtest_discover_tests(VMTest) \ No newline at end of file +gtest_discover_tests(VMTest) +gtest_discover_tests(VMWithParserTest) diff --git a/test/vm/VMWithParserTest.cpp b/test/vm/VMWithParserTest.cpp new file mode 100644 index 0000000..9a0ce9c --- /dev/null +++ b/test/vm/VMWithParserTest.cpp @@ -0,0 +1,30 @@ +#include + +#include "VM.h" +#include "Parser.h" + +TEST(VMWithParserTest, BasicHello) { + std::stringstream ssin; + std::stringstream ssout; + { + VM vm(ssin, ssout); + Parser parser(vm); + parser.loadSecd("( LDC 104 PUTCHAR STOP )"); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "h"); +} + +TEST(VMWithParserTest, BasicFunction) { + std::stringstream ssin; + std::stringstream ssout; + { + VM vm(ssin, ssout); + Parser parser(vm); + parser.loadSecd("( NIL LDC 1 CONS LDC 2 CONS LDF ( LD ( 1 . 1 ) LD ( 1 . 2 ) ADD RET ) AP PUTNUM STOP )"); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "3"); +} \ No newline at end of file