1 /** 2 This module implements the Interpreter class, the main class used by host applications to run scripts 3 4 ──────────────────────────────────────────────────────────────────────────────── 5 6 Copyright (C) 2021 pillager86.rf.gd 7 8 This program is free software: you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation, either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT ANY 14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 15 PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License along with 18 this program. If not, see <https://www.gnu.org/licenses/>. 19 */ 20 module mildew.interpreter; 21 22 import std.typecons; 23 24 import mildew.compiler; 25 import mildew.environment; 26 import mildew.exceptions: ScriptRuntimeException; 27 import mildew.lexer: Token, Lexer; 28 import mildew.nodes; 29 import mildew.parser; 30 import mildew.types; 31 import mildew.vm; 32 33 /** 34 * This is the main interface for the host application to interact with scripts. 35 */ 36 class Interpreter 37 { 38 public: 39 40 /** 41 * Constructs a new Interpreter with a global environment. Note that all calls to evaluate 42 * run in a new environment below the global environment. This allows keywords such as let and const 43 * to not pollute the global namespace. However, scripts can use var to declare variables that 44 * are global. 45 * Params: 46 * printDisasm = If this is set to true, bytecode disassembly of each program will be printed before execution. 47 * printSteps = If this is set to true, detailed step by step execution of bytecode in the VM will be printed. 48 */ 49 this(bool printDisasm = false, bool printSteps = false) 50 { 51 _globalEnvironment = new Environment(this); 52 _compiler = new Compiler(); 53 _vm = new VirtualMachine(_globalEnvironment, printDisasm, printSteps); 54 } 55 56 /** 57 * Initializes the Mildew standard library, such as Object, Math, and console namespaces. This 58 * is optional and is not called by the constructor. For a script to use these methods such as 59 * console.log this must be called first. It is also possible to only call specific 60 * initialize*Library functions and/or force set globals from them to UNDEFINED. 61 */ 62 void initializeStdlib() 63 { 64 import mildew.types.bindings: initializeTypesLibrary; 65 import mildew.stdlib.global: initializeGlobalLibrary; 66 import mildew.stdlib.buffers: initializeBuffersLibrary; 67 import mildew.stdlib.console: initializeConsoleLibrary; 68 import mildew.stdlib.date: initializeDateLibrary; 69 import mildew.stdlib.generator: initializeGeneratorLibrary; 70 import mildew.stdlib.json: initializeJSONLibrary; 71 import mildew.stdlib.map: initializeMapLibrary; 72 import mildew.stdlib.math: initializeMathLibrary; 73 import mildew.stdlib.regexp: initializeRegExpLibrary; 74 import mildew.stdlib.system: initializeSystemLib; 75 import mildew.stdlib.xmlhttprequest: initializeXMLHttpRequestLibrary; 76 initializeTypesLibrary(this); 77 initializeGlobalLibrary(this); 78 initializeBuffersLibrary(this); 79 initializeConsoleLibrary(this); 80 initializeDateLibrary(this); 81 initializeGeneratorLibrary(this); 82 initializeJSONLibrary(this); 83 initializeMapLibrary(this); 84 initializeMathLibrary(this); 85 initializeRegExpLibrary(this); 86 initializeSystemLib(this); 87 initializeXMLHttpRequestLibrary(this); 88 } 89 90 /** 91 * This is the main entry point for evaluating a script program. 92 * Params: 93 * code = This is the source code of a script to be executed. 94 * program = The optional name of the program, defaults to "<program>" 95 * Returns: 96 * If the script has a return statement with an expression, this value will be the result of that expression 97 * otherwise it will be ScriptAny.UNDEFINED 98 */ 99 ScriptAny evaluate(in string code, string name="<program>") 100 { 101 // TODO: evaluate should run all compiled chunks as a function call with module and exports 102 // parameters. 103 auto program = _compiler.compile(code, name); 104 105 return _vm.runProgram(program, []); 106 } 107 108 /** 109 * Evaluates a file that can be either binary bytecode or textual source code. 110 * Params: 111 * pathName = the location of the code file in the file system. 112 * Returns: 113 * The result of evaluating the file, undefined if no return statement. 114 */ 115 ScriptAny evaluateFile(in string pathName) 116 { 117 // TODO if fromScript is true, module and exports parameter that can be checked 118 // and made the return value 119 import std.stdio: File, writefln; 120 import mildew.util.encode: decode; 121 122 File inputFile = File(pathName, "rb"); 123 auto raw = new ubyte[inputFile.size]; 124 if(inputFile.size == 0) 125 return ScriptAny.UNDEFINED; 126 raw = inputFile.rawRead(raw); 127 if(raw.length > 0 && raw[0] == 0x01) 128 { 129 auto program = Program.deserialize(raw, pathName); 130 return _vm.runProgram(program, []); 131 } 132 else 133 { 134 auto source = cast(string)raw; 135 return evaluate(source, pathName); 136 } 137 } 138 139 // TODO: Create an evaluate function with default exception handling with file name info 140 141 /** 142 * Sets a global variable or constant without checking whether or not the variable or const was already 143 * declared. This is used by host applications to define custom functions or objects. 144 * Params: 145 * name = The name of the global variable. 146 * value = The value the variable should be set to. 147 * isConst = Whether or not the script can overwrite the global. 148 */ 149 void forceSetGlobal(T)(in string name, T value, bool isConst=false) 150 { 151 _globalEnvironment.forceSetVarOrConst(name, ScriptAny(value), isConst); 152 } 153 154 /** 155 * Unsets a variable or constant in the global environment. Used by host applications to remove 156 * items that were loaded by the standard library load functions. Specific functions of 157 * script classes can be removed by modifying the "prototype" field of their constructor. 158 * Params: 159 * name = The name of the global variable to unset 160 */ 161 void forceUnsetGlobal(in string name) 162 { 163 _globalEnvironment.forceRemoveVarOrConst(name); 164 } 165 166 /** 167 * Run the VM queued fibers. This API is still a work in progress. 168 */ 169 void runVMFibers() 170 { 171 _vm.runFibersToCompletion(); 172 } 173 174 // TODO function to run one cycle of fibers once VM implements it 175 176 /// Gets the VirtualMachine instance of this Interpreter 177 VirtualMachine vm() { return _vm; } 178 179 private: 180 181 // TODO a registry to keep track of loaded/run files. 182 183 Compiler _compiler; 184 VirtualMachine _vm; 185 Environment _globalEnvironment; 186 } 187