commit 2685cedba484091cc66416df7edf0ded77e74427 Author: Stepan Usatiuk Date: Sat Dec 23 10:50:28 2023 +0100 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3649d6d --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/cmake.xml b/.idea/cmake.xml new file mode 100644 index 0000000..802ba29 --- /dev/null +++ b/.idea/cmake.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..0a9aea9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/psil.iml b/.idea/psil.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/psil.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..12c6a44 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.27) +project(psil) + +if (SANITIZE STREQUAL "YES") + message(WARNING "Enabling sanitizers!") + add_compile_options(-Wall -Wextra -pedantic -Wshadow -Wformat=2 -Wfloat-equal -D_GLIBCXX_DEBUG -Wconversion -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2) + add_compile_options(-fsanitize=address -fsanitize=undefined -fno-sanitize-recover) + add_link_options(-fsanitize=address -fsanitize=undefined -fno-sanitize-recover) +endif () + +if (CMAKE_BUILD_TYPE STREQUAL "Release") + add_compile_options(-flto) + add_link_options(-flto) +endif () + +if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_options(-O3) + add_link_options(-O3) +endif () + +set(CMAKE_CXX_STANDARD 20) + +add_subdirectory(src) + +enable_testing() + +add_subdirectory(test) \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..b7abc67 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,8 @@ +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_subdirectory(vm) + +add_executable(psil main.cpp) + +target_link_libraries(psil PRIVATE vm) diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..484a4fc --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,6 @@ +#include +#include "vm/includes/VM.h" + +int main() { + return 0; +} diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt new file mode 100644 index 0000000..7c846f0 --- /dev/null +++ b/src/vm/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(vm src/VM.cpp src/Cell.cpp) + +target_include_directories(vm PUBLIC includes) diff --git a/src/vm/includes/Cell.h b/src/vm/includes/Cell.h new file mode 100644 index 0000000..b35e5f4 --- /dev/null +++ b/src/vm/includes/Cell.h @@ -0,0 +1,74 @@ +// +// Created by Stepan Usatiuk on 22.12.2023. +// + +#ifndef PSIL_CELL_H +#define PSIL_CELL_H + +#include +#include + +enum class CellType { + INT, + CONS +}; + +struct Cell { + explicit Cell(CellType type) : _type(type) {} + + virtual ~Cell() = 0; + + CellType _type; +}; + +struct IntCell : public Cell { + IntCell() : Cell(CellType::INT) {} + + IntCell(int64_t val) : Cell(CellType::INT), _val(val) {} + + int64_t _val{}; +}; + +struct CommandCell : public IntCell { + enum class CommandNum { + NIL = 1, + LDC = 2, + LD = 3, + SEL = 4, + JOIN = 5, + LDF = 6, + AP = 7, + RET = 8, + DUM = 9, + RAP = 10, + STOP = 11, + + ADD = 100, + SUB = 101, + + READCHAR = 201, + PUTCHAR = 202, + END = 1000 + }; + + CommandCell(CommandNum cmd) : IntCell(static_cast(cmd)) {} + + CommandNum intToCmd() { + assert((_val > 0 && static_cast(_val) <= CommandNum::END)); + return static_cast(_val); + } + +}; + +struct ConsCell : public Cell { + ConsCell() : Cell(CellType::CONS) {} + + ConsCell(Cell *car) : Cell(CellType::CONS), _car(car) {} + + ConsCell(Cell *car, Cell *cdr) : Cell(CellType::CONS), _car(car), _cdr(cdr) {} + + Cell *_car = nullptr; + Cell *_cdr = nullptr; +}; + +#endif //PSIL_CELL_H diff --git a/src/vm/includes/VM.h b/src/vm/includes/VM.h new file mode 100644 index 0000000..6eed788 --- /dev/null +++ b/src/vm/includes/VM.h @@ -0,0 +1,73 @@ +// +// Created by Stepan Usatiuk on 22.12.2023. +// + +#ifndef PSIL_VM_H +#define PSIL_VM_H + +#include +#include +#include + +#include "Cell.h" + +class VM { +public: + VM(std::istream &instream = std::cin, std::ostream &outstream = std::cout); + + void run(); + + void step(); + +// template +// void appendCommand(T cell) { +// push(_c, makeCell(std::move(cell))); +// } + + template + void appendCommand(T *cell) { + push(_c, cell); + } + + template + CT *makeCell(Args... args) { + return static_cast(_cells.emplace_back(new CT(std::forward(args)...))); + } + + Cell *car(ConsCell *cell) { + return cell->_car; + } + + Cell *cdr(ConsCell *cell) { + return cell->_cdr; + } + + ConsCell *cons(Cell *car, Cell *cdr) { + return dynamic_cast(makeCell(car, cdr)); + } + + Cell *pop(ConsCell *&what) { + Cell *ret = what->_car; + what = dynamic_cast(cdr(what)); + return ret; + } + + Cell *push(ConsCell *&what, Cell *toAppend) { + what = cons(toAppend, what); + return what; + } + +private: + std::vector _cells; + ConsCell *_s = nullptr; + ConsCell *_e = nullptr; + ConsCell *_c = nullptr; + ConsCell *_d = nullptr; + bool _stop = false; + + std::istream &_instream; + std::ostream &_outstream; +}; + + +#endif //PSIL_VM_H diff --git a/src/vm/src/Cell.cpp b/src/vm/src/Cell.cpp new file mode 100644 index 0000000..eb99cfe --- /dev/null +++ b/src/vm/src/Cell.cpp @@ -0,0 +1,7 @@ +// +// Created by Stepan Usatiuk on 22.12.2023. +// + +#include "../includes/Cell.h" + +Cell::~Cell() = default; \ No newline at end of file diff --git a/src/vm/src/VM.cpp b/src/vm/src/VM.cpp new file mode 100644 index 0000000..c7f6eed --- /dev/null +++ b/src/vm/src/VM.cpp @@ -0,0 +1,101 @@ +// +// Created by Stepan Usatiuk on 22.12.2023. +// + +#include "../includes/VM.h" + +#include +#include + +void VM::run() { + while (!_stop) step(); +} + +void VM::step() { + CommandCell *popped = dynamic_cast(pop(_c)); + + switch (popped->intToCmd()) { + case CommandCell::CommandNum::NIL: { + push(_s, nullptr); + break; + } + case CommandCell::CommandNum::LDC: { + IntCell *popped2 = dynamic_cast(pop(_c)); + push(_s, popped2); + break; + } + case CommandCell::CommandNum::LD: { + ConsCell *popped2 = dynamic_cast(pop(_c)); + + int64_t frame = dynamic_cast(popped2->_car)->_val; + int64_t arg = dynamic_cast(popped2->_cdr)->_val; + // todo + + break; + } + case CommandCell::CommandNum::SEL: { + IntCell *popped2 = dynamic_cast(pop(_s)); + ConsCell *ct = dynamic_cast(pop(_c)); + ConsCell *cf = dynamic_cast(pop(_c)); + ConsCell *ret = _c; + push(_d, ret); + if (popped2->_val > 0) { + _c = ct; + } else { + _c = cf; + } + + break; + } + case CommandCell::CommandNum::JOIN: { + ConsCell *ret = dynamic_cast(pop(_d)); + _c = ret; + break; + } + case CommandCell::CommandNum::LDF: { + break; + } + case CommandCell::CommandNum::AP: { + break; + } + case CommandCell::CommandNum::RET: { + break; + } + case CommandCell::CommandNum::DUM: { + break; + } + case CommandCell::CommandNum::RAP: { + break; + } + case CommandCell::CommandNum::STOP: { + _stop = true; + break; + } + case CommandCell::CommandNum::ADD: { + break; + } + case CommandCell::CommandNum::SUB: { + break; + } + case CommandCell::CommandNum::END: { + break; + } + case CommandCell::CommandNum::READCHAR: + char c; + _instream >> c; + push(_s, makeCell(c)); + break; + case CommandCell::CommandNum::PUTCHAR: + IntCell *popped2 = dynamic_cast(pop(_s)); + _outstream << (char) popped2->_val; + break; + } + +} + +VM::VM(std::istream &instream, std::ostream &outstream) : _instream(instream), _outstream(outstream) { + _s = dynamic_cast(makeCell(nullptr)); + _e = dynamic_cast(makeCell(nullptr)); + _c = dynamic_cast(makeCell(nullptr)); + _d = dynamic_cast(makeCell(nullptr)); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..26381c9 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,13 @@ +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +add_subdirectory(vm) \ No newline at end of file diff --git a/test/vm/CMakeLists.txt b/test/vm/CMakeLists.txt new file mode 100644 index 0000000..46236c9 --- /dev/null +++ b/test/vm/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable( + VMTest + VMTest.cpp +) +target_link_libraries( + VMTest + vm + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(VMTest) \ No newline at end of file diff --git a/test/vm/VMTest.cpp b/test/vm/VMTest.cpp new file mode 100644 index 0000000..c517376 --- /dev/null +++ b/test/vm/VMTest.cpp @@ -0,0 +1,66 @@ +#include + +#include "VM.h" + +TEST(VMTest, BasicHello) { + std::stringstream ssin; + std::stringstream ssout; + { + VM vm(ssin, ssout); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::STOP)); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::PUTCHAR)); + vm.appendCommand(vm.makeCell('h')); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::LDC)); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::NIL)); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "h"); +} + +TEST(VMTest, SelTest) { + std::stringstream ssin; + std::stringstream ssout; + { + VM vm(ssin, ssout); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::STOP)); + + // True branch true test + ConsCell *tbtt = vm.makeCell(vm.makeCell(CommandCell::CommandNum::JOIN)); + vm.push(tbtt, vm.makeCell(CommandCell::CommandNum::PUTCHAR)); + vm.push(tbtt, vm.makeCell('1')); + vm.push(tbtt, vm.makeCell(CommandCell::CommandNum::LDC)); + + // False branch true test + ConsCell *fbtt = vm.makeCell(vm.makeCell(CommandCell::CommandNum::JOIN)); + vm.push(fbtt, vm.makeCell(CommandCell::CommandNum::PUTCHAR)); + vm.push(fbtt, vm.makeCell('2')); + vm.push(fbtt, vm.makeCell(CommandCell::CommandNum::LDC)); + + // True branch false test + ConsCell *tbft = vm.makeCell(vm.makeCell(CommandCell::CommandNum::JOIN)); + vm.push(tbft, vm.makeCell(CommandCell::CommandNum::PUTCHAR)); + vm.push(tbft, vm.makeCell('3')); + vm.push(tbft, vm.makeCell(CommandCell::CommandNum::LDC)); + + // False branch false test + ConsCell *fbft = vm.makeCell(vm.makeCell(CommandCell::CommandNum::JOIN)); + vm.push(fbft, vm.makeCell(CommandCell::CommandNum::PUTCHAR)); + vm.push(fbft, vm.makeCell('4')); + vm.push(fbft, vm.makeCell(CommandCell::CommandNum::LDC)); + + vm.appendCommand(fbft); + vm.appendCommand(tbft); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::SEL)); + vm.appendCommand(vm.makeCell(0)); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::LDC)); + vm.appendCommand(fbtt); + vm.appendCommand(tbtt); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::SEL)); + vm.appendCommand(vm.makeCell(1)); + vm.appendCommand(vm.makeCell(CommandCell::CommandNum::LDC)); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "14"); +} \ No newline at end of file