Implements very simple REPL, fixes several issues found with it.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Deukhoofd 2021-01-02 17:41:53 +01:00
parent de15173b0b
commit 9109b7513a
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
8 changed files with 317 additions and 11 deletions

View File

@ -12,6 +12,7 @@ endif ()
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
option(TESTS "Whether the test executable should be build as well." OFF) option(TESTS "Whether the test executable should be build as well." OFF)
option(REPL "Whether the repl should be build as well." OFF)
option(STATICC "Whether gcc and stdc++ should be linked statically to the library." OFF) option(STATICC "Whether gcc and stdc++ should be linked statically to the library." OFF)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@ -37,5 +38,11 @@ if (TESTS)
target_compile_definitions(MalachScriptTests PRIVATE TESTS_BUILD) target_compile_definitions(MalachScriptTests PRIVATE TESTS_BUILD)
endif () endif ()
if (REPL)
# Create Test executable
file(GLOB_RECURSE REPL_FILES "repl/*.cpp" "repl/*.hpp")
add_executable(MalachScriptREPL ${REPL_FILES})
target_link_libraries(MalachScriptREPL PUBLIC MalachScript curses)
endif ()

180
repl/main.cpp Normal file
View File

@ -0,0 +1,180 @@
#include <iostream>
#include <ncurses.h>
#include <sstream>
#include "../src/Parser/Lexer/Lexer.hpp"
#include "../src/Parser/Parser.hpp"
void ParseAndUpdate(const std::vector<std::u8string> lines, WINDOW* diagnosticsWindow, WINDOW* parsedWindow) {
std::u8string script;
for (const auto& line : lines) {
script += line;
script += '\n';
}
auto logger = MalachScript::Diagnostics::Logger();
auto lexer = MalachScript::Parser::Lexer(u8"diag", script, &logger);
const auto* firstToken = lexer.Lex();
[[maybe_unused]] const auto* parsedResult = MalachScript::Parser::Parser::Parse(firstToken, u8"diag", &logger);
wclear(diagnosticsWindow);
waddstr(diagnosticsWindow, (char*)script.c_str());
for (const auto& diag : logger.GetMessages()) {
waddch(diagnosticsWindow, '[');
waddstr(diagnosticsWindow, "Error");
waddch(diagnosticsWindow, ']');
waddch(diagnosticsWindow, ' ');
waddstr(diagnosticsWindow,
MalachScript::Diagnostics::DiagnosticTypeHelper::ToEnglishString(diag.GetType()).c_str());
waddstr(diagnosticsWindow, " - ");
auto start = diag.GetSpan().GetStart() - 3;
if (start < 0)
start = 0;
if (start > script.size())
start = script.size() - 1;
auto end = diag.GetSpan().GetEnd() + 3;
if (end >= script.size())
end = script.size() - 1;
waddstr(diagnosticsWindow, (char*)script.substr(start, end - start).c_str());
waddch(diagnosticsWindow, '\n');
}
wrefresh(diagnosticsWindow);
wclear(parsedWindow);
if (logger.GetMessages().size() == 0) {
std::stringstream ss;
parsedResult->Stringify(ss, 0);
waddstr(parsedWindow, ss.str().c_str());
}
wrefresh(parsedWindow);
}
int main([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[]) {
initscr();
cbreak();
noecho();
keypad(stdscr, true);
set_tabsize(4);
int maxX;
int maxY;
getmaxyx(stdscr, maxY, maxX);
wresize(stdscr, maxY, maxX);
wrefresh(stdscr);
const int inputFieldSize = 30;
wborder(stdscr, 0, 0, 0, 0, 0, 0, 0, 0);
wmove(stdscr, 31, 1);
whline(stdscr, ACS_HLINE, maxX - 2);
wmove(stdscr, 32, maxX / 2);
wvline(stdscr, ACS_VLINE, 25);
wrefresh(stdscr);
auto* inputWindow = newwin(inputFieldSize, maxX - 3, 1, 2);
keypad(inputWindow, true);
auto* diagnosticsWindow = newwin(25, (maxX - 4) / 2, 32, 2);
auto* parsedResultWindow = newwin(25, (maxX - 4) / 2 - 2, 32, (maxX - 4) / 2 + 4);
wrefresh(parsedResultWindow);
int row = 0;
int col = 0;
std::vector<std::u8string> lines = {u8""};
int c;
bool running = true;
while (running) {
c = wgetch(inputWindow);
switch (c) {
case 27: running = false; break;
case KEY_LEFT: {
if (col > 0) {
wmove(inputWindow, row, --col);
}
break;
}
case KEY_RIGHT: {
auto& line = lines[row];
if (col < (int)line.size()) {
wmove(inputWindow, row, ++col);
}
break;
}
case KEY_UP:
if (row > 0) {
wmove(inputWindow, --row, col);
}
wrefresh(stdscr);
break;
case KEY_DOWN:
if (row < (int)lines.size() - 1) {
wmove(inputWindow, ++row, col);
}
break;
case KEY_BACKSPACE:
case 127:
if (col > 0) {
mvwdelch(inputWindow, row, --col);
auto& line = lines[row];
line = line.erase(col, 1);
ParseAndUpdate(lines, diagnosticsWindow, parsedResultWindow);
}
break;
case 10:
case KEY_ENTER:
lines.insert(lines.begin() + row + 1, u8"");
row++;
col = 0;
for (size_t i = row - 1; i < lines.size(); i++) {
wmove(inputWindow, i, 0);
wclrtoeol(inputWindow);
waddstr(inputWindow, (char*)lines[i].c_str());
}
wmove(inputWindow, row, col);
break;
case 9:
case KEY_STAB: {
wmove(inputWindow, row, 0);
wclrtoeol(inputWindow);
auto& line = lines[row];
if ((int)line.length() <= col) {
line += u8" ";
} else {
for (size_t i = 0; i < 4; i++) {
line.insert(line.begin() + col, u8' ');
}
}
waddstr(inputWindow, (char*)line.c_str());
col += 4;
wmove(inputWindow, row, col);
ParseAndUpdate(lines, diagnosticsWindow, parsedResultWindow);
break;
}
default: {
wmove(inputWindow, row, 0);
auto& line = lines[row];
if ((int)line.length() <= col) {
line += (char8_t)c;
} else {
line.insert(line.begin() + col, c);
}
waddstr(inputWindow, (char*)line.c_str());
col++;
wmove(inputWindow, row, col);
ParseAndUpdate(lines, diagnosticsWindow, parsedResultWindow);
break;
}
}
wrefresh(inputWindow);
}
endwin();
return EXIT_SUCCESS;
}

View File

@ -35,21 +35,26 @@ namespace MalachScript {
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330,
0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D};
static int constexpr CalcLength(const char8_t* str) { return *str ? 1 + CalcLength(str + 1) : 0; }
static std::u8string empty; static std::u8string empty;
class Identifier { class Identifier {
std::u8string_view _str; const char8_t* _str;
size_t _length;
uint32_t _hash; uint32_t _hash;
public: public:
Identifier() : _str(empty), _hash(0) {} Identifier() : _str(nullptr), _length(0), _hash(0) {}
constexpr Identifier(const char8_t* c) : _str(c), _hash(Hash(c)) {} constexpr Identifier(const char8_t* c) : _str(c), _length(CalcLength(c)), _hash(Hash(c)) {}
Identifier(const std::u8string_view& c) : _str(c), _hash(Hash(c)) {} constexpr Identifier(const char8_t* c, size_t length, uint32_t hash) : _str(c), _length(length), _hash(hash) {}
Identifier(const std::u8string_view& c, uint32_t hash) : _str(c), _hash(hash) {}
[[nodiscard]] constexpr inline uint32_t GetHash() const noexcept { return _hash; } [[nodiscard]] constexpr inline uint32_t GetHash() const noexcept { return _hash; }
[[nodiscard]] constexpr inline const std::u8string_view& GetString() const noexcept { return _str; } [[nodiscard]] constexpr inline std::u8string_view GetString() const noexcept {
return std::u8string_view(_str, _length);
}
[[nodiscard]] constexpr inline size_t GetLength() const noexcept { return _length; }
constexpr inline static uint32_t Hash(const std::u8string_view& sv) { constexpr inline static uint32_t Hash(const std::u8string_view& sv) {
uint32_t crc = 0xffffffff; uint32_t crc = 0xffffffff;
@ -59,6 +64,11 @@ namespace MalachScript {
return crc ^ 0xffffffff; return crc ^ 0xffffffff;
} }
friend std::ostream& operator<<(std::ostream& out, const Identifier& c) {
out << std::string((const char*)c._str, c._length);
return out;
}
private: private:
}; };

View File

@ -12,6 +12,7 @@ namespace MalachScript::Diagnostics {
}; };
class DiagnosticTypeHelper { class DiagnosticTypeHelper {
public:
static std::string ToEnglishString(DiagnosticType type) { static std::string ToEnglishString(DiagnosticType type) {
switch (type) { switch (type) {
case DiagnosticType::UnknownToken: return "Unknown token"; case DiagnosticType::UnknownToken: return "Unknown token";

View File

@ -599,7 +599,7 @@ namespace MalachScript::Parser {
case Identifier::Hash(u8"xor"): case Identifier::Hash(u8"xor"):
return Create<LexTokenImpl<LexTokenKind::XorKeyword>>(TextSpan(start, _position)); return Create<LexTokenImpl<LexTokenKind::XorKeyword>>(TextSpan(start, _position));
default: return Create<IdentifierToken>(TextSpan(start, _position), Identifier(str, hash)); default: return Create<IdentifierToken>(TextSpan(start, _position), Identifier(str.data(), offset, hash));
} }
} }
bool Lexer::IsAlphaNumericalOrUnderscore(char8_t c) { bool Lexer::IsAlphaNumericalOrUnderscore(char8_t c) {

View File

@ -49,6 +49,7 @@ namespace MalachScript::Parser {
ParseFunc(statement, currentToken, log) || ParseNamespace(statement, currentToken, log); ParseFunc(statement, currentToken, log) || ParseNamespace(statement, currentToken, log);
if (!result) { if (!result) {
// TODO: Log error // TODO: Log error
logError(Diagnostics::DiagnosticType::UnexpectedToken, currentToken->GetSpan());
PROGRESS_TOKEN(currentToken); PROGRESS_TOKEN(currentToken);
continue; continue;
} }
@ -82,6 +83,7 @@ namespace MalachScript::Parser {
Identifier identifier; Identifier identifier;
if (!ParseIdentifier(identifier, current)) { if (!ParseIdentifier(identifier, current)) {
logError(DiagnosticType::UnexpectedToken, current->GetSpan()); logError(DiagnosticType::UnexpectedToken, current->GetSpan());
return false;
} }
PROGRESS_TOKEN(current); PROGRESS_TOKEN(current);
std::vector<Identifier> inherits; std::vector<Identifier> inherits;
@ -97,18 +99,21 @@ namespace MalachScript::Parser {
Identifier id; Identifier id;
if (!ParseIdentifier(id, current)) { if (!ParseIdentifier(id, current)) {
logError(DiagnosticType::UnexpectedToken, current->GetSpan()); logError(DiagnosticType::UnexpectedToken, current->GetSpan());
return false;
} }
inherits.push_back(id); inherits.push_back(id);
while (current->GetKind() == LexTokenKind::CommaSymbol) { while (current->GetKind() == LexTokenKind::CommaSymbol) {
PROGRESS_TOKEN(current); PROGRESS_TOKEN(current);
if (!ParseIdentifier(id, current)) { if (!ParseIdentifier(id, current)) {
logError(DiagnosticType::UnexpectedToken, current->GetSpan()); logError(DiagnosticType::UnexpectedToken, current->GetSpan());
return false;
} }
inherits.push_back(id); inherits.push_back(id);
PROGRESS_TOKEN(current); PROGRESS_TOKEN(current);
} }
if (current->GetKind() != LexTokenKind::OpenCurlyParenthesisSymbol) { if (current->GetKind() != LexTokenKind::OpenCurlyParenthesisSymbol) {
logError(DiagnosticType::UnexpectedToken, current->GetSpan()); logError(DiagnosticType::UnexpectedToken, current->GetSpan());
return false;
} }
[[fallthrough]]; [[fallthrough]];
// Intentionally don't break so we continue into the inner body statement. // Intentionally don't break so we continue into the inner body statement.
@ -132,7 +137,7 @@ namespace MalachScript::Parser {
} }
break; break;
} }
default: throw; default: return false;
} }
out = new ParsedClassStatement(TextSpan(start, current->GetSpan().GetEnd()), classAttr, identifier, inherits, out = new ParsedClassStatement(TextSpan(start, current->GetSpan().GetEnd()), classAttr, identifier, inherits,
body); body);
@ -239,6 +244,7 @@ namespace MalachScript::Parser {
if (current->GetKind() != LexTokenKind::SemicolonSymbol) { if (current->GetKind() != LexTokenKind::SemicolonSymbol) {
if (!ParseStatBlock(statblock, current, log)) { if (!ParseStatBlock(statblock, current, log)) {
logError(DiagnosticType::UnexpectedToken, current->GetSpan()); logError(DiagnosticType::UnexpectedToken, current->GetSpan());
return false;
} }
} else { } else {
PROGRESS_TOKEN(current); PROGRESS_TOKEN(current);
@ -367,6 +373,7 @@ namespace MalachScript::Parser {
PROGRESS_TOKEN(currentToken); PROGRESS_TOKEN(currentToken);
if (currentToken->GetKind() != LexTokenKind::CloseParenthesisSymbol) { if (currentToken->GetKind() != LexTokenKind::CloseParenthesisSymbol) {
logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan()); logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan());
return false;
} }
PROGRESS_TOKEN(currentToken); PROGRESS_TOKEN(currentToken);
out = new ParsedParamListStatement(TextSpan(start, currentToken->GetSpan().GetEnd()), parameters); out = new ParsedParamListStatement(TextSpan(start, currentToken->GetSpan().GetEnd()), parameters);
@ -385,9 +392,13 @@ namespace MalachScript::Parser {
if (!ParseType(typeStatement, currentToken, log)) { if (!ParseType(typeStatement, currentToken, log)) {
logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan()); logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan());
return false;
} }
ParseTypeMod(typeMod, currentToken, log); ParseTypeMod(typeMod, currentToken, log);
ParseIdentifier(identifier, currentToken); if (!ParseIdentifier(identifier, currentToken)) {
logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan());
return false;
}
PROGRESS_TOKEN(currentToken); PROGRESS_TOKEN(currentToken);
// TODO: Default expression // TODO: Default expression
@ -403,10 +414,12 @@ namespace MalachScript::Parser {
while (currentToken->GetKind() != LexTokenKind::CloseParenthesisSymbol) { while (currentToken->GetKind() != LexTokenKind::CloseParenthesisSymbol) {
logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan()); logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan());
if (currentToken->GetKind() == LexTokenKind::EndOfFile) { if (currentToken->GetKind() == LexTokenKind::EndOfFile) {
break; logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan());
return false;
} }
if (currentToken->GetKind() == LexTokenKind::SemicolonSymbol) { if (currentToken->GetKind() == LexTokenKind::SemicolonSymbol) {
break; logError(DiagnosticType::UnexpectedToken, currentToken->GetSpan());
return false;
} }
PROGRESS_TOKEN(currentToken); PROGRESS_TOKEN(currentToken);
} }

View File

@ -1,6 +1,7 @@
#ifndef MALACHSCRIPT_PARSEDSTATEMENT_HPP #ifndef MALACHSCRIPT_PARSEDSTATEMENT_HPP
#define MALACHSCRIPT_PARSEDSTATEMENT_HPP #define MALACHSCRIPT_PARSEDSTATEMENT_HPP
#include <sstream>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "../../CoreData/AccessModifier.hpp" #include "../../CoreData/AccessModifier.hpp"
@ -18,6 +19,13 @@ namespace MalachScript::Parser {
virtual ~ParsedStatement() = default; virtual ~ParsedStatement() = default;
[[nodiscard]] virtual ParsedStatementKind GetKind() const noexcept = 0; [[nodiscard]] virtual ParsedStatementKind GetKind() const noexcept = 0;
[[nodiscard]] inline const TextSpan& GetSpan() const noexcept { return _span; } [[nodiscard]] inline const TextSpan& GetSpan() const noexcept { return _span; }
virtual void Stringify(std::stringstream& stream, uint8_t indents) const {
for (uint8_t i = 0; i < indents; i++) {
stream << "\t";
}
stream << ParsedStatementKindHelper::ToString(GetKind());
}
}; };
template <ParsedStatementKind kind> class ParsedStatementImpl : public ParsedStatement { template <ParsedStatementKind kind> class ParsedStatementImpl : public ParsedStatement {
@ -39,6 +47,15 @@ namespace MalachScript::Parser {
[[nodiscard]] inline const std::vector<std::unique_ptr<const ParsedStatement>>& GetStatements() const noexcept { [[nodiscard]] inline const std::vector<std::unique_ptr<const ParsedStatement>>& GetStatements() const noexcept {
return _statements; return _statements;
} }
void Stringify(std::stringstream& stream, uint8_t indents) const override {
ParsedStatement::Stringify(stream, indents);
stream << std::to_string(GetStatements().size());
stream << std::endl;
for (const auto& s : GetStatements()) {
s->Stringify(stream, indents + 1);
}
}
}; };
class ParsedClassStatement : public ParsedStatementImpl<ParsedStatementKind::Class> { class ParsedClassStatement : public ParsedStatementImpl<ParsedStatementKind::Class> {
@ -58,6 +75,15 @@ namespace MalachScript::Parser {
return _body; return _body;
} }
void Stringify(std::stringstream& stream, uint8_t indents) const override {
ParsedStatement::Stringify(stream, indents);
stream << " " << _identifier;
stream << std::endl;
for (const auto& s : GetBody()) {
s->Stringify(stream, indents + 1);
}
}
private: private:
ClassAttr _classAttr; ClassAttr _classAttr;
Identifier _identifier; Identifier _identifier;
@ -108,6 +134,23 @@ namespace MalachScript::Parser {
[[nodiscard]] inline bool IsArray() const noexcept { return _isArray; } [[nodiscard]] inline bool IsArray() const noexcept { return _isArray; }
[[nodiscard]] inline bool IsHandle() const noexcept { return _isHandle; } [[nodiscard]] inline bool IsHandle() const noexcept { return _isHandle; }
[[nodiscard]] inline const ScopedIdentifier& GetScopedIdentifier() const noexcept { return _scopedIdentifier; } [[nodiscard]] inline const ScopedIdentifier& GetScopedIdentifier() const noexcept { return _scopedIdentifier; }
void Stringify(std::stringstream& stream, uint8_t) const override {
if (_isConst) {
stream << "const ";
}
for (auto identifier : _scopedIdentifier.GetScope()) {
stream << identifier;
stream << "::";
}
stream << _scopedIdentifier.GetIdentifier();
if (_isArray) {
stream << "[]";
}
if (_isHandle) {
stream << "&";
}
}
}; };
class ParsedParamListStatement : public ParsedStatementImpl<ParsedStatementKind::ParamList> { class ParsedParamListStatement : public ParsedStatementImpl<ParsedStatementKind::ParamList> {
@ -160,6 +203,15 @@ namespace MalachScript::Parser {
[[nodiscard]] const std::vector<std::unique_ptr<const ParsedParameter>>& GetParameters() const noexcept { [[nodiscard]] const std::vector<std::unique_ptr<const ParsedParameter>>& GetParameters() const noexcept {
return _parameters; return _parameters;
} }
void Stringify(std::stringstream& stream, uint8_t indents) const override {
stream << "(";
for (auto& param : GetParameters()) {
param->GetTypeStatement()->Stringify(stream, indents);
stream << " " << param->GetIdentifier();
}
stream << ")";
}
}; };
class ParsedFuncStatement : public ParsedStatementImpl<ParsedStatementKind::Func> { class ParsedFuncStatement : public ParsedStatementImpl<ParsedStatementKind::Func> {
@ -200,6 +252,14 @@ namespace MalachScript::Parser {
[[nodiscard]] inline const std::unique_ptr<const ParsedStatement>& GetStatBlock() const noexcept { [[nodiscard]] inline const std::unique_ptr<const ParsedStatement>& GetStatBlock() const noexcept {
return _statBlock; return _statBlock;
} }
void Stringify(std::stringstream& stream, uint8_t indents) const override {
ParsedStatement::Stringify(stream, indents);
stream << " " << _identifier;
GetParamList()->Stringify(stream, 0);
stream << std::endl;
GetStatBlock()->Stringify(stream, indents + 1);
}
}; };
class ParsedVirtPropStatement : public ParsedStatementImpl<ParsedStatementKind::VirtProp> { class ParsedVirtPropStatement : public ParsedStatementImpl<ParsedStatementKind::VirtProp> {

View File

@ -31,6 +31,41 @@ namespace MalachScript::Parser {
Switch, Switch,
Case, Case,
}; };
class ParsedStatementKindHelper {
public:
static std::string_view ToString(ParsedStatementKind kind) {
switch (kind) {
case ParsedStatementKind::Unknown: return "Unknown";
case ParsedStatementKind::Script: return "Script";
case ParsedStatementKind::Class: return "Class";
case ParsedStatementKind::TypeDef: return "TypeDef";
case ParsedStatementKind::Namespace: return "Namespace";
case ParsedStatementKind::Type: return "Type";
case ParsedStatementKind::ParamList: return "ParamList";
case ParsedStatementKind::Func: return "Func";
case ParsedStatementKind::VirtProp: return "VirtProp";
case ParsedStatementKind::StatBlock: return "StatBlock";
case ParsedStatementKind::If: return "If";
case ParsedStatementKind::Assign: return "Assign";
case ParsedStatementKind::BinaryExpression: return "BinaryExpression";
case ParsedStatementKind::Void: return "Void";
case ParsedStatementKind::Literal: return "Literal";
case ParsedStatementKind::Return: return "Return";
case ParsedStatementKind::VarAccess: return "VarAccess";
case ParsedStatementKind::Increment: return "Increment";
case ParsedStatementKind::Decrement: return "Decrement";
case ParsedStatementKind::Continue: return "Continue";
case ParsedStatementKind::Break: return "Break";
case ParsedStatementKind::For: return "For";
case ParsedStatementKind::While: return "While";
case ParsedStatementKind::DoWhile: return "DoWhile";
case ParsedStatementKind::Try: return "Try";
case ParsedStatementKind::Switch: return "Switch";
case ParsedStatementKind::Case: return "Case";
}
}
};
} }
#endif // MALACHSCRIPT_PARSEDSTATEMENTKIND_HPP #endif // MALACHSCRIPT_PARSEDSTATEMENTKIND_HPP