mirror of
https://github.com/usatiuk/psil.git
synced 2025-10-28 18:57:48 +01:00
simple secd parser
This commit is contained in:
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal 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>
|
||||
@@ -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
46
src/vm/includes/Parser.h
Normal 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
105
src/vm/src/Parser.cpp
Normal 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();
|
||||
}
|
||||
|
||||
@@ -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(VMWithParserTest)
|
||||
|
||||
30
test/vm/VMWithParserTest.cpp
Normal file
30
test/vm/VMWithParserTest.cpp
Normal 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");
|
||||
}
|
||||
Reference in New Issue
Block a user