1
0
Files
otyaSMILEBASIC/SMILEBASIC/builtinfunctions.d

318 lines
9.6 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 otya.smilebasic.error;
import otya.smilebasic.type;
import otya.smilebasic.petitcomputer;
//プチコンの引数省略は特殊なので
//LOCATE ,,0のように省略できる
struct DefaultValue(T, bool skippable = true)
{
T value;
bool isDefault;
this(int v, bool f)
{
value = v;
isDefault = f;
}
this(int 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;
this(BuiltinFunctionArgument[] argments, ValueType result, void function(PetitComputer, Value[], Value[]) func, int startskip)
{
this.argments = argments;
this.result = result;
this.func = func;
this.startskip = startskip;
}
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 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 0;
}
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) display)
{
}
static void BEEP(PetitComputer p, DefaultValue!(int, false) display)
{
}
//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)
{
builtinFunctions[name] = new BuiltinFunction(
GetFunctionParamType!(BuiltinFunction, name),
GetFunctionReturnType!(BuiltinFunction, name),
mixin(AddFunc!(BuiltinFunction, name)),
GetStartSkip!(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
{
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 - 1, 0, 0, ParameterTypeTuple!(__traits(getMember, T, N))) ~ "));}";
}
else static if(is(ReturnType!(__traits(getMember, T, N)) == void))
{
const string AddFunc = "function void(PetitComputer p, Value[] arg, Value[] ret){if(ret.length != 0){throw new IllegalFunctionCall();}" ~ N ~ "(" ~
AddFuncArg!(ParameterTypeTuple!(__traits(getMember, T, N)).length - 1, 0, 0, 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);
}
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] == 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, P...)
{
enum I = L - N;
static if(is(P[0] == double))
{
enum add = 1;
const string arg = "arg[" ~ I.to!string ~ "].castDouble";
}
else static if(is(P[0] == PetitComputer))
{
enum add = 0;
const string arg = "p";
}
else static if(is(P[0] == int))
{
enum add = 1;
const string arg = "arg[" ~ I.to!string ~ "].castInteger";
}
else static if(is(P[0] == wstring))
{
enum add = 1;
const string arg = "arg[" ~ I.to!string ~ "].castString";
}
else static if(is(P[0] == DefaultValue!int))
{
enum add = 1;
const string arg = "fromIntToDefault(arg[" ~ I.to!string ~ "])";
}
else static if(is(P[0] == DefaultValue!(int, false)))
{
enum add = 1;
const string arg = "fromIntToSkip(arg[" ~ I.to!string ~ "])";
}
else
{
enum add = 1;
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, P[1..$]);
}
}