mirror of
https://github.com/jorenchik/mdemory.git
synced 2026-03-22 00:26:21 +00:00
partial implementation of all question types
This commit is contained in:
@@ -8,8 +8,10 @@
|
|||||||
enum class TokenType {
|
enum class TokenType {
|
||||||
TextFragment,
|
TextFragment,
|
||||||
QuestionEnd,
|
QuestionEnd,
|
||||||
|
MatchGroupEnd,
|
||||||
ElementDashStart,
|
ElementDashStart,
|
||||||
ElementPlusStart,
|
ElementPlusStart,
|
||||||
|
ElementOrderModifier,
|
||||||
Identifier,
|
Identifier,
|
||||||
IdentifierStart,
|
IdentifierStart,
|
||||||
IdentifierEnd,
|
IdentifierEnd,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
|
|
||||||
|
|
||||||
struct Question {
|
struct Question {
|
||||||
virtual std::string ToString() const = 0;
|
virtual std::string ToString() const = 0;
|
||||||
virtual ~Question() = default;
|
virtual ~Question() = default;
|
||||||
@@ -16,20 +17,31 @@ struct Choice {
|
|||||||
bool IsCorrect;
|
bool IsCorrect;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SingleAnswerQuestion : public Question {
|
enum MultiElementType {
|
||||||
std::string ID;
|
Regular = 0,
|
||||||
std::string QuestionText;
|
MultiChoice,
|
||||||
std::string Answer;
|
Order
|
||||||
std::string Section;
|
|
||||||
|
|
||||||
std::string ToString() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MultipleChoiceQuestion : public Question {
|
struct MultiElementQuestion : public Question {
|
||||||
std::string ID;
|
std::string ID;
|
||||||
std::string QuestionText;
|
std::string QuestionText;
|
||||||
std::vector<Choice> Choices;
|
std::vector<Choice> Choices;
|
||||||
std::string Section;
|
std::string Section;
|
||||||
|
MultiElementType type;
|
||||||
|
|
||||||
|
std::string ToString() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Group {
|
||||||
|
std::string name;
|
||||||
|
std::string elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GroupQuestion : public Question {
|
||||||
|
std::string ID;
|
||||||
|
std::string QuestionText;
|
||||||
|
std::vector<Group> Groups;
|
||||||
|
|
||||||
std::string ToString() const override;
|
std::string ToString() const override;
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
@@ -199,17 +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 (SingleAnswerQuestion* sa = dynamic_cast<SingleAnswerQuestion*>(questions[i])) {
|
if (AnswerQuestion* mw = dynamic_cast<AnswerQuestion*>(questions[i])) {
|
||||||
mdems[i]->wFrontText->setText(QString::fromStdString(sa->QuestionText));
|
|
||||||
auto answer = sa->Answer;
|
|
||||||
answer = transformAnswer(answer);
|
|
||||||
answer = std::format("- {}", answer);
|
|
||||||
mdems[i]->backLabels[0]->setText(QString::fromStdString(answer));
|
|
||||||
if (mdems[i]->wBack->isVisible()) {
|
|
||||||
mdems[i]->wBack->hide();
|
|
||||||
}
|
|
||||||
mdems[i]->labelCount = 1;
|
|
||||||
} else if (MultipleChoiceQuestion* mw = dynamic_cast<MultipleChoiceQuestion*>(questions[i])) {
|
|
||||||
mdems[i]->wFrontText->setText(
|
mdems[i]->wFrontText->setText(
|
||||||
QString::fromStdString(mw->QuestionText)
|
QString::fromStdString(mw->QuestionText)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -199,6 +199,26 @@ Result<std::vector<Token>> TokenizeMdem(const std::string& fileRunes) {
|
|||||||
previousColumn = column;
|
previousColumn = column;
|
||||||
textStarted = false;
|
textStarted = false;
|
||||||
break;
|
break;
|
||||||
|
case '^':
|
||||||
|
makeTokenWithTokenBuffer(
|
||||||
|
TokenType::ElementOrderModifier,
|
||||||
|
1,
|
||||||
|
TokenType::TextFragment
|
||||||
|
);
|
||||||
|
previousRow = row;
|
||||||
|
previousColumn = column;
|
||||||
|
textStarted = false;
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
makeTokenWithTokenBuffer(
|
||||||
|
TokenType::MatchGroupEnd,
|
||||||
|
1,
|
||||||
|
TokenType::TextFragment
|
||||||
|
);
|
||||||
|
previousRow = row;
|
||||||
|
previousColumn = column;
|
||||||
|
textStarted = false;
|
||||||
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
makeTokenWithTokenBuffer(
|
makeTokenWithTokenBuffer(
|
||||||
TokenType::QuestionEnd,
|
TokenType::QuestionEnd,
|
||||||
@@ -255,7 +275,9 @@ std::string Token::ToString(const TokenType* ttype) {
|
|||||||
switch (*ttype) {
|
switch (*ttype) {
|
||||||
case TokenType::TextFragment: return "text fragment";
|
case TokenType::TextFragment: return "text fragment";
|
||||||
case TokenType::QuestionEnd: return "question end symbol";
|
case TokenType::QuestionEnd: return "question end symbol";
|
||||||
|
case TokenType::MatchGroupEnd: return "match group end";
|
||||||
case TokenType::ElementDashStart: return "dash element start";
|
case TokenType::ElementDashStart: return "dash element start";
|
||||||
|
case TokenType::ElementOrderModifier: return "order element modifier";
|
||||||
case TokenType::ElementPlusStart: return "plus element start";
|
case TokenType::ElementPlusStart: return "plus element start";
|
||||||
case TokenType::Identifier: return "identifier";
|
case TokenType::Identifier: return "identifier";
|
||||||
case TokenType::IdentifierStart: return "start of identifier";
|
case TokenType::IdentifierStart: return "start of identifier";
|
||||||
|
|||||||
@@ -14,20 +14,11 @@
|
|||||||
|
|
||||||
struct QuestionElement {
|
struct QuestionElement {
|
||||||
bool isDash;
|
bool isDash;
|
||||||
|
bool isGroup;
|
||||||
std::string content;
|
std::string content;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string SingleAnswerQuestion::ToString() const {
|
std::string MultiElementQuestion::ToString() const {
|
||||||
return std::format(
|
|
||||||
"<Single choice>:{} section:{} id:{} answer:{}",
|
|
||||||
QuestionText,
|
|
||||||
Section,
|
|
||||||
ID,
|
|
||||||
Answer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MultipleChoiceQuestion::ToString() const {
|
|
||||||
std::stringstream choiceOut;
|
std::stringstream choiceOut;
|
||||||
for (const auto& choice : Choices) {
|
for (const auto& choice : Choices) {
|
||||||
char opener;
|
char opener;
|
||||||
@@ -61,17 +52,25 @@ std::map<TokenType, std::vector<TokenType>> parserAutomata() {
|
|||||||
TokenType::QuestionEnd,
|
TokenType::QuestionEnd,
|
||||||
TokenType::ElementDashStart,
|
TokenType::ElementDashStart,
|
||||||
TokenType::ElementPlusStart,
|
TokenType::ElementPlusStart,
|
||||||
|
TokenType::MatchGroupEnd,
|
||||||
TokenType::SectionIdentifierStart,
|
TokenType::SectionIdentifierStart,
|
||||||
TokenType::SectionStart,
|
TokenType::SectionStart,
|
||||||
TokenType::EndOfFile,
|
TokenType::EndOfFile,
|
||||||
TokenType::SectionEnd
|
TokenType::SectionEnd
|
||||||
};
|
};
|
||||||
|
automata[TokenType::MatchGroupEnd] = {
|
||||||
|
TokenType::ElementDashStart
|
||||||
|
};
|
||||||
automata[TokenType::QuestionEnd] = {
|
automata[TokenType::QuestionEnd] = {
|
||||||
TokenType::ElementDashStart,
|
TokenType::ElementDashStart,
|
||||||
TokenType::ElementPlusStart
|
TokenType::ElementPlusStart
|
||||||
};
|
};
|
||||||
automata[TokenType::ElementDashStart] = {
|
automata[TokenType::ElementDashStart] = {
|
||||||
TokenType::IdentifierStart,
|
TokenType::IdentifierStart,
|
||||||
|
TokenType::TextFragment,
|
||||||
|
TokenType::ElementOrderModifier
|
||||||
|
};
|
||||||
|
automata[TokenType::ElementOrderModifier] = {
|
||||||
TokenType::TextFragment
|
TokenType::TextFragment
|
||||||
};
|
};
|
||||||
automata[TokenType::ElementPlusStart] = {
|
automata[TokenType::ElementPlusStart] = {
|
||||||
@@ -165,7 +164,10 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
|
|||||||
|
|
||||||
std::string id, questionText;
|
std::string id, questionText;
|
||||||
std::vector<QuestionElement> questionElements;
|
std::vector<QuestionElement> questionElements;
|
||||||
// Parsing for a single question or multiple choice question
|
bool isOrderQuestion = false;
|
||||||
|
bool isGroupQuestion = false;
|
||||||
|
|
||||||
|
// Start element parsing & add to the offset.
|
||||||
if (tokens[i + 1].tokenType == TokenType::IdentifierStart) {
|
if (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;
|
||||||
@@ -176,17 +178,30 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
|
|||||||
i += 3;
|
i += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
auto isInBounds = [tokens](size_t i) {
|
||||||
if (i + 3 < tokens.size() && tokens[i + 3].tokenType != TokenType::EndOfFile) {
|
return i < tokens.size() && tokens[i].tokenType != TokenType::EndOfFile;
|
||||||
size_t offset = tokens[i + 1].tokenType == TokenType::IdentifierStart ? 5 : 2;
|
};
|
||||||
if (tokens[i].tokenType == TokenType::SectionIdentifierStart ||
|
|
||||||
tokens[i].tokenType == TokenType::SectionEnd) {
|
// Parse elements of a question.
|
||||||
|
while (isInBounds(i)) {
|
||||||
|
|
||||||
|
// Handle other constructs.
|
||||||
|
if (tokens[i].tokenType == TokenType::SectionIdentifierStart) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i + offset < tokens.size() && tokens[i + offset].tokenType == TokenType::QuestionEnd) {
|
if (tokens[i].tokenType == TokenType::SectionEnd) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check question end.
|
||||||
|
if (tokens[i].tokenType == TokenType::ElementDashStart && isInBounds(i + 3)) {
|
||||||
|
// Distance to the possible question end.
|
||||||
|
size_t offset = tokens[i + 1].tokenType == TokenType::IdentifierStart ? 5 : 2;
|
||||||
|
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 {
|
return {
|
||||||
questions,
|
questions,
|
||||||
"Invalid identifier placement",
|
"Invalid identifier placement",
|
||||||
@@ -195,42 +210,72 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i + 2 >= tokens.size()) {
|
|
||||||
break;
|
// Determine element type.
|
||||||
|
bool isDash;
|
||||||
|
bool isGroup = false;
|
||||||
|
if (isInBounds(i+1) && tokens[i].tokenType == TokenType::ElementOrderModifier) {
|
||||||
|
isOrderQuestion = true;
|
||||||
|
if (!isDash) {
|
||||||
|
// TODO: err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tokens[i].tokenType == TokenType::ElementDashStart) {
|
||||||
|
isDash = true;
|
||||||
|
if (isOrderQuestion) {
|
||||||
|
// TODO: err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isDash = false;
|
||||||
|
}
|
||||||
|
if (isInBounds(i+2) && tokens[i].tokenType == TokenType::MatchGroupEnd) {
|
||||||
|
isGroup = true;
|
||||||
|
isGroupQuestion = true;
|
||||||
|
if (!isDash) {
|
||||||
|
// TODO: err
|
||||||
|
}
|
||||||
|
if (isOrderQuestion) {
|
||||||
|
// TODO: err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create question elements
|
|
||||||
QuestionElement questionElement;
|
QuestionElement questionElement;
|
||||||
questionElement.isDash = (tokens[i].tokenType == TokenType::ElementDashStart);
|
questionElement.isDash = isDash;
|
||||||
|
questionElement.isGroup = isGroup;
|
||||||
questionElement.content = tokens[i + 1].content;
|
questionElement.content = tokens[i + 1].content;
|
||||||
questionElements.push_back(questionElement);
|
questionElements.push_back(questionElement);
|
||||||
i += 2;
|
|
||||||
|
size_t offset = 2;
|
||||||
|
if (isOrderQuestion) {
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
if (isGroup) {
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (questionElements.size() > 1) {
|
if (questionElements.size() > 1) {
|
||||||
auto* mcq = new MultipleChoiceQuestion();
|
if (isGroupQuestion) {
|
||||||
mcq->ID = id;
|
GroupQuestion *question = new GroupQuestion();
|
||||||
mcq->QuestionText = questionText;
|
// TODO
|
||||||
|
|
||||||
|
} if (isOrderQuestion) {
|
||||||
|
auto *question = new MultiElementQuestion();
|
||||||
|
question->ID = id;
|
||||||
|
question->QuestionText = questionText;
|
||||||
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;
|
||||||
mcq->Choices.push_back(choice);
|
question->Choices.push_back(choice);
|
||||||
}
|
}
|
||||||
mcq->Section = section;
|
question->Section = section;
|
||||||
questions.push_back(mcq);
|
questions.push_back(question);
|
||||||
if (debug) {
|
if (debug) {
|
||||||
std::cout << mcq->ToString() << "\n";
|
std::cout << question->ToString() << "\n";
|
||||||
}
|
}
|
||||||
} else if (questionElements.size() == 1) {
|
|
||||||
auto* saq = new SingleAnswerQuestion();
|
|
||||||
saq->ID = id;
|
|
||||||
saq->QuestionText = questionText;
|
|
||||||
saq->Answer = questionElements[0].content;
|
|
||||||
saq->Section = section;
|
|
||||||
questions.push_back(saq);
|
|
||||||
if (debug) {
|
|
||||||
std::cout << saq->ToString() << "\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (tokens[i].tokenType == TokenType::SectionIdentifierStart) {
|
} else if (tokens[i].tokenType == TokenType::SectionIdentifierStart) {
|
||||||
|
|||||||
37
tasks.md
Normal file
37
tasks.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
## 1-N Answer question
|
||||||
|
|
||||||
|
- kfoewf >
|
||||||
|
- fiewfiwe
|
||||||
|
|
||||||
|
- kfoewf >
|
||||||
|
- fiewfiwe
|
||||||
|
- fewpfowe
|
||||||
|
- fweofopew
|
||||||
|
- ofpewpofkew
|
||||||
|
|
||||||
|
## Order question
|
||||||
|
|
||||||
|
- ewjpfwe >
|
||||||
|
^- ewijfew
|
||||||
|
^- ewijfew
|
||||||
|
^- ewijfew
|
||||||
|
^- ewijfew
|
||||||
|
^- ewijfew
|
||||||
|
|
||||||
|
## Match question
|
||||||
|
|
||||||
|
- iowjefew >
|
||||||
|
- fiwfo:
|
||||||
|
- fewfew
|
||||||
|
- ifoewf
|
||||||
|
- ejpfe:
|
||||||
|
- _
|
||||||
|
- fewfw:
|
||||||
|
- fewjpfe
|
||||||
|
- fioewf
|
||||||
|
|
||||||
|
- [ ] Augment the lexer for Order and match question;
|
||||||
|
- [ ] Parse the questions accordingly;
|
||||||
|
- [ ] Escape character;
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user