705 lines
18 KiB
D
705 lines
18 KiB
D
module otya.smilebasic.type;
|
|
import otya.smilebasic.error;
|
|
import otya.smilebasic.compiler;
|
|
import otya.smilebasic.vm;
|
|
static import std.algorithm;
|
|
static import std.algorithm.comparison;
|
|
enum ValueType : byte
|
|
{
|
|
Void,//DEF A()ENDの返り値とか未初期化の変数とか
|
|
Integer,
|
|
Double,
|
|
String,
|
|
Array,
|
|
IntegerArray,
|
|
DoubleArray,
|
|
StringArray,
|
|
InternalAddress,
|
|
InternalSlotAddress,
|
|
Reference,
|
|
IntegerReference,
|
|
DoubleReference,
|
|
StringReference,
|
|
StringArrayReference,
|
|
Data,
|
|
Function,
|
|
}
|
|
struct Data
|
|
{
|
|
DataTable table;
|
|
int index;
|
|
void read(out Value value, VM vm)
|
|
{
|
|
if(index >= table.length)
|
|
throw new OutOfDATA();
|
|
value = table.read(index);
|
|
index++;
|
|
}
|
|
}
|
|
struct VMAddress
|
|
{
|
|
byte slot;
|
|
uint address;
|
|
}
|
|
struct Value
|
|
{
|
|
ValueType type;
|
|
union
|
|
{
|
|
int integerValue;
|
|
double doubleValue;
|
|
Array!wchar stringValue;
|
|
Array!int integerArray;
|
|
Array!double doubleArray;
|
|
Array!(Array!wchar) stringArray;
|
|
VMAddress internalAddress;
|
|
Value* reference;
|
|
ArrayReference!int integerReference;
|
|
ArrayReference!double doubleReference;
|
|
ArrayReference!wchar stringReference;
|
|
ArrayReference!(Array!wchar) stringArrayReference;
|
|
Data data;
|
|
Function func;
|
|
}
|
|
this(int value)
|
|
{
|
|
this.type = ValueType.Integer;
|
|
integerValue = value;
|
|
}
|
|
this(bool value)
|
|
{
|
|
this(cast(int)value);
|
|
}
|
|
this(double value)
|
|
{
|
|
this.type = ValueType.Double;
|
|
doubleValue = value;
|
|
}
|
|
this(wstring value)
|
|
{
|
|
this.type = ValueType.String;
|
|
stringValue = new Array!wchar(cast(wchar[])value.dup);
|
|
}
|
|
this(wchar[] value)
|
|
{
|
|
this.type = ValueType.String;
|
|
stringValue = new Array!wchar(value);
|
|
}
|
|
this(wchar value)
|
|
{
|
|
this.type = ValueType.String;
|
|
stringValue = new Array!wchar([value]);
|
|
}
|
|
this(Array!wchar value)
|
|
{
|
|
this.type = ValueType.String;
|
|
stringValue = value;
|
|
}
|
|
this(ValueType type)
|
|
{
|
|
this.type = type;
|
|
if(type == ValueType.String)
|
|
{
|
|
stringValue = new Array!wchar(0);
|
|
}
|
|
}
|
|
this(Value* r)
|
|
{
|
|
this.type = ValueType.Reference;
|
|
reference = r;
|
|
}
|
|
this(ArrayReference!int r)
|
|
{
|
|
this.type = ValueType.IntegerReference;
|
|
integerReference = r;
|
|
}
|
|
this(ArrayReference!double r)
|
|
{
|
|
this.type = ValueType.DoubleReference;
|
|
doubleReference = r;
|
|
}
|
|
this(ArrayReference!wchar r)
|
|
{
|
|
this.type = ValueType.StringReference;
|
|
stringReference = r;
|
|
}
|
|
this(ArrayReference!(Array!wchar) r)
|
|
{
|
|
this.type = ValueType.StringArrayReference;
|
|
stringArrayReference = r;
|
|
}
|
|
this(Data data)
|
|
{
|
|
this.type = ValueType.Data;
|
|
this.data = data;
|
|
}
|
|
this(Function func)
|
|
{
|
|
this.type = ValueType.Function;
|
|
this.func = func;
|
|
}
|
|
this(Array!int a)
|
|
{
|
|
this.type = ValueType.IntegerArray;
|
|
this.integerArray = a;
|
|
}
|
|
this(Array!double a)
|
|
{
|
|
this.type = ValueType.DoubleArray;
|
|
this.doubleArray = a;
|
|
}
|
|
this(Array!(Array!wchar) a)
|
|
{
|
|
this.type = ValueType.StringArray;
|
|
this.stringArray = a;
|
|
}
|
|
void castOp(ValueType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ValueType.Double:
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
int boolValue()
|
|
{
|
|
switch(type)
|
|
{
|
|
case ValueType.Double:
|
|
return this.doubleValue != 0;
|
|
case ValueType.Integer:
|
|
return this.integerValue != 0;
|
|
case ValueType.String:
|
|
return 3;//3.1現在では文字列はtrue(ただし!"A"などは動かない)
|
|
default:
|
|
//配列はtype mismatch
|
|
throw new TypeMismatch();
|
|
}
|
|
}
|
|
bool isArray()
|
|
{
|
|
return this.type == ValueType.IntegerArray || this.type == ValueType.DoubleArray ||
|
|
this.type == ValueType.StringArray || this.type == ValueType.String;
|
|
}
|
|
bool isNonStringArray()
|
|
{
|
|
return this.type == ValueType.IntegerArray || this.type == ValueType.DoubleArray ||
|
|
this.type == ValueType.StringArray;
|
|
}
|
|
bool isNumberArray()
|
|
{
|
|
return this.type == ValueType.IntegerArray || this.type == ValueType.DoubleArray;
|
|
}
|
|
bool isNumber()
|
|
{
|
|
return this.type == ValueType.Integer || this.type == ValueType.Double;
|
|
}
|
|
bool isInteger()
|
|
{
|
|
return this.type == ValueType.Integer;
|
|
}
|
|
bool isDouble()
|
|
{
|
|
return this.type == ValueType.Double;
|
|
}
|
|
bool isString()
|
|
{
|
|
return this.type == ValueType.String;
|
|
}
|
|
int castInteger()
|
|
{
|
|
if (this.type == ValueType.Integer)
|
|
{
|
|
return this.integerValue;
|
|
}
|
|
if (this.type == ValueType.Double)
|
|
{
|
|
return cast(int)this.doubleValue;
|
|
}
|
|
throw new TypeMismatch();
|
|
}
|
|
double castDouble()
|
|
{
|
|
if (this.type == ValueType.Integer)
|
|
{
|
|
return this.integerValue;
|
|
}
|
|
if (this.type == ValueType.Double)
|
|
{
|
|
return this.doubleValue;
|
|
}
|
|
throw new TypeMismatch();
|
|
}
|
|
Array!wchar castString()
|
|
{
|
|
if (this.type == ValueType.String)
|
|
return this.stringValue;
|
|
throw new TypeMismatch();
|
|
}
|
|
wstring castDString()
|
|
{
|
|
if (this.type == ValueType.String)
|
|
return cast(immutable)this.stringValue.array;
|
|
throw new TypeMismatch();
|
|
}
|
|
string toString()
|
|
{
|
|
import std.conv, std.format;
|
|
switch(this.type)
|
|
{
|
|
case ValueType.Void:
|
|
return "void";
|
|
case ValueType.Integer:
|
|
return this.integerValue.to!string;
|
|
case ValueType.String:
|
|
return this.stringValue.array.to!string;
|
|
case ValueType.IntegerArray:
|
|
return format("integerarray[%s]", length);
|
|
case ValueType.DoubleArray:
|
|
return format("doublearray[%s]", length);
|
|
case ValueType.StringArray:
|
|
return format("doublearray[%s]", length);
|
|
case ValueType.InternalAddress:
|
|
return format("internal address address=0x%x", integerValue);
|
|
case ValueType.InternalSlotAddress:
|
|
return format("internal address slot=%d, address=0x%x", internalAddress.slot, internalAddress.address);
|
|
default:
|
|
return type.to!string;
|
|
}
|
|
}
|
|
@property int length()
|
|
{
|
|
switch(this.type)
|
|
{
|
|
case ValueType.String:
|
|
return cast(int)stringValue.length;
|
|
case ValueType.IntegerArray:
|
|
return cast(int)integerArray.length;
|
|
case ValueType.DoubleArray:
|
|
return cast(int)doubleArray.length;
|
|
case ValueType.StringArray:
|
|
return cast(int)stringArray.length;
|
|
default:
|
|
throw new TypeMismatch();
|
|
}
|
|
}
|
|
@property void length(int len)
|
|
{
|
|
switch(this.type)
|
|
{
|
|
case ValueType.String:
|
|
stringValue.length = len;
|
|
return;
|
|
case ValueType.IntegerArray:
|
|
integerArray.length = len;
|
|
return;
|
|
case ValueType.DoubleArray:
|
|
doubleArray.length = len;
|
|
return;
|
|
case ValueType.StringArray:
|
|
stringArray.length = len;
|
|
return;
|
|
default:
|
|
throw new TypeMismatch();
|
|
}
|
|
}
|
|
@property int dimCount()
|
|
{
|
|
switch(this.type)
|
|
{
|
|
case ValueType.String:
|
|
return stringValue.dimCount;
|
|
case ValueType.IntegerArray:
|
|
return integerArray.dimCount;
|
|
case ValueType.DoubleArray:
|
|
return doubleArray.dimCount;
|
|
case ValueType.StringArray:
|
|
return stringArray.dimCount;
|
|
default:
|
|
throw new TypeMismatch();
|
|
}
|
|
}
|
|
void opIndexAssign(Value v, int i)
|
|
{
|
|
switch(this.type)
|
|
{
|
|
//とりあえず破壊的に書き換えた
|
|
//どのみちこれだと文字数を増やすことができないのでArray!wcharで管理させたい
|
|
case ValueType.String:
|
|
stringValue[i] = v.castString;
|
|
break;
|
|
case ValueType.IntegerArray:
|
|
integerArray.array[i] = v.castInteger;
|
|
break;
|
|
case ValueType.DoubleArray:
|
|
doubleArray.array[i] = v.castDouble;
|
|
break;
|
|
case ValueType.StringArray:
|
|
stringArray.array[i] = v.castString;
|
|
break;
|
|
default:
|
|
throw new TypeMismatch();
|
|
}
|
|
}
|
|
Value opIndex(int i)
|
|
{
|
|
switch(this.type)
|
|
{
|
|
//とりあえず破壊的に書き換えた
|
|
//どのみちこれだと文字数を増やすことができないのでArray!wcharで管理させたい
|
|
case ValueType.String:
|
|
return Value(stringValue[i]);
|
|
case ValueType.IntegerArray:
|
|
return Value(integerArray.array[i]);
|
|
case ValueType.DoubleArray:
|
|
return Value(doubleArray.array[i]);
|
|
case ValueType.StringArray:
|
|
return Value(stringArray.array[i]);
|
|
default:
|
|
throw new TypeMismatch();
|
|
}
|
|
}
|
|
ValueType elementType()
|
|
{
|
|
switch(this.type)
|
|
{
|
|
case ValueType.String:
|
|
return ValueType.String;
|
|
case ValueType.IntegerArray:
|
|
return ValueType.Integer;
|
|
case ValueType.DoubleArray:
|
|
return ValueType.Double;
|
|
case ValueType.StringArray:
|
|
return ValueType.String;
|
|
default:
|
|
throw new TypeMismatch();
|
|
}
|
|
}
|
|
bool canCast(ValueType t)
|
|
{
|
|
return t == this.type || ((t == ValueType.Integer || t == ValueType.Double) && isNumber);
|
|
}
|
|
}
|
|
//import std.experimental.ndslice;
|
|
//ndsliceがこれに相当?
|
|
class Array(T)
|
|
{
|
|
T[] array;
|
|
//最大要素数2^^31
|
|
//4次元配列
|
|
int[4] dim;
|
|
int dimCount;
|
|
this(int len)
|
|
{
|
|
dim[0] = len;
|
|
dim[1] = 0;
|
|
dim[2] = 0;
|
|
dim[3] = 0;
|
|
array = new T[len];
|
|
static if(is(T == double))
|
|
{
|
|
array[] = 0;
|
|
}
|
|
dimCount = 1;
|
|
}
|
|
static if (!is(T == int))
|
|
{
|
|
this(T[] array)
|
|
{
|
|
dim[0] = cast(int)array.length;
|
|
dim[1] = 0;
|
|
dim[2] = 0;
|
|
dim[3] = 0;
|
|
this.array = array;
|
|
dimCount = 1;
|
|
}
|
|
}
|
|
@property size_t length()
|
|
{
|
|
return array.length;
|
|
}
|
|
this(int[] dim)
|
|
{
|
|
int len = 1;
|
|
foreach(int i, j; dim)
|
|
{
|
|
len *= j;
|
|
this.dim[i] = dim[i];
|
|
}
|
|
array = new T[len];
|
|
static if(is(T == double))
|
|
{
|
|
array[] = 0;
|
|
}
|
|
dimCount = cast(int)dim.length;
|
|
}
|
|
T opIndexAssign(T v, int[] dim)
|
|
{
|
|
import core.exception;
|
|
switch(dim.length)
|
|
{
|
|
case 1:
|
|
return this[dim[0]] = v;
|
|
case 2:
|
|
return this[dim[0], dim[1]] = v;
|
|
case 3:
|
|
return this[dim[0], dim[1], dim[2]] = v;
|
|
case 4:
|
|
return this[dim[0], dim[1], dim[2], dim[3]] = v;
|
|
default:
|
|
throw new RangeError();
|
|
}
|
|
}
|
|
ref T opIndex(int[] dim)
|
|
{
|
|
import core.exception;
|
|
switch(dim.length)
|
|
{
|
|
case 1:
|
|
return this[dim[0]];
|
|
case 2:
|
|
return this[dim[0], dim[1]];
|
|
case 3:
|
|
return this[dim[0], dim[1], dim[2]];
|
|
case 4:
|
|
return this[dim[0], dim[1], dim[2], dim[3]];
|
|
default:
|
|
throw new RangeError();
|
|
}
|
|
}
|
|
ref T opIndex(int i1)
|
|
{
|
|
if(dimCount != 1) throw new SyntaxError();
|
|
if(i1 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1];
|
|
}
|
|
ref T opIndex(int i1, int i2)
|
|
{
|
|
if(dimCount != 2) throw new SyntaxError();
|
|
if(i1 >= dim[1] || i2 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1 * dim[0] + i2];
|
|
}
|
|
ref T opIndex(int i1, int i2, int i3)
|
|
{
|
|
if(dimCount != 3) throw new SyntaxError();
|
|
if(i1 >= dim[2] || i2 >= dim[1] || i3 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1 * dim[0] * dim[1] + i2 * dim[0] + i3];
|
|
}
|
|
ref T opIndex(int i1, int i2, int i3, int i4)
|
|
{
|
|
if(dimCount != 4) throw new SyntaxError();
|
|
if(i1 >= dim[3] || i2 >= dim[2] || i3 >= dim[1] || i4 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1 * dim[0] * dim[1] * dim[2] + i2 * dim[0] * dim[1] + i3 * dim[0] + i4];
|
|
}
|
|
T opIndexAssign(T v, int i1)
|
|
{
|
|
if(dimCount != 1) throw new SyntaxError();
|
|
if(i1 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1] = v;
|
|
}
|
|
T opIndexAssign(T v, int i1, int i2)
|
|
{
|
|
if(dimCount != 2) throw new SyntaxError();
|
|
if(i1 >= dim[1] || i2 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1 * dim[0] + i2] = v;
|
|
}
|
|
T opIndexAssign(T v, int i1, int i2, int i3)
|
|
{
|
|
if(dimCount != 3) throw new SyntaxError();
|
|
if(i1 >= dim[2] || i2 >= dim[1] || i3 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1 * dim[0] * dim[1] + i2 * dim[0] + i3] = v;
|
|
}
|
|
T opIndexAssign(T v, int i1, int i2, int i3, int i4)
|
|
{
|
|
if(dimCount != 4) throw new SyntaxError();
|
|
if(i1 >= dim[3] || i2 >= dim[2] || i3 >= dim[1] || i4 >= dim[0]) throw new SubscriptOutOfRange();
|
|
return array[i1 * dim[0] * dim[1] * dim[2] + i2 * dim[0] * dim[1] + i3 * dim[0] + i4] = v;
|
|
}
|
|
void push(T v)
|
|
{
|
|
if (dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
array ~= v;
|
|
dim[0]++;
|
|
}
|
|
void push(Array!T v)
|
|
{
|
|
if (dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
array ~= v.array;
|
|
dim[0] = cast(int)length;
|
|
}
|
|
T pop()
|
|
{
|
|
if (dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
auto last = array[$ - 1];
|
|
array.length--;
|
|
dim[0]--;
|
|
return last;
|
|
}
|
|
void unshift(T v)
|
|
{
|
|
if (dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
array = v ~ array;
|
|
dim[0]++;
|
|
}
|
|
void unshift(Array!T v)
|
|
{
|
|
if (dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
array = v.array ~ array;
|
|
dim[0] = cast(int)length;
|
|
}
|
|
T shift()
|
|
{
|
|
if (dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
auto l = array[0];
|
|
array = array[1..$];
|
|
dim[0]--;
|
|
return l;
|
|
}
|
|
@property void length(int size)
|
|
{
|
|
if (dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
array.length = size;
|
|
static if (is(T == double))
|
|
{
|
|
array[array.length - size..$] = 0;
|
|
}
|
|
dim[0] = size;
|
|
}
|
|
void append(Array!T input)
|
|
{
|
|
if (dimCount != 1 || input.dimCount != 1)
|
|
{
|
|
throw new TypeMismatch();
|
|
}
|
|
array ~= input.array;
|
|
dim[0] = cast(int)array.length;
|
|
}
|
|
void opIndexAssign(Array!T v, int i1)
|
|
{
|
|
if(dimCount != 1 || v.dimCount != 1 ) throw new SyntaxError();
|
|
if(i1 >= dim[0]) throw new SubscriptOutOfRange();
|
|
array = array[0..i1] ~ v.array ~ array[i1 + 1..$];
|
|
dim[0] = cast(int)array.length;
|
|
}
|
|
Array!T opOpAssign(string op = "~")(Array!T v)
|
|
{
|
|
if(dimCount != 1 || v.dimCount != 1 ) throw new SyntaxError();
|
|
array ~= v.array;
|
|
dim[0] = cast(int)array.length;
|
|
return this;
|
|
}
|
|
int calcIndex(int[] index)
|
|
{
|
|
if(dimCount != index.length) throw new SyntaxError();
|
|
import core.exception;
|
|
switch(index.length)
|
|
{
|
|
case 1:
|
|
return index[0];
|
|
case 2:
|
|
return index[0] * dim[0] + index[1];
|
|
case 3:
|
|
return index[0] * dim[0] * dim[1] + index[1] * dim[0] + index[2];
|
|
case 4:
|
|
return index[0] * dim[0] * dim[1] * dim[2] + index[1] * dim[0] * dim[1] + index[2] * dim[0] + index[3];
|
|
default:
|
|
throw new RangeError();
|
|
}
|
|
}
|
|
ArrayReference!T reference(int[] index)
|
|
{
|
|
auto i = calcIndex(index);
|
|
return ArrayReference!T(this, i);
|
|
}
|
|
void insert(Array!T ary, int index)
|
|
{
|
|
if(dimCount != 1 || ary.dimCount != 1 ) throw new SyntaxError();
|
|
if(index >= dim[0]) throw new SubscriptOutOfRange();
|
|
array = array[0..index] ~ ary.array ~ array[index + 1..$];
|
|
dim[0] = cast(int)array.length;
|
|
}
|
|
protected this(Array!T a)
|
|
{
|
|
this.dim = a.dim;
|
|
this.dimCount = a.dimCount;
|
|
this.array = a.array.dup;
|
|
}
|
|
Array!T dup()
|
|
{
|
|
return new Array!T(this);
|
|
}
|
|
public override int opCmp(Object o)
|
|
{
|
|
Array!T array = cast(Array!T)o;
|
|
if (!array)
|
|
return super.opCmp(o);
|
|
return std.algorithm.comparison.cmp(this.array, array.array);
|
|
}
|
|
|
|
}
|
|
struct ArrayReference(T)
|
|
{
|
|
Array!T reference;
|
|
int index;
|
|
T opAssign(T v)
|
|
{
|
|
return reference.array[index] = v;
|
|
}
|
|
static if (is(T == wchar))
|
|
{
|
|
void opAssign(Array!T ary)
|
|
{
|
|
reference.insert(ary, index);
|
|
}
|
|
}
|
|
T opOpAssign(string op)(T v)
|
|
{
|
|
mixin("return reference.array[index]" ~ op ~ "=v;");
|
|
}
|
|
T opBinary(string op)(T v)
|
|
{
|
|
mixin("return reference.array[index]" ~ op ~ "v;");
|
|
}
|
|
T2 opCast(T2)()
|
|
{
|
|
return cast(T2)reference.array[index];
|
|
}
|
|
void swap(T2)(ArrayReference!T2 t2)
|
|
{
|
|
T temp = reference.array[index];
|
|
reference.array[index] = cast(T)t2;
|
|
t2 = cast(T2)temp;
|
|
}
|
|
void swap(T2)(ref T2 t2)
|
|
if (!is(T2 T3 == ArrayReference!T3))
|
|
{
|
|
T temp = reference[index];
|
|
reference[index] = t2;
|
|
t2 = temp;
|
|
}
|
|
}
|