From 4ee8a06232070a21e7425a3ab20202ea92d62518 Mon Sep 17 00:00:00 2001 From: Slavasil Date: Sun, 23 Mar 2025 22:09:27 +0300 Subject: [PATCH] implement question bank parsing --- App/Data/Database.cs | 103 +++++++++++++++++++++++++++++++++++++++++++ App/Program.cs | 10 ++++- 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 App/Data/Database.cs diff --git a/App/Data/Database.cs b/App/Data/Database.cs new file mode 100644 index 0000000..5f50d17 --- /dev/null +++ b/App/Data/Database.cs @@ -0,0 +1,103 @@ +namespace Game.Data { + public class Database { + public List questions = new List(); + public static Database ReadFromFile(string filename) { + var db = new Database(); + using (StreamReader rd = new StreamReader(filename)) { + string? line; + int lineNumber = 1; + while ((line = rd.ReadLine()) != null) { + int i = 0; + int len = line.Length; + int state = 0; + int correctAnswer = -1; + List answers = new List(); + + // read question + while (i < len + 1) { + if (i == len) { + throw new DatabaseReadException(I18n.Message.ERROR_NO_ANSWER_OPTIONS, lineNumber, i); + } else if (line[i] == '|') + if (state == 1) { + ++i; + break; + } else { + state = 1; + } + else state = 0; + ++i; + } + string question = line.Substring(0, i - 2); + + int currentAnswer = 0; + while (i < len) { + // read answer text + state = 0; + int answerStart = i; + while (i < len + 1) { + if (i == len) { + throw new DatabaseReadException(I18n.Message.ERROR_EXPECTED_ARROW_TOKEN, lineNumber, i); + } else if (state == 0) { + if (line[i] == '=') + state = 1; + } else if (state == 1) { + if (line[i] == '>') { + ++i; + break; + } else { + state = 0; + } + } + ++i; + } + string answer = line.Substring(answerStart, i - answerStart - 2); + // read True or False + if (line[i] == 'T') { + if (i + 3 < len && line[i + 1] == 'r' && line[i + 2] == 'u' && line[i + 3] == 'e') { + correctAnswer = currentAnswer; + i += 4; + } else { + throw new DatabaseReadException(I18n.Message.ERROR_EXPECTED_TRUE_OR_FALSE, lineNumber, i); + } + } else if (line[i] == 'F') { + if (i + 4 < len && line[i + 1] == 'a' && line[i + 2] == 'l' && line[i + 3] == 's' && line[i + 4] == 'e') { + i += 5; + } else { + throw new DatabaseReadException(I18n.Message.ERROR_EXPECTED_TRUE_OR_FALSE, lineNumber, i); + } + } + // consume separator + if (i + 1 < len && (line[i] != '|' || line[i + 1] != '|')) + throw new DatabaseReadException(I18n.Message.ERROR_EXPECTED_ANSWER_SEPARATOR, lineNumber, i); + i += 2; + answers.Add(answer); + ++currentAnswer; + } + + lineNumber++; + db.questions.Add(new Question(question, answers, correctAnswer)); + } + } + return db; + } + } + + public class DatabaseReadException : Exception { + public readonly int line, column; + public readonly I18n.Message messageId; + + public DatabaseReadException(I18n.Message messageId) { + this.messageId = messageId; + line = -1; + column = -1; + } + + public DatabaseReadException(I18n.Message messageId, int line, int column) { + this.messageId = messageId; + this.line = line; + this.column = column; + } + } + + public record Question (string question, List answers, int correctAnswer); +} \ No newline at end of file diff --git a/App/Program.cs b/App/Program.cs index 4dd9c2e..9f7c6ee 100644 --- a/App/Program.cs +++ b/App/Program.cs @@ -29,7 +29,15 @@ namespace Game { return 2; } - string databaseFileName = opts.databaseFilename ?? gameConfig.databasePath; + string databaseFileName = opts.databaseFilename ?? gameConfig.databasePath; + Database questionDatabase; + try { + questionDatabase = Database.ReadFromFile(databaseFileName); + } catch (DatabaseReadException e) { + display.ShowFatalError(string.Format(I18n.GetMessage(I18n.Message.SYNTAX_ERROR), I18n.GetMessage(e.messageId), e.line.ToString(), e.column.ToString())); + Console.ReadKey(); + return 3; + } display.ResetWindow();