parser supports all needed question types

This commit is contained in:
jorenchik
2024-09-29 11:13:56 +03:00
parent 2c3b904853
commit 031803cbad
19 changed files with 143 additions and 49 deletions

View File

@@ -8,6 +8,10 @@
struct Question { struct Question {
std::string ID;
std::string QuestionText;
std::string Section;
virtual std::string ToString() const = 0; virtual std::string ToString() const = 0;
virtual ~Question() = default; virtual ~Question() = default;
}; };
@@ -24,23 +28,18 @@ enum MultiElementType {
}; };
struct MultiElementQuestion : public Question { struct MultiElementQuestion : public Question {
std::string ID;
std::string QuestionText;
std::vector<Choice> Choices; std::vector<Choice> Choices;
std::string Section;
MultiElementType type; MultiElementType type;
std::string ToString() const override; std::string ToString() const override;
}; };
struct Group { struct Group {
std::string name; std::string name;
std::string elements; std::vector<std::string> elements;
}; };
struct GroupQuestion : public Question { struct GroupQuestion : public Question {
std::string ID;
std::string QuestionText;
std::vector<Group> Groups; std::vector<Group> Groups;
std::string ToString() const override; std::string ToString() const override;

View File

@@ -1,2 +1,3 @@
Debug Debug
Release Release
.cache

View File

@@ -199,7 +199,7 @@ void CreateMdems(std::vector<Question*>& questions) {
}; };
for (size_t i = 0; i < questions.size(); ++i) { for (size_t i = 0; i < questions.size(); ++i) {
if (AnswerQuestion* mw = dynamic_cast<AnswerQuestion*>(questions[i])) { if (MultiElementQuestion* mw = dynamic_cast<MultiElementQuestion*>(questions[i])) {
mdems[i]->wFrontText->setText( mdems[i]->wFrontText->setText(
QString::fromStdString(mw->QuestionText) QString::fromStdString(mw->QuestionText)
); );

View File

@@ -1,2 +1,3 @@
Debug Debug
Release Release
.cache

View File

@@ -111,7 +111,16 @@ Result<std::vector<Token>> TokenizeMdem(const std::string& fileRunes) {
row += 1; row += 1;
column = 0; column = 0;
} }
buffer.push_back(c);
if (c == '\\') {
i += 1;
if (i < fileRunes.size()) {
buffer.push_back(fileRunes[i]);
}
continue;
} else {
buffer.push_back(c);
}
// SkipWhitetext // SkipWhitetext
if (!textStarted) { if (!textStarted) {

View File

@@ -19,25 +19,45 @@ struct QuestionElement {
}; };
std::string MultiElementQuestion::ToString() const { std::string MultiElementQuestion::ToString() const {
std::stringstream choiceOut; std::stringstream ss;
for (const auto& choice : Choices) { for (const auto& choice : Choices) {
char opener; char opener;
if (choice.IsCorrect) { if (type == MultiElementType::Order) {
opener = '^';
} else if (choice.IsCorrect) {
opener = '+'; opener = '+';
} else { } else {
opener = '-'; opener = '-';
} }
choiceOut << opener << " " << choice.Answer << "; "; ss << opener << " " << choice.Answer << "; ";
} }
return std::format( return std::format(
"<Multiple choice>:{} section: {} id: {}\n{}", "<Multiple element>\nsection:{}\nid:{}\n{}\n{}",
QuestionText, Section,
Section, ID,
ID, QuestionText,
choiceOut.str() ss.str()
); );
} }
std::string GroupQuestion::ToString() const {
std::stringstream ss;
for (auto group: Groups) {
ss << group.name << ": ";
for (auto el: group.elements) {
ss << el << ", ";
}
ss << "; ";
}
return std::format(
"<GroupQuestion>\nsection:{}\nid:{}\n{}\n{}",
Section,
ID,
QuestionText,
ss.str()
);
}
// Automaton for validating token transitions // Automaton for validating token transitions
std::map<TokenType, std::vector<TokenType>> automata; std::map<TokenType, std::vector<TokenType>> automata;
@@ -166,9 +186,22 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
std::vector<QuestionElement> questionElements; std::vector<QuestionElement> questionElements;
bool isOrderQuestion = false; bool isOrderQuestion = false;
bool isGroupQuestion = false; bool isGroupQuestion = false;
bool isPlusQuestion = false;
auto isInBounds = [tokens](size_t i) {
return i < tokens.size() && tokens[i].tokenType != TokenType::EndOfFile;
};
// Start element parsing & add to the offset. // Start element parsing & add to the offset.
if (tokens[i + 1].tokenType == TokenType::IdentifierStart) { if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
return {
questions,
"cannot have order modifier ('^') in the question definition",
tokens[i + 1].row,
tokens[i + 1].column
};
}
if (isInBounds(i + 1) && tokens[i + 1].tokenType == TokenType::IdentifierStart) {
id = tokens[i + 2].content; id = tokens[i + 2].content;
questionText = tokens[i + 4].content; questionText = tokens[i + 4].content;
i += 6; i += 6;
@@ -178,10 +211,6 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
i += 3; i += 3;
} }
auto isInBounds = [tokens](size_t i) {
return i < tokens.size() && tokens[i].tokenType != TokenType::EndOfFile;
};
// Parse elements of a question. // Parse elements of a question.
while (isInBounds(i)) { while (isInBounds(i)) {
@@ -194,12 +223,17 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
} }
// Check question end. // Check question end.
if (tokens[i].tokenType == TokenType::ElementDashStart && isInBounds(i + 3)) { if (isInBounds(i + 3) && tokens[i].tokenType == TokenType::ElementDashStart) {
// Distance to the possible question end. // Distance to the possible question end.
size_t offset = tokens[i + 1].tokenType == TokenType::IdentifierStart ? 5 : 2; size_t offset;
if (tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
offset = tokens[i + 2].tokenType == TokenType::IdentifierStart ? 6 : 3;
} else {
offset = tokens[i + 1].tokenType == TokenType::IdentifierStart ? 5 : 2;
}
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. // Cannot place the identifier on the ordinary element.
return { return {
@@ -214,39 +248,61 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
// Determine element type. // Determine element type.
bool isDash; bool isDash;
bool isGroup = false; bool isGroup = false;
if (isInBounds(i+1) && tokens[i].tokenType == TokenType::ElementOrderModifier) { bool isOrder = false;
isOrderQuestion = true;
if (!isDash) {
// TODO: err
}
}
if (tokens[i].tokenType == TokenType::ElementDashStart) { if (tokens[i].tokenType == TokenType::ElementDashStart) {
isDash = true; isDash = true;
if (isOrderQuestion) {
// TODO: err
}
} else { } else {
isDash = false; isDash = false;
isPlusQuestion = true;
} }
if (isInBounds(i+2) && tokens[i].tokenType == TokenType::MatchGroupEnd) { if (isInBounds(i+1) && tokens[i + 1].tokenType == TokenType::ElementOrderModifier) {
isOrder = true;
isOrderQuestion = true;
if (!isDash) {
return {
questions,
"order questions can only be used with dashes ('-')",
tokens[i].row,
tokens[i].column
};
}
if (isGroupQuestion) {
return {
questions,
"question with groups cannot be ordered ('-^' and ':')",
tokens[i].row,
tokens[i].column
};
}
if (isInBounds(i + 3) && tokens[i + 3].tokenType == TokenType::MatchGroupEnd) {
return {
questions,
"cannot have groups in order question('-^' and ':')",
tokens[i].row,
tokens[i].column
};
}
}
if (isInBounds(i + 2) && tokens[i + 2].tokenType == TokenType::MatchGroupEnd) {
isGroup = true; isGroup = true;
isGroupQuestion = true; isGroupQuestion = true;
if (!isDash) { if (!isDash) {
// TODO: err return {questions, "group questions can only be used with dashes ('-')"};
}
if (isOrderQuestion) {
// TODO: err
} }
} }
QuestionElement questionElement; QuestionElement questionElement;
questionElement.isDash = isDash; questionElement.isDash = isDash;
questionElement.isGroup = isGroup; questionElement.isGroup = isGroup;
questionElement.content = tokens[i + 1].content; if (isOrder) {
questionElement.content = tokens[i + 2].content;
} else {
questionElement.content = tokens[i + 1].content;
}
questionElements.push_back(questionElement); questionElements.push_back(questionElement);
size_t offset = 2; size_t offset = 2;
if (isOrderQuestion) { if (isOrder) {
offset += 1; offset += 1;
} }
if (isGroup) { if (isGroup) {
@@ -256,25 +312,53 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
i += offset; i += offset;
} }
if (questionElements.size() > 1) { if (questionElements.size() > 0) {
if (isGroupQuestion) { if (isGroupQuestion) {
GroupQuestion *question = new GroupQuestion(); auto *question = new GroupQuestion();
// TODO question->ID = id;
question->QuestionText = questionText;
} if (isOrderQuestion) { question->Section = section;
int32_t k = -1;
for (size_t i = 0; i < questionElements.size(); ++i) {
auto questionElement = questionElements[i];
if (questionElement.isGroup) {
++k;
auto group = Group();
group.name = questionElement.content;
question->Groups.push_back(group);
} else {
if (k >= 0) {
question->Groups[k].elements.push_back(
questionElement.content
);
}
}
}
questions.push_back(question);
if (debug) {
std::cout << question->ToString() << "\n";
}
} else {
auto *question = new MultiElementQuestion(); auto *question = new MultiElementQuestion();
question->ID = id; question->ID = id;
question->QuestionText = questionText; question->QuestionText = questionText;
question->Section = section;
for (const auto& elem : questionElements) { for (const auto& elem : questionElements) {
Choice choice; Choice choice;
choice.Answer = elem.content; choice.Answer = elem.content;
choice.IsCorrect = !elem.isDash; choice.IsCorrect = !elem.isDash;
question->Choices.push_back(choice); question->Choices.push_back(choice);
} }
question->Section = section;
questions.push_back(question); questions.push_back(question);
if (isPlusQuestion) {
question->type = MultiElementType::MultiChoice;
} else if (isOrderQuestion) {
question->type = MultiElementType::Order;
} else {
question->type = MultiElementType::Regular;
}
if (debug) { if (debug) {
std::cout << question->ToString() << "\n"; std::cout << question->ToString() << "\n";
} }
} }
} }