1 /** 2 This module implements script functions that are stored in the global namespace. 3 See https://pillager86.github.io/dmildew/global.html for more information. 4 5 ──────────────────────────────────────────────────────────────────────────────── 6 7 Copyright (C) 2021 pillager86.rf.gd 8 9 This program is free software: you can redistribute it and/or modify it under 10 the terms of the GNU General Public License as published by the Free Software 11 Foundation, either version 3 of the License, or (at your option) any later 12 version. 13 14 This program is distributed in the hope that it will be useful, but WITHOUT ANY 15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 16 PARTICULAR PURPOSE. See the GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 module mildew.stdlib.global; 22 23 import mildew.environment; 24 import mildew.interpreter; 25 import mildew.types; 26 27 /** 28 * This is called by the interpreter's initializeStdlib method to store functions in the global namespace. 29 * Documentation for these functions can be found at https://pillager86.github.io/dmildew/global.html 30 * Params: 31 * interpreter = The Interpreter instance to load the functions into. 32 */ 33 void initializeGlobalLibrary(Interpreter interpreter) 34 { 35 // experimental: runFile 36 interpreter.forceSetGlobal("runFile", new ScriptFunction("runFile", &native_runFile)); 37 interpreter.forceSetGlobal("clearTimeout", new ScriptFunction("clearTimeout", &native_clearTimeout)); 38 interpreter.forceSetGlobal("isdefined", new ScriptFunction("isdefined", &native_isdefined)); 39 interpreter.forceSetGlobal("isFinite", new ScriptFunction("isFinite", &native_isFinite)); 40 interpreter.forceSetGlobal("isNaN", new ScriptFunction("isNaN", &native_isNaN)); 41 interpreter.forceSetGlobal("parseFloat", new ScriptFunction("parseFloat", &native_parseFloat)); 42 interpreter.forceSetGlobal("parseInt", new ScriptFunction("parseInt", &native_parseInt)); 43 interpreter.forceSetGlobal("setTimeout", new ScriptFunction("setTimeout", &native_setTimeout)); 44 } 45 46 // 47 // Global method implementations 48 // 49 50 // experimental DO NOT USE 51 private ScriptAny native_runFile(Environment env, ScriptAny* thisObj, 52 ScriptAny[] args, ref NativeFunctionError nfe) 53 { 54 if(args.length < 1) 55 { 56 nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS; 57 return ScriptAny.UNDEFINED; 58 } 59 auto fileName = args[0].toString(); 60 try 61 { 62 return env.g.interpreter.evaluateFile(fileName, false, true); 63 64 } 65 catch(Exception ex) 66 { 67 nfe = NativeFunctionError.RETURN_VALUE_IS_EXCEPTION; 68 return ScriptAny(ex.msg); 69 } 70 } 71 72 private ScriptAny native_clearTimeout(Environment env, ScriptAny* thisObj, 73 ScriptAny[] args, ref NativeFunctionError nfe) 74 { 75 import mildew.vm.virtualmachine: VirtualMachine; 76 import mildew.vm.fiber: ScriptFiber; 77 78 if(args.length < 1) 79 { 80 nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS; 81 return ScriptAny.UNDEFINED; 82 } 83 auto sfib = args[0].toNativeObject!ScriptFiber; 84 if(sfib is null) 85 { 86 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 87 return ScriptAny.UNDEFINED; 88 } 89 if(sfib.toString() != "Timeout") 90 return ScriptAny(false); 91 return ScriptAny(env.g.interpreter.vm.removeFiber(sfib)); 92 } 93 94 private ScriptAny native_isdefined(Environment env, 95 ScriptAny* thisObj, 96 ScriptAny[] args, 97 ref NativeFunctionError nfe) 98 { 99 if(args.length < 1) 100 return ScriptAny(false); 101 auto varToLookup = args[0].toString(); 102 return ScriptAny(env.variableOrConstExists(varToLookup)); 103 } 104 105 private ScriptAny native_isFinite(Environment env, ScriptAny* thisObj, 106 ScriptAny[] args, ref NativeFunctionError nfe) 107 { 108 import std.math: isFinite; 109 if(args.length < 1) 110 return ScriptAny.UNDEFINED; 111 if(!args[0].isNumber) 112 return ScriptAny.UNDEFINED; 113 immutable value = args[0].toValue!double; 114 return ScriptAny(isFinite(value)); 115 } 116 117 private ScriptAny native_isNaN(Environment env, ScriptAny* thisObj, 118 ScriptAny[] args, ref NativeFunctionError nfe) 119 { 120 import std.math: isNaN; 121 if(args.length < 1) 122 return ScriptAny.UNDEFINED; 123 if(!args[0].isNumber) 124 return ScriptAny(true); 125 immutable value = args[0].toValue!double; 126 return ScriptAny(isNaN(value)); 127 } 128 129 private ScriptAny native_parseFloat(Environment env, ScriptAny* thisObj, 130 ScriptAny[] args, ref NativeFunctionError nfe) 131 { 132 import std.conv: to, ConvException; 133 if(args.length < 1) 134 return ScriptAny(double.nan); 135 auto str = args[0].toString(); 136 try 137 { 138 immutable value = to!double(str); 139 return ScriptAny(value); 140 } 141 catch(ConvException) 142 { 143 return ScriptAny(double.nan); 144 } 145 } 146 147 private ScriptAny native_parseInt(Environment env, ScriptAny* thisObj, 148 ScriptAny[] args, ref NativeFunctionError nfe) 149 { 150 import std.conv: to, ConvException; 151 if(args.length < 1) 152 return ScriptAny.UNDEFINED; 153 auto str = args[0].toString(); 154 immutable radix = args.length > 1 ? args[1].toValue!int : 10; 155 try 156 { 157 immutable value = to!long(str, radix); 158 return ScriptAny(value); 159 } 160 catch(ConvException) 161 { 162 return ScriptAny.UNDEFINED; 163 } 164 } 165 166 // experimental. TODO: subclass Fiber just for the toString and name properties. 167 // TODO setInterval and a cancel function 168 private ScriptAny native_setTimeout(Environment env, ScriptAny* thisObj, 169 ScriptAny[] args, ref NativeFunctionError nfe) 170 { 171 import core.thread: Thread; 172 import std.datetime: dur, Clock; 173 import std.concurrency: yield; 174 import mildew.types.bindings: getLocalThis; 175 176 // all environments are supposed to be linked to the global one. if not, there is a bug 177 auto vm = env.g.interpreter.vm; 178 if(args.length < 2) 179 { 180 nfe = NativeFunctionError.WRONG_NUMBER_OF_ARGS; 181 return ScriptAny.UNDEFINED; 182 } 183 auto func = args[0].toValue!ScriptFunction; 184 if(func is null) 185 { 186 nfe = NativeFunctionError.WRONG_TYPE_OF_ARG; 187 return ScriptAny.UNDEFINED; 188 } 189 auto timeout = args[1].toValue!size_t; 190 args = args[2..$]; 191 bool _; // @suppress(dscanner.suspicious.unmodified) 192 // auto thisToUsePtr = env.lookupVariableOrConst("this", _); 193 auto thisToUse = args.length > 0 ? getLocalThis(env, args[0]) : ScriptAny.UNDEFINED; 194 ScriptFunction funcToAsync = new ScriptFunction("callback", 195 delegate ScriptAny(Environment env, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError) { 196 // Thread.sleep(dur!"msecs"(timeout)); 197 198 immutable start = Clock.currStdTime() / 10_000; 199 long current = start; 200 while(current - start <= timeout) 201 { 202 yield(); 203 current = Clock.currStdTime() / 10_000; 204 } 205 return vm.runFunction(func, thisToUse, args); 206 }); 207 208 // auto fiber = vm.async(funcToAsync, thisToUsePtr? *thisToUsePtr: ScriptAny.UNDEFINED, args_); 209 // auto retVal = new ScriptObject("Timeout", null, fiber); 210 auto retVal = vm.async("Timeout", funcToAsync, thisToUse, args); 211 212 return ScriptAny(retVal); 213 }