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..$]); } }