early custom classes

This commit is contained in:
Yoshman29
2023-03-30 17:34:41 +02:00
parent a9c1b360c1
commit 172fdc9f67
6 changed files with 238 additions and 20 deletions
-1
View File
@@ -27,7 +27,6 @@ using StringTools;
@:allow(funkin.desktop.editors.CharacterEditor)
@:allow(funkin.game.StrumLine)
@:allow(funkin.game.PlayState)
@:build(funkin.macros.SoftcodedClassMacro.build())
class Character extends FunkinSprite implements IBeatReceiver implements IOffsetCompatible
{
private var __stunnedTime:Float = 0;
+1
View File
@@ -16,6 +16,7 @@ class ScriptsMacro {
// FOR ABSTRACTS
Compiler.addGlobalMetadata('haxe.xml', '@:build(hscript.UsingHandler.build())');
Compiler.addGlobalMetadata('haxe.CallStack', '@:build(hscript.UsingHandler.build())');
}
}
#end
+115 -11
View File
@@ -1,19 +1,35 @@
package funkin.macros;
using StringTools;
class SoftcodedClassMacro {
#if macro
public static function init() {
trace("cock and balls");
// Compiler.addGlobalMetadata('funkin.game', '@:build(funkin.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('funkin', '@:build(funkin.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('flixel', '@:build(funkin.macros.SoftcodedClassMacro.build())');
}
public static function build():Array<Field> {
var fields = Context.getBuildFields();
var cl = Context.getLocalClass();
var funcs:Array<Field> = [];
if (cl != null) {
var scriptClassType = TPath({
pack: ['funkin', 'scripting'],
name: 'Script',
sub: 'ScriptClass'
});
var funcs:Array<Field> = [];
funcs.push({
pos: Context.currentPos(),
name: '__scriptClass',
kind: FVar(scriptClassType, macro null)
});
var c = cl.get();
trace(c.name);
if (c.name.endsWith('__Softcoded')) return fields;
if (c.params.length > 0) return fields; // NOT SUPPORTED
if (c.isAbstract) return fields;
for(m in c.meta.get()) {
@@ -25,7 +41,19 @@ class SoftcodedClassMacro {
}
}
for(f in fields) {
var shadowClass = macro class {
};
shadowClass.name = '${c.name}__Softcoded';
shadowClass.fields = funcs;
shadowClass.pack = c.pack; // todo
shadowClass.kind = TDClass({
pack: c.pack,
name: c.name
}, [], false, false, false);
for(f in [for(f in fields) f]) {
var valid = true;
if (f.access != null) for(e in f.access) {
switch (e) {
@@ -56,12 +84,60 @@ class SoftcodedClassMacro {
}] : [])
}};
};
var isVoid = fun.ret.match(TPath({name: "Void"}));
if (!isVoid)
funcExpr = macro return ${funcExpr};
var accessors = [];
if (f.access != null) for(f in f.access) accessors.push(f);
accessors.remove(AOverride);
if (f.name != "new") {
if (f.name == "new") {
var constructorExpr = macro {
${{
expr: ECall({
pos: Context.currentPos(),
expr: EConst(CIdent("super"))
}, fun.args != null ? [for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}] : []),
pos: Context.currentPos()
}}
__scriptClass = scrObj;
__scriptClass.call("new", ${{
pos: Context.currentPos(),
expr: EArrayDecl([for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}])
}}, this);
}
var func:Function = {
expr: constructorExpr,
args: [for(a in fun.args) a]
};
func.args.insert(0, {
name: "scrObj",
type: scriptClassType
});
funcs.push({
pos: Context.currentPos(),
name: 'new',
kind: FFun(func),
access: [for(a in accessors) a]
});
var printer = new Printer();
continue;
} else {
var func:Function = {
expr: funcExpr,
args: fun.args
@@ -70,13 +146,13 @@ class SoftcodedClassMacro {
pos: Context.currentPos(),
name: '__super__${f.name}',
kind: FFun(func),
access: accessors
access: [for(a in accessors) a]
});
}
accessors.push(AOverride);
var overrideFuncExpr = macro {
var overrideFuncExpr = isVoid ? macro {
var fieldName = ${{
pos: Context.currentPos(),
expr: EConst(CString(f.name, DoubleQuotes))
@@ -88,7 +164,21 @@ class SoftcodedClassMacro {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}])
}});
}}, this);
} else ${funcExpr}
} : macro {
var fieldName = ${{
pos: Context.currentPos(),
expr: EConst(CString(f.name, DoubleQuotes))
}};
if (__scriptClass != null && __scriptClass.hasField(fieldName) && Reflect.isFunction(__scriptClass.get(fieldName))) {
return __scriptClass.call(fieldName, ${{
pos: Context.currentPos(),
expr: EArrayDecl([for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}])
}}, this);
} else ${funcExpr}
};
@@ -103,14 +193,28 @@ class SoftcodedClassMacro {
access: accessors
});
var printer = new Printer();
trace(printer.printField(funcs[funcs.length - 1]));
default:
}
}
var imports = [for(i in Context.getLocalImports()) i];
imports.push({
path: [for(p in c.module.split(".")) {
pos: Context.currentPos(),
name: p
}],
mode: IAll
});
imports.push({
path: [for(p in c.module.split(".")) {
pos: Context.currentPos(),
name: p
}],
mode: INormal
});
Context.defineModule(c.module + "__Softcoded", [shadowClass], imports);
}
return fields;
}
+54 -7
View File
@@ -1,5 +1,6 @@
package funkin.scripting;
import haxe.io.Path;
import funkin.scripting.Script.ScriptClass;
import hscript.Expr.ClassDecl;
import hscript.Expr.ModuleDecl;
@@ -14,6 +15,7 @@ class HScript extends Script {
public var expr:Expr;
public var decls:Array<ModuleDecl> = null;
public var code:String;
public var folderlessPath:String;
public static function initParser() {
var parser = new Parser();
@@ -29,7 +31,10 @@ class HScript extends Script {
code = Assets.getText(path);
parser = initParser();
folderlessPath = Path.directory(path);
interp.errorHandler = _errorHandler;
interp.importFailedCallback = importFailedCallback;
interp.staticVariables = Script.staticVariables;
interp.allowStaticVariables = interp.allowPublicVariables = true;
@@ -49,6 +54,25 @@ class HScript extends Script {
}
}
private function importFailedCallback(cl:Array<String>):Bool {
var path = cl.join("/");
var scr = Script.create(Paths.script('classes/$path', null, false));
if (!(scr is DummyScript)) {
// script is valid
var cla = scr.getClass(cl.last());
if (cla != null) {
interp.variables.set(cl.last(), Script.createCustomClass(cla));
} else {
interp.variables.set(cl.last(), scr);
}
return true;
}
return false;
}
private function _errorHandler(error:Error) {
var fn = '$fileName:${error.line}: ';
@@ -131,16 +155,18 @@ class HScript extends Script {
if (decls == null) {
decls = parser.parseModule(code, fileName);
}
var imports:Array<Array<String>> = [];
for(d in decls) {
switch(d) {
case DPackage(path):
// ignore
case DImport(path, everything):
// ignore
imports.push(path);
case DClass(c):
if (c.name == name) {
}
trace(c.name);
trace(name);
if (c.name == name)
return new HScriptClass(c, interp, imports, folderlessPath);
case DTypedef(c):
// ignore
}
@@ -221,17 +247,23 @@ class HScriptClass extends ScriptClass {
var interp:Interp;
public function new(decl:ClassDecl, sInterp:Interp) {
public function new(decl:ClassDecl, sInterp:Interp, classesToImport:Array<Array<String>>, forderlessPath:String) {
super();
this.name = decl.name;
switch(decl.extend) {
case CTPath(p, params):
this.classPath = p.join(".");
default:
}
interp = new Interp();
interp.errorHandler = sInterp.errorHandler;
interp.staticVariables = sInterp.staticVariables;
interp.allowStaticVariables = interp.allowPublicVariables = false;
// interp.execute({ e : EBlock(decl.fields), pmin : 0, pmax : 0, origin : fileName, line : 0 });
interp.allowStaticVariables = interp.allowPublicVariables = true;
interp.importFailedCallback = importFailedCallback;
var parser = HScript.initParser();
@@ -246,7 +278,22 @@ class HScriptClass extends ScriptClass {
}
}
public override function hasField(field:String) {
return interp.variables.exists(field);
}
public override function get(field:String) {
return interp.variables[field];
}
public override function set(field:String, v:Dynamic) {
interp.variables[field] = v;
}
public override function onCall(field:String, parameters:Array<Dynamic>, parent:Dynamic) {
interp.scriptObject = parent;
var v = interp.variables[field];
if (v != null && Reflect.isFunction(v))
return (parameters != null && parameters.length > 0) ? Reflect.callMethod(null, v, parameters) : v();
return null;
}
}
+64 -1
View File
@@ -3,6 +3,7 @@ package funkin.scripting;
import lime.app.Application;
import flixel.util.FlxDestroyUtil.IFlxDestroyable;
import haxe.io.Path;
import hscript.IHScriptCustomConstructor;
/**
* Class used for scripting.
@@ -154,6 +155,15 @@ class Script extends FlxBasic implements IFlxDestroyable {
return new DummyScript(path);
}
public static function createCustomClass(s:ScriptClass) {
var t:Class<IScriptClassInstance> = cast Type.resolveClass('${s.classPath}__Softcoded');
if (t == null) {
Logs.trace('Class at ${s.classPath} was not found.', ERROR);
return null;
}
return new ScriptCustomClass(s, t);
}
/**
* Creates a new instance of the script class.
* @param path
@@ -277,11 +287,64 @@ class Script extends FlxBasic implements IFlxDestroyable {
}
class ScriptClass {
public var classPath:String = null;
public var folderlessPath:String = null;
public function new() {}
public function get(field:String):Dynamic {return null;}
public function set(field:String, v:Dynamic) {}
public function call(field:String, ?args:Array<Dynamic>) {return null;}
public function hasField(field:String):Bool {return false;}
/**
* Calls the function `func` defined in the class.
* @param func Name of the function
* @param parameters (Optional) Parameters of the function.
* @return Result (if void, then null)
*/
public function call(func:String, parameters:Array<Dynamic>, parent:Dynamic):Dynamic {
var result = onCall(func, parameters == null ? [] : parameters, parent);
return result;
}
public function onCall(func:String, parameters:Array<Dynamic>, parent:Dynamic):Dynamic {return null;}
private function importFailedCallback(cl:Array<String>):Bool {
var path = cl.join("/");
var scr = Script.create(Paths.script('$folderlessPath$path', null, true));
if (!(scr is DummyScript)) {
// script is valid
var cla = scr.getClass(cl.last());
if (cla != null) {
set(cl.last(), Script.createCustomClass(cla));
} else {
set(cl.last(), scr);
}
return true;
}
return false;
}
}
class ScriptCustomClass implements IHScriptCustomConstructor {
var scrClass:ScriptClass;
var scrType:Class<IScriptClassInstance>;
public function new(scrClass:ScriptClass, scrType:Class<IScriptClassInstance>) {
this.scrClass = scrClass;
this.scrType = scrType;
}
public function hnew(...args:Dynamic):Dynamic {
var a = [for(arg in args) arg];
a.insert(0, scrClass);
return Type.createInstance(scrType, a);
}
}
interface IScriptClassInstance {
}
+4
View File
@@ -21,12 +21,16 @@ import flixel.graphics.frames.FlxFrame;
import flixel.graphics.frames.FlxFramesCollection;
import flash.geom.ColorTransform;
import funkin.chart.Chart;
import haxe.CallStack;
using StringTools;
@:allow(funkin.game.PlayState)
class CoolUtil
{
public static function getLastExceptionStack():String {
return CallStack.toString(CallStack.exceptionStack());
}
/*
* Returns `v` if not null, `defaultValue` otherwise.
* @param v The value