Website Summary Files Commits Branches @commit @diff @stats
path: root / Week12 / Exercise06.cpp
diff options
context:
space:
mode:
1 files changed, 303 insertions, 0 deletions
-rw-r--r--Week12/Exercise06.cpp303
diff --git a/Week12/Exercise06.cpp b/Week12/Exercise06.cpp
new file mode 100644
index 0000000..e0d3a72
--- /dev/null
+++ b/Week12/Exercise06.cpp
@@ -0,0 +1,303 @@
+#include <iostream>
+
+/* Менажиране на памет на текста */
+
+// Заделяне на памет
+void newText(char**& text, unsigned& size, unsigned& allocated) {
+ size = 0;
+ allocated = 8;
+ text = new char*[allocated];
+}
+
+// Реоразмеряване
+void enlargeText(char**& text, unsigned& size, unsigned& allocated) {
+ allocated *= 2;
+ char** largerText = new char*[allocated];
+
+ for (unsigned i = 0; i < size; i++)
+ largerText[i] = text[i];
+
+ delete[] text;
+ text = largerText;
+}
+
+// Освобождаване на памет
+void deleteText(char**& text, unsigned& size) {
+ for (unsigned i = 0; i < size; i++)
+ delete[] text[i];
+ delete[] text;
+ text = nullptr;
+}
+
+/* Обсег */
+//
+// Тези функции наричам "консуматори", защото приемат указател към низ,
+// който указател инкрементират.
+//
+// Тоест, ако указателя сочи към "1234Hello", след като го подадем на
+// consumeUnsigned, ще сочи към "Hello" (и value ще бъде 1234).
+//
+
+bool isDigit(char x) {
+ return '0' <= x && x <= '9';
+}
+
+bool consumeUnsigned(const char*& cmd, unsigned& value) {
+ value = 0;
+ bool consumedAnything = false;
+
+ while (isDigit(*cmd) && *cmd != '\0') {
+ value = value * 10 + (*cmd - '0');
+ cmd++;
+ consumedAnything = true;
+ }
+
+ return consumedAnything;
+}
+
+bool consumeLineAddress(const char*& cmd, unsigned current, unsigned lastLine, unsigned& address) {
+ bool consumedAnything = false;
+
+ if (*cmd == '.') {
+ address = current;
+ cmd++;
+ consumedAnything = true;
+ }
+ else if (*cmd == '$') {
+ address = lastLine;
+ cmd++;
+ consumedAnything = true;
+ }
+ else {
+ consumedAnything = consumeUnsigned(cmd, address);
+
+ // Ако не ни е подаден адрес
+ if (address == 0) {
+ address = current;
+ }
+ // Ако има числов адрес
+ else {
+ address--;
+ if (address > lastLine)
+ address = lastLine;
+ }
+ }
+
+ return consumedAnything;
+}
+
+// Обработва подадения обсег, като връща индекси rangeStart и rangeEnd,
+// т.е. автоматично превръща ред "5" на индекс 4.
+void consumeLineRange(const char*& cmd, unsigned current, unsigned lastLine, unsigned& rangeStart, unsigned& rangeEnd) {
+ bool consumedStart = consumeLineAddress(cmd, current, lastLine, rangeStart);
+
+ if (*cmd == ',') {
+ // Ако не въведем обсег, тогава обсега е текущия ред.
+ // Но, ако въведем обсег за няколко реда, без да има
+ // експлицитни начало и край (т.е. целия обсег е ","),
+ // тогава това трябва да е същото като "1,$".
+ if (!consumedStart)
+ rangeStart = 0;
+
+ cmd++;
+
+ if (!consumeLineAddress(cmd, current, lastLine, rangeEnd))
+ rangeEnd = lastLine;
+ }
+ else {
+ rangeEnd = rangeStart;
+ }
+}
+
+/* Команда */
+
+void printCommand(char** text, unsigned& textLines, unsigned selected) {
+ if (selected >= textLines)
+ std::cout << "(null text)" << std::endl;
+ else
+ std::cout << text[selected] << std::endl;
+}
+
+void enumerateCommand(char** text, unsigned& textLines, unsigned selected) {
+ if (selected >= textLines) {
+ std::cout << "(null text)" << std::endl;
+ }
+ else {
+ std::cout << (selected + 1) << ' ';
+ printCommand(text, textLines, selected);
+ }
+}
+
+void deleteCommand(char**& text, unsigned& textLines, unsigned selected) {
+ if (textLines < 1)
+ return;
+
+ delete[] text[selected];
+
+ // Отместваме всички редове под изтрития с една позиция нагоре
+ if (textLines > 1) {
+ for (unsigned l = selected; l < textLines - 1; l++)
+ text[l] = text[l + 1];
+ }
+
+ text[--textLines] = nullptr;
+}
+
+char* cpystr(const char* str) {
+ // Тази логика може да се изведе в strlen
+ unsigned length = 0;
+ while (str[length] != '\0')
+ length++;
+
+ char* copy = new char[length + 1];
+ // <= length, за да копираме терминиращата нула
+ for (unsigned i = 0; i <= length; i++)
+ copy[i] = str[i];
+
+ return copy;
+}
+
+void insertCommand(const char* argText, char**& text, unsigned& textLines, unsigned& textAllocated, unsigned selected) {
+ if (textAllocated <= textLines + 1)
+ enlargeText(text, textLines, textAllocated);
+
+ // Да вмъкнем текст преди даден ред е същото, като да
+ // измествим с един ред надолу всички редове от избрания до последния вкл.
+ // и да променим реда на който индекс оригинално беше избрания.
+ // Гаден случай: ако текста ни е празен, не трябва да правим преместването.
+ if (textLines > 0) {
+ for (unsigned i = textLines; i > selected; i--)
+ text[i] = text[i - 1];
+ }
+ textLines++;
+
+ text[selected] = cpystr(argText);
+}
+
+void appendCommand(const char* argText, char**& text, unsigned& textLines, unsigned& textAllocated, unsigned selected) {
+ // Да добавиш текст след подаден ред е същото, като
+ // да добавиш текст преди реда след подадения.
+ // Гаден случай: ако текста ни е празен, трябва задължително да
+ // вмъкнем на ред с индекс 0.
+ insertCommand(argText, text, textLines, textAllocated, selected + (textLines > 0));
+}
+
+bool evaluateCommand(const char* cmd, char**& text, unsigned& textLines, unsigned& textAllocated, unsigned& current) {
+ unsigned start, end;
+ consumeLineRange(cmd, current, textLines - (textLines > 1), start, end);
+
+ if (start > end) {
+ std::cout << "Start line in range cannot be after end line!" << std::endl;
+ return true;
+ }
+
+ for (unsigned selected = start; selected <= end; selected++) {
+ switch (*cmd) {
+ // Ако има само въведен обсег и няма въведена команда
+ case '\0':
+ current = selected;
+ break;
+
+ case 'p':
+ printCommand(text, textLines, selected);
+ break;
+
+ case 'n':
+ enumerateCommand(text, textLines, selected);
+ break;
+
+ case 'd':
+ deleteCommand(text, textLines, selected);
+
+ if (selected <= current && current > 0)
+ current--;
+
+ if (end > 0) {
+ end--;
+ // Тук се възползваме от факта, че (при unsigned int)
+ // 0 - 1 = 4294967295
+ // 4294967295 + 1 = 0
+ selected--;
+ }
+
+ break;
+
+ case 'i':
+ insertCommand(cmd + 1, text, textLines, textAllocated, selected);
+ current = selected;
+
+ end++;
+ selected++;
+ break;
+
+ case 'a':
+ appendCommand(cmd + 1, text, textLines, textAllocated, selected);
+ current = selected + (textLines > 1);
+
+ end++;
+ selected++;
+ break;
+
+ case 'q':
+ return false;
+
+ default:
+ std::cout << "Unknown command " << *cmd << "!" << std::endl;
+ return true;
+ }
+ }
+ return true;
+}
+
+void getInput(char*& input, unsigned &allocated, unsigned &size);
+
+int main() {
+ unsigned textAllocated, textSize;
+ char** text;
+ newText(text, textSize, textAllocated);
+
+ unsigned currentLine = 0;
+
+ unsigned cmdAllocated = 0, cmdSize = 0;
+ char* cmd;
+
+ getInput(cmd, cmdAllocated, cmdSize);
+ while (evaluateCommand(cmd, text, textSize, textAllocated, currentLine)) {
+ cmdSize = 0;
+ getInput(cmd, cmdAllocated, cmdSize);
+ }
+
+ deleteText(text, textSize);
+ delete[] cmd;
+
+ return 0;
+}
+
+void getInput(char*& input, unsigned &allocated, unsigned &size) {
+ if (allocated == 0) {
+ allocated = 8;
+ size = 0;
+
+ input = new char[allocated];
+ }
+
+ char curr;
+ std::cin.get(curr);
+ while (curr != '\n') {
+ input[size++] = curr;
+ std::cin.get(curr);
+
+ // Реоразмеряване
+ if (size >= allocated) {
+ allocated *= 2;
+ char* newSentenceArray = new char[allocated];
+
+ for (unsigned i = 0; i < size; i++)
+ newSentenceArray[i] = input[i];
+
+ delete[] input;
+ input = newSentenceArray;
+ }
+ }
+ input[size] = '\0';
+}