simple secd parser

This commit is contained in:
2023-12-23 19:03:13 +01:00
parent 3bace25d46
commit 43c41282f3
6 changed files with 204 additions and 2 deletions

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

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

46
src/vm/includes/Parser.h Normal file
View File

@@ -0,0 +1,46 @@
//
// Created by Stepan Usatiuk on 23.12.2023.
//
#ifndef PSIL_PARSER_H
#define PSIL_PARSER_H
#include <string_view>
#include <string>
#include <queue>
class VM;
struct Cell;
class Parser {
public:
explicit Parser(VM &vm);
void loadSecd(std::string_view input);
private:
void compileBody(const std::function<void(Cell *)> &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<std::string> _tokens;
};
Tokenizer _tokenizer;
};
#endif //PSIL_PARSER_H

105
src/vm/src/Parser.cpp Normal file
View File

@@ -0,0 +1,105 @@
//
// Created by Stepan Usatiuk on 23.12.2023.
//
#include "Parser.h"
#include <ranges>
#include <stack>
#include "VM.h"
Parser::Parser(VM &vm) : _vm(vm) {}
void Parser::loadSecd(std::string_view input) {
_tokenizer.load(input);
std::stack<Cell *> out;
compileBody([&out](Cell *cmd) { out.emplace(cmd); });
while (!out.empty()) {
this->_vm.appendCommand(out.top());
out.pop();
}
}
void Parser::compileBody(const std::function<void(Cell *)> &sink) {
auto token = _tokenizer.getNext();
if (token != "(") throw std::invalid_argument("Expected (");
while (token != ")") {
token = _tokenizer.getNext();
if (token == "NIL") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::NIL));
} else if (token == "LDC") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::LDC));
sink(_vm.makeCell<IntCell>(std::stoi(_tokenizer.getNext())));
} else if (token == "LD") {
sink(_vm.makeCell<CommandCell>(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<ConsCell>(_vm.makeCell<IntCell>(frame), _vm.makeCell<IntCell>(loc)));
} else if (token == "LDF") {
std::stack<Cell *> out;
compileBody([&out](Cell *cmd) { out.emplace(cmd); });
if (out.empty()) throw std::invalid_argument("Function body empty");
ConsCell *fntop = _vm.makeCell<ConsCell>(out.top());
out.pop();
while (!out.empty()) {
_vm.push(fntop, out.top());
out.pop();
}
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::LDF));
sink(fntop);
} else if (token == "CONS") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::CONS));
} else if (token == "AP") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::AP));
} else if (token == "ADD") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::ADD));
} else if (token == "RET") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::RET));
} else if (token == "PUTCHAR") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::PUTCHAR));
} else if (token == "PUTNUM") {
sink(_vm.makeCell<CommandCell>(CommandCell::CommandNum::PUTNUM));
} else if (token == "STOP") {
sink(_vm.makeCell<CommandCell>(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();
}

View File

@@ -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)
gtest_discover_tests(VMTest)
gtest_discover_tests(VMWithParserTest)

View File

@@ -0,0 +1,30 @@
#include <gtest/gtest.h>
#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");
}