mirror of
https://github.com/jorenchik/mdemory.git
synced 2026-03-22 00:26:21 +00:00
parser supports all needed question types
This commit is contained in:
@@ -8,6 +8,10 @@
|
||||
|
||||
|
||||
struct Question {
|
||||
std::string ID;
|
||||
std::string QuestionText;
|
||||
std::string Section;
|
||||
|
||||
virtual std::string ToString() const = 0;
|
||||
virtual ~Question() = default;
|
||||
};
|
||||
@@ -24,23 +28,18 @@ enum MultiElementType {
|
||||
};
|
||||
|
||||
struct MultiElementQuestion : public Question {
|
||||
std::string ID;
|
||||
std::string QuestionText;
|
||||
std::vector<Choice> Choices;
|
||||
std::string Section;
|
||||
MultiElementType type;
|
||||
|
||||
std::string ToString() const override;
|
||||
};
|
||||
|
||||
struct Group {
|
||||
std::string name;
|
||||
std::string elements;
|
||||
std::string name;
|
||||
std::vector<std::string> elements;
|
||||
};
|
||||
|
||||
struct GroupQuestion : public Question {
|
||||
std::string ID;
|
||||
std::string QuestionText;
|
||||
std::vector<Group> Groups;
|
||||
|
||||
std::string ToString() const override;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
1
src/cpp/qtapp/.gitignore
vendored
1
src/cpp/qtapp/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
Debug
|
||||
Release
|
||||
.cache
|
||||
|
||||
@@ -199,7 +199,7 @@ void CreateMdems(std::vector<Question*>& questions) {
|
||||
};
|
||||
|
||||
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(
|
||||
QString::fromStdString(mw->QuestionText)
|
||||
);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1
src/cpp/transpiler/.gitignore
vendored
1
src/cpp/transpiler/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
Debug
|
||||
Release
|
||||
.cache
|
||||
|
||||
@@ -111,7 +111,16 @@ Result<std::vector<Token>> TokenizeMdem(const std::string& fileRunes) {
|
||||
row += 1;
|
||||
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
|
||||
if (!textStarted) {
|
||||
|
||||
@@ -19,25 +19,45 @@ struct QuestionElement {
|
||||
};
|
||||
|
||||
std::string MultiElementQuestion::ToString() const {
|
||||
std::stringstream choiceOut;
|
||||
std::stringstream ss;
|
||||
for (const auto& choice : Choices) {
|
||||
char opener;
|
||||
if (choice.IsCorrect) {
|
||||
if (type == MultiElementType::Order) {
|
||||
opener = '^';
|
||||
} else if (choice.IsCorrect) {
|
||||
opener = '+';
|
||||
} else {
|
||||
opener = '-';
|
||||
}
|
||||
choiceOut << opener << " " << choice.Answer << "; ";
|
||||
ss << opener << " " << choice.Answer << "; ";
|
||||
}
|
||||
return std::format(
|
||||
"<Multiple choice>:{} section: {} id: {}\n{}",
|
||||
QuestionText,
|
||||
Section,
|
||||
ID,
|
||||
choiceOut.str()
|
||||
"<Multiple element>\nsection:{}\nid:{}\n{}\n{}",
|
||||
Section,
|
||||
ID,
|
||||
QuestionText,
|
||||
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
|
||||
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;
|
||||
bool isOrderQuestion = 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.
|
||||
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;
|
||||
questionText = tokens[i + 4].content;
|
||||
i += 6;
|
||||
@@ -178,10 +211,6 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
|
||||
i += 3;
|
||||
}
|
||||
|
||||
auto isInBounds = [tokens](size_t i) {
|
||||
return i < tokens.size() && tokens[i].tokenType != TokenType::EndOfFile;
|
||||
};
|
||||
|
||||
// Parse elements of a question.
|
||||
while (isInBounds(i)) {
|
||||
|
||||
@@ -194,12 +223,17 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
|
||||
}
|
||||
|
||||
// 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.
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (offset == 5 && tokens[i + 5].tokenType != TokenType::QuestionEnd) {
|
||||
// Cannot place the identifier on the ordinary element.
|
||||
return {
|
||||
@@ -214,39 +248,61 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
|
||||
// Determine element type.
|
||||
bool isDash;
|
||||
bool isGroup = false;
|
||||
if (isInBounds(i+1) && tokens[i].tokenType == TokenType::ElementOrderModifier) {
|
||||
isOrderQuestion = true;
|
||||
if (!isDash) {
|
||||
// TODO: err
|
||||
}
|
||||
}
|
||||
bool isOrder = false;
|
||||
if (tokens[i].tokenType == TokenType::ElementDashStart) {
|
||||
isDash = true;
|
||||
if (isOrderQuestion) {
|
||||
// TODO: err
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
isGroupQuestion = true;
|
||||
if (!isDash) {
|
||||
// TODO: err
|
||||
}
|
||||
if (isOrderQuestion) {
|
||||
// TODO: err
|
||||
return {questions, "group questions can only be used with dashes ('-')"};
|
||||
}
|
||||
}
|
||||
|
||||
QuestionElement questionElement;
|
||||
questionElement.isDash = isDash;
|
||||
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);
|
||||
|
||||
size_t offset = 2;
|
||||
if (isOrderQuestion) {
|
||||
if (isOrder) {
|
||||
offset += 1;
|
||||
}
|
||||
if (isGroup) {
|
||||
@@ -256,25 +312,53 @@ Result<std::vector<Question*>> ParseQuestions(const std::vector<Token>& tokens)
|
||||
i += offset;
|
||||
}
|
||||
|
||||
if (questionElements.size() > 1) {
|
||||
if (questionElements.size() > 0) {
|
||||
if (isGroupQuestion) {
|
||||
GroupQuestion *question = new GroupQuestion();
|
||||
// TODO
|
||||
|
||||
} if (isOrderQuestion) {
|
||||
auto *question = new GroupQuestion();
|
||||
question->ID = id;
|
||||
question->QuestionText = questionText;
|
||||
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();
|
||||
question->ID = id;
|
||||
question->QuestionText = questionText;
|
||||
question->Section = section;
|
||||
for (const auto& elem : questionElements) {
|
||||
Choice choice;
|
||||
choice.Answer = elem.content;
|
||||
choice.IsCorrect = !elem.isDash;
|
||||
question->Choices.push_back(choice);
|
||||
}
|
||||
question->Section = section;
|
||||
questions.push_back(question);
|
||||
if (isPlusQuestion) {
|
||||
question->type = MultiElementType::MultiChoice;
|
||||
} else if (isOrderQuestion) {
|
||||
question->type = MultiElementType::Order;
|
||||
} else {
|
||||
question->type = MultiElementType::Regular;
|
||||
}
|
||||
if (debug) {
|
||||
std::cout << question->ToString() << "\n";
|
||||
std::cout << question->ToString() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user