diff options
1 files changed, 303 insertions, 0 deletions
| -rw-r--r-- | Week12/Exercise06.cpp | 303 |
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'; +} |
