module otya.smilebasic.program; import otya.smilebasic.error; import std.range; import std.container; import std.algorithm; unittest { Slot slot; slot.init(); slot.currentLine = 1; assert(slot.get() == "\n"); slot.currentLine = 1; slot.set("1"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "1\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 2; slot.set("2"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "1\n"); assert(slot.get() == "2\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; slot.set("A"); assert(slot.get() == "2\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "A\n"); assert(slot.get() == "2\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 2; slot.set("I\nJ"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); //PRGINS slot.currentLine = 1; slot.insert("3", false); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "3\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; slot.insert("4", true); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "3\n"); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 2; slot.insert("5", false); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "3\n"); assert(slot.get() == "5\n"); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 2; slot.insert("6", true); assert(slot.get() == "6\n"); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "3\n"); assert(slot.get() == "5\n"); assert(slot.get() == "6\n"); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); //PRGDEL slot.currentLine = 1; slot.delete_(1); assert(slot.get() == "5\n"); assert(slot.get() == "6\n"); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "5\n"); assert(slot.get() == "6\n"); assert(slot.get() == "4\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 2; slot.delete_(2); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "5\n"); assert(slot.get() == "A\n"); assert(slot.get() == "I\n"); assert(slot.get() == "J\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.delete_(-1); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "\n"); assert(slot.get() == ""); slot.set("A"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "A\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); slot.delete_(1); slot.delete_(1); slot.delete_(1); slot.delete_(1); slot.currentLine = 1; slot.set("A"); slot.set("B"); slot.currentLine = 1; slot.set("A"); slot.set("B"); slot.currentLine = 1; assert(slot.get() == "A\n"); assert(slot.get() == "B\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); assert(slot.size(SizeType.Line) == 3); slot.delete_(-1); assert(slot.size(SizeType.Line) == 0); slot.currentLine = 1; slot.set("A"); slot.set("B"); slot.delete_(1); assert(slot.get() == "B\n"); assert(slot.get() == ""); slot.currentLine = -1; assert(slot.get() == "B\n"); assert(slot.get() == ""); slot.currentLine = 1; assert(slot.get() == "A\n"); assert(slot.get() == "B\n"); assert(slot.get() == ""); slot.currentLine = 2; slot.delete_(1); assert(slot.get() == "A\n"); assert(slot.get() == ""); slot.delete_(-1); slot.set("1"); slot.set("2"); slot.set("3"); slot.set("4"); slot.set("5"); slot.set("6"); slot.set("7"); slot.currentLine = 1; slot.currentLine = 1; slot.get(); slot.insert("A", false); slot.get(); slot.get(); slot.insert("B", true); assert(slot.get() == "4\n"); assert(slot.get() == "5\n"); slot.insert("C", false); slot.get(); slot.currentLine = 1; slot.currentLine = 1; assert(slot.get() == "A\n"); assert(slot.get() == "1\n"); assert(slot.get() == "2\n"); assert(slot.get() == "3\n"); assert(slot.get() == "B\n"); assert(slot.get() == "4\n"); assert(slot.get() == "C\n"); assert(slot.get() == "5\n"); assert(slot.get() == "6\n"); assert(slot.get() == "7\n"); assert(slot.get() == "\n"); assert(slot.get() == ""); assert(slot.get() == ""); assert(slot.get() == ""); assert(slot.get() == ""); slot.insert("D", true); slot.insert("E", false); slot.insert("F", false); slot.insert("G", false); slot.currentLine = 1; assert(slot.get() == "A\n"); assert(slot.get() == "1\n"); assert(slot.get() == "2\n"); assert(slot.get() == "3\n"); assert(slot.get() == "B\n"); assert(slot.get() == "4\n"); assert(slot.get() == "C\n"); assert(slot.get() == "5\n"); assert(slot.get() == "6\n"); assert(slot.get() == "7\n"); assert(slot.get() == "\n"); assert(slot.get() == "E\n"); assert(slot.get() == "F\n"); assert(slot.get() == "G\n"); assert(slot.get() == "D\n"); assert(slot.get() == ""); } enum SizeType { Line = 0, Char = 1, FreeChar = 2, } struct Slot { private DList!wstring program; private DList!wstring.Range range; private DList!wstring.Range range2; wstring name; void init() { program = make!(DList!wstring)(["\n"w]); range = program[]; } void load(wstring filename, wstring data) { name = filename; program.clear(); foreach(l; splitter(data, "\n")) { program.insertBack(l ~ '\n'); } if (program.back == "\n") { program.removeBack(); } range = program[]; } wchar[] text() { import std.algorithm.iteration, std.outbuffer; auto buffer = new OutBuffer(); buffer.reserve(program.opSlice.map!"(a.length + 1) * 2".sum); foreach(line; program) { buffer.write(line); } ubyte[] progbuff = buffer.toBytes; wchar[] progbuff2 = (cast(wchar*)progbuff.ptr)[0..progbuff.length / 2]; return progbuff2; } import otya.smilebasic.token; wstring getLine(SourceLocation loc) { import std.string; int i; foreach (line; program) { i++; if (i == loc.line) return line; } return ""; } void currentLine(int line) { if (line == -1) { //range = DList!wstring.Range(program._last); range = program[]; while (!range.empty) { auto old = range.save; range.popFront(); if (range.empty) { range = old; break; } } range2 = range.save; return; } range = program[]; range.popFrontN(line - 1); range2 = range.save; } wstring get() { if (range.empty) { return ""; } auto a = range.front; range2 = range.save; range.popFront(); return a; } void set(wstring v) { int i; foreach(l; splitter(v, "\n")) { if (!range.empty && i == 0) { auto b = range.save; b.popFront(); if (b.empty) { program.insertBefore(range, l ~ "\n"); } else { range2 = range.save; range.put(l ~ "\n"); } } else { if (range.empty) { if (!range2.empty) { range = range2; program.insertBefore(range, l ~ "\n"); range2 = range.save; range.popFront(); continue; } program.insertBefore(range, l ~ "\n"); } else { program.insertBefore(range, l ~ "\n"); } } i++; } } void insert(wstring line, bool isBack) { typeof(range) backup; if (isBack) { backup = range.save; range = range2; range.popFront(); } foreach(l; splitter(line, "\n")) { if (isBack) { //backup = range.save; if (range.empty) { program.insertBack(l ~ "\n"); continue; } program.insertBefore(range, l ~ "\n"); } else { if (range2.empty) { program.insertBack(l ~ "\n"); continue; } program.insertBefore(range2, l ~ "\n"); } } if (isBack) { if (range.empty) { currentLine = -1; return; } range = backup; if (range == range2) { range.popFront(); } else { if (!range.empty) { range2 = range.save; } } return; } if (!range.empty) range2 = range.save; } void delete_(int count) { if (count < 0) { init(); range2 = range.save; range.popFront(); return; } else { if (range.empty) { for (int i = 0; i < count;i++) { program.removeBack(); } } else { range = program.linearRemove(range.take(count)); } if (range.empty) { range = program[]; if (range.empty) { program.insertAfter(range, "\n"w); range = program[]; } else { currentLine = -1; } } } range2 = range.save; } int memorySize = 1048476;//WiiU..? int size(SizeType type) { switch(type) { case SizeType.Line: { auto a = cast(int)program[].walkLength; if (a == 1 && program.front.length == 1 && program.front[0] == '\n'/*?*/) return 0; return a; } case SizeType.Char: return cast(int)program[].map!(x => x.length).sum; case SizeType.FreeChar: return cast(int)(memorySize - program[].map!(x => x.length).sum).max(0); default: throw new OutOfRange("PRGSIZE", 2); } } } class Program { Slot[] slot; bool PRGEDITused; int currentSlot = -1/*PRGSLOT default value*/; int slotSize = 4; this() { slot = new Slot[5]; foreach(ref s; slot) { s.init(); } } void edit(int slot, int line) { PRGEDITused = true; this.currentSlot = slot; this.slot[slot].currentLine = line; } wstring get() { if (!PRGEDITused) throw new UsePRGEDITBeforeAnyPRGFunction("PRGGET$"); return this.slot[currentSlot].get; } void set(wstring v) { if (!PRGEDITused) throw new UsePRGEDITBeforeAnyPRGFunction("PRGSET"); this.slot[currentSlot].set = v; } void insert(wstring line, bool isBack) { if (!PRGEDITused) throw new UsePRGEDITBeforeAnyPRGFunction("PRGINS"); this.slot[currentSlot].insert(line, isBack); } void delete_(int count) { if (!PRGEDITused) throw new UsePRGEDITBeforeAnyPRGFunction("PRGDEL"); this.slot[currentSlot].delete_(count); } int size(int slot, SizeType st) { return this.slot[slot].size(st); } wstring name(int slot) { return this.slot[slot].name; } }