570 lines
14 KiB
D
570 lines
14 KiB
D
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;
|
|
}
|
|
}
|