comments and error message improvements

This commit is contained in:
jorenchik
2024-11-18 13:16:36 +02:00
parent 581b4b3926
commit 7f1c9f26e5
2 changed files with 100 additions and 44 deletions

View File

@@ -17,11 +17,12 @@ int32_t column;
int32_t previousRow; int32_t previousRow;
int32_t previousColumn; int32_t previousColumn;
bool textStarted = false; bool textStarted = false;
bool identifierStarted = false; bool cooldownStarted = false;
bool sof; bool sof;
/* /*
* TODO * Noņem norādītos simbolus no simbolu virknes kreisās un labās pusēs.
* Simboli tiek noņemti līdz tiek sastapts simbols, kas nav norādīts.
*/ */
void trimString(std::string *str, std::string trimChars) { void trimString(std::string *str, std::string trimChars) {
@@ -123,12 +124,14 @@ Result<std::vector<Token>> tokenizeMdem(const std::string& content) {
for (size_t i = 0; i < content.size(); ++i) { for (size_t i = 0; i < content.size(); ++i) {
char c = content[i]; char c = content[i];
// Apstrādā īpašos simbolus un tekstu. // Pavirza faila norādi un papildina buferi.
if (c == '\n') { if (c == '\n') {
row += 1; row += 1;
column = 0; column = 0;
} }
if (c == '\\') { if (c == '\\') {
// Simbolus, kas abilst citām tekstvienībām, var ievadīt,
// ja pirms tiem ieliek '\' simbolu.
i += 1; i += 1;
if (i < content.size()) { if (i < content.size()) {
buffer.push_back(content[i]); buffer.push_back(content[i]);
@@ -137,6 +140,7 @@ Result<std::vector<Token>> tokenizeMdem(const std::string& content) {
} else { } else {
buffer.push_back(c); buffer.push_back(c);
} }
// Iepriekšējā tekstvienības pozīcijas uzturēšana.
if (!textStarted) { if (!textStarted) {
if (c == '\n') { if (c == '\n') {
previousRow += 1; previousRow += 1;
@@ -150,7 +154,7 @@ Result<std::vector<Token>> tokenizeMdem(const std::string& content) {
} }
} }
// Emitē tekstvienības. // Izveido viena simbola tekstvienības, ja tāda ir sastapta.
switch (c) { switch (c) {
case '[': { case '[': {
tokenWithBuffer( tokenWithBuffer(
@@ -161,13 +165,16 @@ Result<std::vector<Token>> tokenizeMdem(const std::string& content) {
previousRow = row; previousRow = row;
previousColumn = column; previousColumn = column;
textStarted = false; textStarted = false;
identifierStarted = true;
// Karodziņš, lai zinātu, kad ir jānosaka pārtraukuma
// tekstvienību.
cooldownStarted = true;
} break; } break;
case ']': { case ']': {
if (!identifierStarted) { if (!cooldownStarted) {
return { return {
tokens, tokens,
"Nevar beigt identifikatoru, ja tas nav iesākts", "Nevar beigt pārtraukuma norādīšanu, ja tas nav iesākts",
tokens[i].row, tokens[i].row,
tokens[i].column tokens[i].column
}; };
@@ -180,7 +187,7 @@ Result<std::vector<Token>> tokenizeMdem(const std::string& content) {
previousRow = row; previousRow = row;
previousColumn = column; previousColumn = column;
textStarted = false; textStarted = false;
identifierStarted = false; cooldownStarted = false;
} break; } break;
case '-': { case '-': {
tokenWithBuffer( tokenWithBuffer(
@@ -255,9 +262,13 @@ Result<std::vector<Token>> tokenizeMdem(const std::string& content) {
std::cout << "SECTION END: LEXER\n"; std::cout << "SECTION END: LEXER\n";
} }
// Leksiskā analīze ir veiksmīga - neatgriež kļūdu.
return {tokens, ""}; return {tokens, ""};
} }
/*
* Tekstvienības nosaukums latviešu valodā.
*/
std::string Token::toString(const TokenType* ttype) { std::string Token::toString(const TokenType* ttype) {
switch (*ttype) { switch (*ttype) {
case TokenType::TextFragment: return "teksta fragments"; case TokenType::TextFragment: return "teksta fragments";
@@ -275,6 +286,9 @@ std::string Token::toString(const TokenType* ttype) {
} }
} }
/*
* Tekstvienību reprezentējoša simbolu virkne atkļūdošanai.
*/
std::string Token::toString() const { std::string Token::toString() const {
std::string contentStr = content; std::string contentStr = content;
static const std::regex nextLineExp("\n", std::regex_constants::ECMAScript); static const std::regex nextLineExp("\n", std::regex_constants::ECMAScript);

View File

@@ -10,80 +10,81 @@
#include <sstream> #include <sstream>
#include <format> #include <format>
#include "config.h" #include "config.h"
#include "lexer.h" #include "lexer.h"
#include "result.h" #include "result.h"
#include "parser.h" #include "parser.h"
#include "stringUtils.h" #include "stringUtils.h"
typedef std::map<TokenType, std::vector<TokenType>> TokenAutomata; typedef std::map<TokenType, std::vector<TokenType>> TokenTransitions;
TokenAutomata *automata = nullptr; TokenTransitions *transitions = nullptr;
/* /*
* Tekstvienību secības pārejas, kas nosaka, kādā secībā tekstvienības var būt. * Tekstvienību secības pārejas, kas nosaka, kādā secībā tekstvienības var būt.
* Pāreja no tekstvienības A uz tekstvienību B ir atļauta, tikai ja sarakstā ar
* atslēgu tekstvienībā A ir tekstvienība B.
* */ * */
void initParserAutomata() { void initTransitions() {
automata = new TokenAutomata; transitions = new TokenTransitions;
(*automata)[TokenType::TextFragment] = { (*transitions)[TokenType::TextFragment] = {
TokenType::QuestionEnd, TokenType::QuestionEnd,
TokenType::ElementDashStart, TokenType::ElementDashStart,
TokenType::ElementPlusStart, TokenType::ElementPlusStart,
TokenType::MatchGroupEnd, TokenType::MatchGroupEnd,
TokenType::EndOfFile, TokenType::EndOfFile,
}; };
(*automata)[TokenType::MatchGroupEnd] = { (*transitions)[TokenType::MatchGroupEnd] = {
TokenType::ElementDashStart TokenType::ElementDashStart
}; };
(*automata)[TokenType::QuestionEnd] = { (*transitions)[TokenType::QuestionEnd] = {
TokenType::ElementDashStart, TokenType::ElementDashStart,
TokenType::ElementPlusStart TokenType::ElementPlusStart
}; };
(*automata)[TokenType::ElementDashStart] = { (*transitions)[TokenType::ElementDashStart] = {
TokenType::CooldownStart, TokenType::CooldownStart,
TokenType::TextFragment, TokenType::TextFragment,
TokenType::ElementOrderModifier TokenType::ElementOrderModifier
}; };
(*automata)[TokenType::ElementOrderModifier] = { (*transitions)[TokenType::ElementOrderModifier] = {
TokenType::TextFragment TokenType::TextFragment
}; };
(*automata)[TokenType::ElementPlusStart] = { (*transitions)[TokenType::ElementPlusStart] = {
TokenType::TextFragment TokenType::TextFragment
}; };
(*automata)[TokenType::Cooldown] = { (*transitions)[TokenType::Cooldown] = {
TokenType::CooldownEnd, TokenType::CooldownEnd,
}; };
(*automata)[TokenType::CooldownStart] = { (*transitions)[TokenType::CooldownStart] = {
TokenType::Cooldown TokenType::Cooldown
}; };
(*automata)[TokenType::CooldownEnd] = { (*transitions)[TokenType::CooldownEnd] = {
TokenType::TextFragment TokenType::TextFragment
}; };
(*automata)[TokenType::StartOfFile] = { (*transitions)[TokenType::StartOfFile] = {
TokenType::TextFragment, TokenType::TextFragment,
TokenType::ElementDashStart, TokenType::ElementDashStart,
TokenType::EndOfFile TokenType::EndOfFile
}; };
(*automata)[TokenType::EndOfFile] = {}; (*transitions)[TokenType::EndOfFile] = {};
} }
/* /*
* Pārbauda, vai vai tekstvienību sarakstu akceptē atbilst atbilst valodas * Pārbauda, vai tekstvienību saraksts atbilst valodas
* automāts. * definētām pieļaujamām pārejām.
* */ * */
Result<NoneType> ValidateGrammar(const std::vector<Token>& tokens) { Result<NoneType> ValidateGrammar(const std::vector<Token>& tokens) {
if (!automata) { if (!transitions) {
initParserAutomata(); initTransitions();
} }
for (size_t i = 0; i < tokens.size() - 1; ++i) { for (size_t i = 0; i < tokens.size() - 1; ++i) {
Token token = tokens[i]; Token token = tokens[i];
Token nextToken = tokens[i + 1]; Token nextToken = tokens[i + 1];
if ( if (
std::find( std::find(
(*automata)[token.tokenType].begin(), (*transitions)[token.tokenType].begin(),
(*automata)[token.tokenType].end(), (*transitions)[token.tokenType].end(),
nextToken.tokenType nextToken.tokenType
) == (*automata)[token.tokenType].end()) { ) == (*transitions)[token.tokenType].end()) {
auto capitalize = [](const std::string& str) { auto capitalize = [](const std::string& str) {
if (str.empty()) return str; if (str.empty()) return str;
@@ -105,13 +106,24 @@ Result<NoneType> ValidateGrammar(const std::vector<Token>& tokens) {
return {}; return {};
} }
// @Fix: Prevent duplicate group names and questions in ordered question (to // @Fix: before EOF is acceptable
// simplify checking in practice). // @Fix: remove section from questions
/*
* Apstrādā tekstvienības, iegūstot datumu un laiku un vienu vai vairākus
* dažāda veida jautājumus. Veiksmes gadījumā atgriež jautājumu norādes un
* datumu un laiku, ja tāds bija norādīts. Kļūdas gadījumā atgriež jautājumu
* norādes un kļūdu ar to atrašanos vietu failā. Atrašanās vieta ir nosakāma no
* atrašanās informācijas tekstvienību objektos.
* */
Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) { Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
auto questions = std::vector<Question*>(); auto questions = std::vector<Question*>();
time_t time = 0; time_t time = 0;
auto makeResult = [&questions, &time](std::string error, Token token) -> Result<ParseInfo> { // Palīgfunkcija - atgriež jautājumus un laiku ar norādītu tekstvienības
// kļūdas informāciju.
auto makeResult = [&questions, &time](std::string error, Token token)
-> Result<ParseInfo> {
return { return {
{ questions, time }, { questions, time },
error, error,
@@ -120,10 +132,10 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
}; };
}; };
// Sākotnējā validācija.
if (tokens.size() == 0) { if (tokens.size() == 0) {
return makeResult("", Token()); return makeResult("", Token());
} }
auto result = ValidateGrammar(tokens); auto result = ValidateGrammar(tokens);
if (result.error.length() > 0) { if (result.error.length() > 0) {
return makeResult( return makeResult(
@@ -139,10 +151,13 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
std::cout << "SECTION: PARSER:\n"; std::cout << "SECTION: PARSER:\n";
} }
// Palīgfunkcija - pārbauda, vai tekstvienības saraksta indeksā ir
// elements, kas nav faila beigas.
auto isInBounds = [tokens](size_t i) { auto isInBounds = [tokens](size_t i) {
return i < tokens.size() && tokens[i].tokenType != TokenType::EndOfFile; return i < tokens.size() && tokens[i].tokenType != TokenType::EndOfFile;
}; };
// Sākuma datumu un laiku mēģina nolasīt, ja tāds ir norādīts.
if (isInBounds(i) && tokens[i].tokenType == TokenType::TextFragment) { if (isInBounds(i) && tokens[i].tokenType == TokenType::TextFragment) {
const std::string format = "%d.%m.%Y %H:%M"; const std::string format = "%d.%m.%Y %H:%M";
const std::string datetime = tokens[i].content.c_str(); const std::string datetime = tokens[i].content.c_str();
@@ -159,6 +174,7 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
i++; i++;
} }
// Pamata parsēšana.
while (i < tokens.size()) { while (i < tokens.size()) {
if (tokens[i].tokenType == TokenType::ElementDashStart) { if (tokens[i].tokenType == TokenType::ElementDashStart) {
std::string questionText; std::string questionText;
@@ -170,14 +186,18 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
bool hasGroupEncountered = false; bool hasGroupEncountered = false;
Token questionStartToken; Token questionStartToken;
// Start element parsing & add to the offset. // Šajā momentā ir sagaidāms jautājuma sākums - tam nevar būt secības modifikators.
if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) { if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
return makeResult( return makeResult(
"Nevar izmantot secības modifikatoru ('^') jautājuma sākumā", "Nevar izmantot secības modifikatoru ('^') jautājuma sākumā",
tokens[i + 1]); tokens[i + 1]);
} }
// Piefiksē sākumu, lai varētu sniegt labāku kļūdas
// atrašanos vietu kļūdas gadījumā.
questionStartToken = tokens[i]; questionStartToken = tokens[i];
// Apstrādā pārtraukumu, ja tāds ir.
if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::CooldownStart) { if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::CooldownStart) {
try { try {
cooldown = std::stod(tokens[i + 2].content); cooldown = std::stod(tokens[i + 2].content);
@@ -195,12 +215,13 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
i += 3; i += 3;
} }
// Parse elements of a question. // Jautājumu elementu parsēšana.
while (isInBounds(i)) { while (isInBounds(i)) {
// Check question end. // Pārbauda, vai nav sastapts cits jautājuma sākums, un noslēdz, ja tas tā ir.
if (isInBounds(i + 3) && tokens[i].tokenType == TokenType::ElementDashStart) { if (isInBounds(i + 3) && tokens[i].tokenType == TokenType::ElementDashStart) {
// Distance to the possible question end.
// Jautājumam var būt un var nebūt pārtraukums - nosaka vai tas būtu.
size_t offset; size_t offset;
if (tokens[i + 1].tokenType == TokenType::ElementOrderModifier) { if (tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
offset = tokens[i + 2].tokenType == TokenType::CooldownStart ? 6 : 3; offset = tokens[i + 2].tokenType == TokenType::CooldownStart ? 6 : 3;
@@ -210,19 +231,21 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
if (isInBounds(i + offset) && tokens[i + offset].tokenType == TokenType::QuestionEnd) { if (isInBounds(i + offset) && tokens[i + offset].tokenType == TokenType::QuestionEnd) {
break; break;
} }
if (offset == 5 && tokens[i + 5].tokenType != TokenType::QuestionEnd) { if (offset == 5 && tokens[i + 5].tokenType != TokenType::QuestionEnd) {
// Cannot place the identifier on the ordinary element.
return makeResult( return makeResult(
"Nepareiza idenfikatora izvietošana", "Jautājuma elementam nevar būt pārtraukums",
tokens[i] tokens[i]
); );
} }
} }
// Determine element type. // Jautājuma elementa noteikšana un ar to saistītās kļūdas.
bool isDash; bool isDash;
bool isGroup = false; bool isGroup = false;
bool isOrder = false; bool isOrder = false;
// Elementa sākums.
if (tokens[i].tokenType == TokenType::ElementDashStart) { if (tokens[i].tokenType == TokenType::ElementDashStart) {
isDash = true; isDash = true;
} else { } else {
@@ -241,6 +264,8 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
); );
} }
} }
// Elementa secības modifikators.
if (isInBounds(i+1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) { if (isInBounds(i+1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
isOrder = true; isOrder = true;
isOrderQuestion = true; isOrderQuestion = true;
@@ -269,6 +294,8 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
); );
} }
} }
// Elementa grupas modifikators.
if (isInBounds(i + 2) && tokens[i + 2].tokenType == TokenType::MatchGroupEnd) { if (isInBounds(i + 2) && tokens[i + 2].tokenType == TokenType::MatchGroupEnd) {
isGroup = true; isGroup = true;
isGroupQuestion = true; isGroupQuestion = true;
@@ -289,6 +316,7 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
hasGroupEncountered = true; hasGroupEncountered = true;
} }
// Izveido atbilstoša veida jautājuma elementu.
QuestionElement questionElement; QuestionElement questionElement;
questionElement.isDash = isDash; questionElement.isDash = isDash;
questionElement.isGroup = isGroup; questionElement.isGroup = isGroup;
@@ -299,23 +327,28 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
} }
questionElements.push_back(questionElement); questionElements.push_back(questionElement);
size_t offset = 2; // Nākamā elementa atrašanās vieta ir atkarīga no elementu
// veida, kas ir sastapts.
size_t offset = 2;
if (isOrder) { if (isOrder) {
offset += 1; offset += 1;
} }
if (isGroup) { if (isGroup) {
offset += 1; offset += 1;
} }
i += offset; i += offset;
} }
// Izveido jautājuma objektu.
// Fix: else block - jautājums bez elementiem.
if (questionElements.size() > 0) { if (questionElements.size() > 0) {
if (isGroupQuestion) { if (isGroupQuestion) {
auto *question = new GroupQuestion(); auto *question = new GroupQuestion();
question->cooldown = cooldown; question->cooldown = cooldown;
question->questionText = questionText; question->questionText = questionText;
question->section = section; question->section = section;
// Izveido grupas; i - elementu iterators; k - grupu iterators.
int32_t k = -1; int32_t k = -1;
for (size_t i = 0; i < questionElements.size(); ++i) { for (size_t i = 0; i < questionElements.size(); ++i) {
auto questionElement = questionElements[i]; auto questionElement = questionElements[i];
@@ -344,12 +377,14 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
question->questionText = cleanContent(questionText); question->questionText = cleanContent(questionText);
question->section = section; question->section = section;
// Izveido vairāku elementu jautājumu.
auto existingElements = std::set<std::string>(); auto existingElements = std::set<std::string>();
for (const auto& elem : questionElements) { for (const auto& elem : questionElements) {
Choice choice; Choice choice;
choice.answer = cleanContent(elem.content); choice.answer = cleanContent(elem.content);
choice.isCorrect = !elem.isDash; choice.isCorrect = !elem.isDash;
// Secības elementiem nedrīkst būt vienādi elementi.
if (isOrderQuestion) { if (isOrderQuestion) {
if (existingElements.contains(choice.answer)) { if (existingElements.contains(choice.answer)) {
return makeResult( return makeResult(
@@ -366,6 +401,7 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
} }
questions.push_back(question); questions.push_back(question);
// Uzstāda vairāku elementu jautājuma specializēto veidu.
if (isPlusQuestion) { if (isPlusQuestion) {
question->type = MultiElementType::MultiChoice; question->type = MultiElementType::MultiChoice;
} else if (isOrderQuestion) { } else if (isOrderQuestion) {
@@ -400,6 +436,9 @@ Result<ParseInfo> parseQuestions(const std::vector<Token>& tokens) {
); );
} }
/*
* Simbolu virkne, kas attēlo jautājumu atkļūdošanai.
*/
std::string MultiElementQuestion::toString() const { std::string MultiElementQuestion::toString() const {
std::stringstream ss; std::stringstream ss;
for (const auto& choice : choices) { for (const auto& choice : choices) {
@@ -422,6 +461,9 @@ std::string MultiElementQuestion::toString() const {
); );
} }
/*
* Simbolu virkne, kas attēlo jautājumu atkļūdošanai.
*/
std::string GroupQuestion::toString() const { std::string GroupQuestion::toString() const {
std::stringstream ss; std::stringstream ss;
for (auto group: groups) { for (auto group: groups) {