1
0
Files
otyaSMILEBASIC/SMILEBASIC/builtinfunctions.d
2015-08-20 22:58:35 +09:00

698 lines
21 KiB
D

module otya.smilebasic.builtinfunctions;
import std.conv;
import std.typecons;
import std.typetuple;
import std.traits;
import std.stdio;
import std.ascii;
import std.range;
import otya.smilebasic.error;
import otya.smilebasic.type;
import otya.smilebasic.petitcomputer;
import otya.smilebasic.sprite;
//プチコンの引数省略は特殊なので
//LOCATE ,,0のように省略できる
struct DefaultValue(T, bool skippable = true)
{
T value;
bool isDefault;
this(T v, bool f)
{
value = v;
isDefault = f;
}
this(T v)
{
value = v;
isDefault = false;
}
this(bool f)
{
isDefault = f;
}
void setDefaultValue(T v)
{
if(isDefault)
value = v;
}
mixin Proxy!value;
}
alias ValueType = otya.smilebasic.type.ValueType;
struct BuiltinFunctionArgument
{
ValueType argType;
bool optionalArg;
bool skipArg;
}
class BuiltinFunction
{
BuiltinFunctionArgument[] argments;
ValueType result;
void function(PetitComputer, Value[], Value[]) func;
int startskip;
bool variadic;
this(BuiltinFunctionArgument[] argments, ValueType result, void function(PetitComputer, Value[], Value[]) func, int startskip,
bool variadic)
{
this.argments = argments;
this.result = result;
this.func = func;
this.startskip = startskip;
this.variadic = variadic;
}
bool hasSkipArgument()
{
return this.startskip != this.argments.length;
}
import std.math;
/*
static pure double ABS(double a)
{
return a < 0 ? -a : a;
}*/
static double function(double) ABS = &abs!double;
static double function(double) SGN = &sgn!double;
static double SIN(double arg1)
{
return sin(arg1);
}
//static ABS = function double(double x) => abs(this.result == ValueType.Double ? 1 : 0);
static void LOCATE(PetitComputer p, DefaultValue!int x, DefaultValue!int y, DefaultValue!(int, false) z)
{
x.setDefaultValue(p.CSRX);
y.setDefaultValue(p.CSRY);
z.setDefaultValue(p.CSRZ);
p.CSRX = cast(int)x;
p.CSRY = cast(int)y;
p.CSRZ = cast(int)z;
}
static void COLOR(PetitComputer p, DefaultValue!int fore, DefaultValue!(int, false) back)
{
fore.setDefaultValue(p.consoleForeColor);
back.setDefaultValue(p.consoleBackColor);
p.consoleForeColor = cast(int)fore;
p.consoleBackColor = cast(int)back;
}
static void VSYNC(PetitComputer p, DefaultValue!int time)
{
time.setDefaultValue(1);
p.vsync(cast(int)time);
}
//TODO:プチコンのCLSには引数の個数制限がない
static void CLS(PetitComputer p/*vaarg*/)
{
p.cls;
}
static void ASSERT__(PetitComputer p, int cond, wstring message)
{
if(!cond)
{
p.printConsole("Assertion failed: ", message, "\n");
}
assert(cond, message.to!string);
}
static int BUTTON(PetitComputer p, DefaultValue!(int, false) mode)
{
return p.button;
}
static void VISIBLE(PetitComputer p, DefaultValue!(int) console, DefaultValue!(int) graphic, DefaultValue!(int) BG, DefaultValue!(int) sprite)
{
}
static void XSCREEN(PetitComputer p, int mode, DefaultValue!(int, false) a, DefaultValue!(int, false) b)
{
}
static void DISPLAY(PetitComputer p, DefaultValue!(int, false) display)
{
}
static void GCLS(PetitComputer p, DefaultValue!(int, false) color)
{
color.setDefaultValue(p.gcolor);
p.gfill(p.useGRP, 0, 0, 511, 511, cast(int)color);
}
static void GPSET(PetitComputer p, int x, int y, DefaultValue!(int, false) color)
{
color.setDefaultValue(p.gcolor);
p.gpset(p.useGRP, x, y, cast(int)color);
}
static void GLINE(PetitComputer p, int x, int y, int x2, int y2, DefaultValue!(int, false) color)
{
color.setDefaultValue(p.gcolor);
p.gline(p.useGRP, x, y, x2, y2, cast(int)color);
}
static void GBOX(PetitComputer p, int x, int y, int x2, int y2, DefaultValue!(int, false) color)
{
color.setDefaultValue(p.gcolor);
p.gbox(p.useGRP, x, y, x2, y2, cast(int)color);
}
static void GFILL(PetitComputer p, int x, int y, int x2, int y2, DefaultValue!(int, false) color)
{
color.setDefaultValue(p.gcolor);
p.gfill(p.useGRP, x, y, x2, y2, cast(int)color);
}
static void GCOLOR(PetitComputer p, int color)
{
p.gcolor = color;
}
static void GPRIO(PetitComputer p, int z)
{
}
static void BGMPLAY(PetitComputer p, int music)
{
}
static void BEEP(PetitComputer p, DefaultValue!(int, false) beep, DefaultValue!(int, false) pitch, DefaultValue!(int, false) volume, DefaultValue!(int, false) pan)
{
}
static void STICK(PetitComputer p, DefaultValue!(int, false) mp, out int x, out int y)
{
//JOYSTICK?
x = 0;
y = 0;
}
static int RGB(int R, int G, int B, DefaultValue!(int, false) _)
{
if(!_.isDefault)
{
//やや強引なオーバーロード
return PetitComputer.RGB(cast(ubyte)R, cast(ubyte)G, cast(ubyte)B, cast(ubyte)_);
}
return PetitComputer.RGB(cast(ubyte)R, cast(ubyte)G, cast(ubyte)B);
}
static int RND(int max)
{
import std.random;
return uniform(0, max - 1);
}
static void DTREAD(DefaultValue!(wstring, false) date, out int Y, out int M, out int D/*W*/)
{
import std.datetime;
auto currentTime = Clock.currTime();
if(date.isDefault)
{
Y = currentTime.year;
M = currentTime.month;
D = currentTime.day;
}
else
{
import std.format;
auto v = date.value;
formattedRead(v, "%d/%d/%d", &Y, &M, &D);
}
}
//hairetuha?
static int LEN(wstring str)
{
return str.length;
}
static double VAL(wstring str)
{
if(str[0..2] == "&H")
{
return str[2..$].to!int(16);
}
double val = str.to!double;
return val;
}
static double FLOOR(double val)
{
return val.floor;
}
static wstring MID(wstring str, int i, int len)
{
//挙動未定
return str[i..i + len];
}
static void SPSET(PetitComputer p, int id, int defno)
{
p.sprite.spset(id, defno);
}
static void SPHIDE(PetitComputer p, int id)
{
p.sprite.sphide(id);
}
static void SPSHOW(PetitComputer p, int id)
{
p.sprite.spshow(id);
}
static void SPOFS(PetitComputer p, int id, int x, int y, DefaultValue!(int, false) z)
{
p.sprite.spofs(id, x, y);
}
static void SPANIM(PetitComputer p, Value[] va_args)
{
writeln("NOTIMPL:SPANIM");
auto args = retro(va_args);
int no = args[0].castInteger;
double[] animdata = new double[args.length - 2];
int i;
foreach(a; args[2..$])
{
animdata[i++] = a.castDouble;
}
if(args[1].isString)
p.sprite.spanim(no, args[1].castString, animdata);
if(args[1].isNumber)
p.sprite.spanim(no, cast(SpriteAnimTarget)(args[1].castInteger), animdata);
}
static void BGMSTOP(PetitComputer p)
{
writeln("NOTIMPL:BGMSTOP");
}
static int BGMCHK(PetitComputer p)
{
writeln("NOTIMPL:BGMCHK");
return false;
}
static int CHKCHR(PetitComputer p, int x, int y)
{
return cast(int)(p.console[y][x].character);
}
static wstring FORMAT(PetitComputer p, Value[] va_args)
{
alias retro!(Value[]) VaArgs;
auto args = retro(va_args);
auto format = args[0].castString;
import std.array : appender;
import std.format;
import std.string;
auto w = appender!wstring();
int j = 1;
for(int i = 0; i < format.length; i++)
{
auto f = format[i];
if(f == '%')
{
int d = indexOf(format, 'D', CaseSensitive.yes);
if(d != -1)
{
auto spec = singleSpec(format[i .. d + 1]);
spec.spec = 'd';
formatValue(w, args[j].castInteger, spec);
j++;
i = d;
continue;
}
d = indexOf(format, 'X', CaseSensitive.yes);
if(d != -1)
{
auto spec = singleSpec(format[i .. d + 1]);
spec.spec = 'x';
formatValue(w, args[j].castDouble, spec);
j++;
i = d;
continue;
}
d = indexOf(format, 'S', CaseSensitive.yes);
if(d != -1)
{
auto spec = singleSpec(format[i .. d + 1]);
spec.spec = 's';
formatValue(w, args[j].castString, spec);
j++;
i = d;
continue;
}
d = indexOf(format, 'F', CaseSensitive.yes);
if(d != -1)
{
auto spec = singleSpec(format[i .. d + 1]);
spec.spec = 'f';
formatValue(w, args[j].castDouble, spec);
j++;
i = d;
continue;
}
}
w ~= f;
}
//プチコン互換FOMA
return cast(immutable)w.data();
}
static wstring CHR(int code)
{
return (cast(wchar)code).to!wstring;
}
static double POW(double a1, double a2)
{
return a1 ^^ a2;
}
static double SQR(double a1)
{
return sqrt(a1);
}
//alias void function(PetitComputer, Value[], Value[]) BuiltinFunc;
static BuiltinFunction[wstring] builtinFunctions;
static this()
{
foreach(name; __traits(derivedMembers, BuiltinFunction))
{
writeln(name);
static if(/*__traits(isStaticFunction, __traits(getMember, BuiltinFunction, name)) && */name[0].isUpper)
{
pragma(msg, AddFunc!(BuiltinFunction, name));
wstring suffix = "";
if(is(ReturnType!(__traits(getMember, BuiltinFunction, name)) == wstring))
{
suffix = "$";
}
builtinFunctions[name ~ suffix] = new BuiltinFunction(
GetFunctionParamType!(BuiltinFunction, name),
GetFunctionReturnType!(BuiltinFunction, name),
mixin(AddFunc!(BuiltinFunction, name)),
GetStartSkip!(BuiltinFunction, name),
IsVariadic!(BuiltinFunction, name),
);
writeln(AddFunc!(BuiltinFunction, name));
}
}
}
}
template GetStartSkip(T, string N)
{
private template SkipSkip(int I, P...)
{
static if(P.length <= I)
{
enum SkipSkip = I - is(P[0] == PetitComputer);
}
else static if(is(P[I] == DefaultValue!(int, false)))
{
enum SkipSkip = I - is(P[0] : PetitComputer);
}
else
{
enum SkipSkip = SkipSkip!(I + 1, P);
}
}
enum GetStartSkip = SkipSkip!(0, ParameterTypeTuple!(__traits(getMember, T, N)));
}
template GetFunctionReturnType(T, string N)
{
static if(is(ReturnType!(__traits(getMember, T, N)) == double))
{
enum GetFunctionReturnType = ValueType.Double;
}
else static if(is(ReturnType!(__traits(getMember, T, N)) == int))
{
enum GetFunctionReturnType = ValueType.Integer;
}
else static if(is(ReturnType!(__traits(getMember, T, N)) == void))
{
enum GetFunctionReturnType = ValueType.Void;
}
else static if(is(ReturnType!(__traits(getMember, T, N)) == wstring))
{
enum GetFunctionReturnType = ValueType.String;
}
else
{
enum GetFunctionReturnType = ValueType.Void;
static assert(false, "Invalid type");
}
}
template AddFunc(T, string N)
{
static if(is(ReturnType!(__traits(getMember, T, N)) == double) || is(ReturnType!(__traits(getMember, T, N)) == int))
{
const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){if(ret.length != 1){throw new IllegalFunctionCall();}ret[0] = Value(" ~ N ~ "(" ~
AddFuncArg!(/*ParameterTypeTuple!(__traits(getMember, T, N)).length*/GetArgumentCount!(T,N) - 1, 0, 0, 0, T, N
, ParameterTypeTuple!(__traits(getMember, T, N))) ~ "));}";
}
else static if(is(ReturnType!(__traits(getMember, T, N)) == void))
{
pragma(msg, GetArgumentCount!(T,N));
const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){/*if(ret.length != 0){throw new IllegalFunctionCall();}*/" ~ OutArgsInit!(T,N) ~ N ~ "(" ~
AddFuncArg!(/*ParameterTypeTuple!(__traits(getMember, T, N)).length*/GetArgumentCount!(T,N) - 1, 0, 0, 0, T, N,
ParameterTypeTuple!(__traits(getMember, T, N))) ~ ");}";
}
else static if(is(ReturnType!(__traits(getMember, T, N)) == wstring))
{
const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){if(ret.length != 1){throw new IllegalFunctionCall();}ret[0] = Value(" ~ N ~ "(" ~
AddFuncArg!(/*ParameterTypeTuple!(__traits(getMember, T, N)).length*/GetArgumentCount!(T,N) - 1, 0, 0, 0, T, N
, ParameterTypeTuple!(__traits(getMember, T, N))) ~ "));}";
}
else
{
const string AddFunc = "";
static assert(false, "Invalid type");
}
}
DefaultValue!int fromIntToDefault(Value v)
{
if(v.isNumber)
return DefaultValue!int(v.castInteger());
else
return DefaultValue!int(true);
}
DefaultValue!(int, false) fromIntToSkip(Value v)
{
if(v.isNumber)
return DefaultValue!(int, false)(v.castInteger());
else
return DefaultValue!(int, false)(true);
}
DefaultValue!wstring fromStringToDefault(Value v)
{
if(v.type == ValueType.String)
return DefaultValue!wstring(v.castString());
else
return DefaultValue!wstring(true);
}
DefaultValue!(wstring, false) fromStringToSkip(Value v)
{
if(v.type == ValueType.String)
return DefaultValue!(wstring, false)(v.castString());
else
return DefaultValue!(wstring, false)(true);
}
template GetFunctionParamType(T, string N)
{
enum GetFunctionParamType = mixin("[" ~ Array!(ParameterTypeTuple!(__traits(getMember, T, N))) ~ "]");
private template Array(P...)
{
static if(P.length == 0)
{
const string arg = "";
enum Array = "";
}
else
{
static if(is(P[0] == double))
{
const string arg = "ValueType.Double, false";
}
else static if(is(P[0] == int))
{
const string arg = "ValueType.Integer, false";
}
else static if(is(P[0] == wstring))
{
const string arg = "ValueType.String, false";
}
else static if(is(P[0] == DefaultValue!int))
{
const string arg = "ValueType.Integer, true";
}
else static if(is(P[0] == DefaultValue!(int, false)))
{
const string arg = "ValueType.Integer, true";
}
else static if(is(P[0] == DefaultValue!(wstring)))
{
const string arg = "ValueType.String, false";
}
else static if(is(P[0] == DefaultValue!(wstring, false)))
{
const string arg = "ValueType.String, true";
}
else static if(is(P[0] == Value[]))
{
const string arg = "";
}
else static if(is(P[0] == PetitComputer))
{
static if(P.length != 0)
{
enum Array = Array!(P[1..$]);
}
else
{
enum Array = "";
}
}
static if(1 == P.length && !is(P[0] == PetitComputer))
{
enum Array = "BuiltinFunctionArgument(" ~ arg ~ ")";
}
else static if(!is(P[0] == PetitComputer))
{
enum Array = "BuiltinFunctionArgument(" ~ arg ~ ")," ~ Array!(P[1..$]);
}
}
}
}
template AddFuncArg(int L, int N, int M, int O, T, string NAME, P...)
{
enum I = L - N;
enum storage = ParameterStorageClassTuple!(__traits(getMember, T, NAME))[M];
static if(is(P[0] == double))
{
enum add = 1;
enum outadd = 0;
const string arg = "arg[" ~ I.to!string ~ "].castDouble";
}
else static if(is(P[0] == PetitComputer))
{
enum add = 0;
enum outadd = 0;
const string arg = "p";
}
else static if(is(P[0] == int))
{
static if(storage & ParameterStorageClass.out_)
{
enum add = 0;
enum outadd = 1;
const string arg = "ret[" ~ O.to!string ~ "].integerValue";
}
else
{
enum add = 1;
enum outadd = 0;
const string arg = "arg[" ~ I.to!string ~ "].castInteger";
}
}
else static if(is(P[0] == wstring))
{
enum add = 1;
enum outadd = 0;
const string arg = "arg[" ~ I.to!string ~ "].castString";
}
else static if(is(P[0] == DefaultValue!int))
{
enum add = 1;
enum outadd = 0;
const string arg = "fromIntToDefault(arg[" ~ I.to!string ~ "])";
}
else static if(is(P[0] == DefaultValue!(int, false)))
{
enum add = 1;
enum outadd = 0;
const string arg = "fromIntToSkip(arg[" ~ I.to!string ~ "])";
}
else static if(is(P[0] == DefaultValue!wstring))
{
enum add = 1;
enum outadd = 0;
const string arg = "fromStringToDefault(arg[" ~ I.to!string ~ "])";
}
else static if(is(P[0] == DefaultValue!(wstring, false)))
{
enum add = 1;
enum outadd = 0;
const string arg = "fromStringToSkip(arg[" ~ I.to!string ~ "])";
}
else static if(is(P[0] == Value[]))
{
const string arg = "arg";
}
else
{
enum add = 1;
enum outadd = 0;
static assert(false, "Invalid type");
const string arg = "";
}
static if(1 == P.length)
{
const string AddFuncArg = arg;
}
else
{
const string AddFuncArg = arg ~ ", " ~ AddFuncArg!(L - !add, N + add, M + 1, O + outadd, T, NAME, P[1..$]);
}
}
template OutArgsInit(T, string N, int I = 0, int J = 0)
{
enum tuple = ParameterStorageClassTuple!(__traits(getMember, T, N))[I];
alias param = ParameterTypeTuple!(__traits(getMember, T, N));
static if(!param.length)
{
enum OutArgsInit = "";
}
else
{
static if(tuple & ParameterStorageClass.out_)
{
enum add = 1;
enum ret1 = "ret[" ~ J.to!string ~ "].type = ";
static if(is(param[I] == int))
{
enum ret2 = "ValueType.Integer;";
}
enum result = ret1 ~ ret2;
}
else
{
enum add = 0;
enum result = "";
}
static if(param.length > I + 1)
{
enum OutArgsInit = result ~ OutArgsInit!(T, N, I + 1, J + add);
}
else
{
enum OutArgsInit = result;
}
}
}
template GetArgumentCount(T, string N, int I = 0)
{
enum tuple = ParameterStorageClassTuple!(__traits(getMember, T, N))[I];
alias param = ParameterTypeTuple!(__traits(getMember, T, N));
static if(is(param[I] == PetitComputer))
{
enum add = 0 + 1;
}
else
{
static if(tuple & ParameterStorageClass.out_)
{
enum add = 0;
}
else
{
enum add = 1;
}
}
static if(param.length > I + 1)
{
enum GetArgumentCount = add + GetArgumentCount!(T, N, I + 1);
}
else
{
enum GetArgumentCount = add;
}
}
template IsVariadic(T, string N, int I = 0)
{
alias param = ParameterTypeTuple!(__traits(getMember, T, N));
static if(param.length == 0 || param.length <= I)
{
enum IsVariadic = false;
}
else static if(is(param[I] == Value[]))
{
enum IsVariadic = true;
}
else
{
enum IsVariadic = IsVariadic!(T, N, I + 1);
}
}