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
| |