Files
CodenameEngine/source/funkin/backend/scripting/HScript.hx
T

185 lines
4.8 KiB
Haxe

package funkin.backend.scripting;
import hscript.*;
import hscript.Expr.Error;
import hscript.Parser;
import openfl.Assets;
class HScript extends Script {
public var interp:Interp;
public var parser:Parser;
public var expr:Expr;
public var code:String = null;
//public var folderlessPath:String;
var __importedPaths:Array<String>;
public static function initParser() {
var parser = new Parser();
parser.allowJSON = parser.allowMetadata = parser.allowTypes = true;
parser.preprocesorValues = Script.getDefaultPreprocessors();
return parser;
}
public override function onCreate(path:String) {
super.onCreate(path);
interp = new Interp();
try {
if(Assets.exists(rawPath)) code = Assets.getText(rawPath);
} catch(e) Logs.error('Error while reading $path: ${Std.string(e)}');
parser = initParser();
//folderlessPath = Path.directory(path);
__importedPaths = [path];
interp.errorHandler = _errorHandler;
interp.importFailedCallback = importFailedCallback;
interp.staticVariables = Script.staticVariables;
interp.allowStaticVariables = interp.allowPublicVariables = true;
interp.variables.set("trace", Reflect.makeVarArgs((args) -> {
var v:String = Std.string(args.shift());
for (a in args) v += ", " + Std.string(a);
this.trace(v);
}));
#if GLOBAL_SCRIPT
funkin.backend.scripting.GlobalScript.call("onScriptCreated", [this, "hscript"]);
#end
loadFromString(code);
}
public override function loadFromString(code:String) {
try {
if (code != null && code.trim() != "")
expr = parser.parseString(code, fileName);
} catch(e:Error) {
_errorHandler(e);
} catch(e) {
_errorHandler(new Error(ECustom(e.toString()), 0, 0, fileName, 0));
}
return this;
}
private function importFailedCallback(cl:Array<String>):Bool {
if(_importFailedCallback(cl, "source/") || _importFailedCallback(cl, "")) {
return true;
}
return false;
}
private function _importFailedCallback(cl:Array<String>, prefix:String):Bool {
var assetsPath = 'assets/$prefix${cl.join("/")}';
for(hxExt in ["hx", "hscript", "hsc", "hxs"]) {
var p = '$assetsPath.$hxExt';
if (__importedPaths.contains(p))
return true; // no need to reimport again
if (Assets.exists(p)) {
var code = Assets.getText(p);
var expr:Expr = null;
try {
if (code != null && code.trim() != "") {
parser.line = 1; // fun fact: this is all you need to reuse a parser without issues. all the other vars get reset on parse.
expr = parser.parseString(code, cl.join("/") + "." + hxExt);
}
} catch(e:Error) {
_errorHandler(e);
} catch(e) {
_errorHandler(new Error(ECustom(e.toString()), 0, 0, fileName, 0));
}
if (expr != null) {
@:privateAccess
interp.exprReturn(expr);
__importedPaths.push(p);
}
return true;
}
}
return false;
}
private function _errorHandler(error:Error) {
var fileName = error.origin;
if(remappedNames.exists(fileName))
fileName = remappedNames.get(fileName);
var fn = '$fileName:${error.line}: ';
var err = error.toString();
if (err.startsWith(fn)) err = err.substr(fn.length);
Logs.traceColored([
Logs.logText(fn, GREEN),
Logs.logText(err, RED)
], ERROR);
}
public override function setParent(parent:Dynamic) {
interp.scriptObject = parent;
}
public override function onLoad() {
@:privateAccess
interp.execute(parser.mk(EBlock([]), 0, 0));
if (expr != null) {
interp.execute(expr);
call("new", []);
}
}
public override function reload() {
// save variables
interp.allowStaticVariables = interp.allowPublicVariables = false;
var savedVariables:Map<String, Dynamic> = [];
for(k=>e in interp.variables) {
if (!Reflect.isFunction(e)) {
savedVariables[k] = e;
}
}
var oldParent = interp.scriptObject;
onCreate(path);
for(k=>e in Script.getDefaultVariables(this))
set(k, e);
load();
setParent(oldParent);
for(k=>e in savedVariables)
interp.variables.set(k, e);
interp.allowStaticVariables = interp.allowPublicVariables = true;
}
private override function onCall(funcName:String, parameters:Array<Dynamic>):Dynamic {
if (interp == null) return null;
if (!interp.variables.exists(funcName)) return null;
var func = interp.variables.get(funcName);
if (func != null && Reflect.isFunction(func))
return Reflect.callMethod(null, func, parameters);
return null;
}
public override function get(val:String):Dynamic {
return interp.variables.get(val);
}
public override function set(val:String, value:Dynamic) {
interp.variables.set(val, value);
}
public override function trace(v:Dynamic) {
var posInfo = interp.posInfos();
Logs.traceColored([
Logs.logText('${fileName}:${posInfo.lineNumber}: ', GREEN),
Logs.logText(Std.isOfType(v, String) ? v : Std.string(v))
], TRACE);
}
public override function setPublicMap(map:Map<String, Dynamic>) {
this.interp.publicVariables = map;
}
}