Skip to content

Commit 1ef9daa

Browse files
committed
Merge branch 'Archipelago' of https://github.com/Z11Coding/Mixtape-Engine-Rework into Archipelago
2 parents 067385d + 844e7dd commit 1ef9daa

File tree

6 files changed

+410
-14
lines changed

6 files changed

+410
-14
lines changed

Project.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@
242242
<!--Macro fixes-->
243243
<haxeflag name="--macro" value="allowPackage('flash')" />
244244
<haxeflag name="--macro" value="include('my.pack')" />
245+
<haxeflag name="--macro" value="yutautil.RuntimeTypedef.processTypedef()" />
245246

246247
<haxeflag name="--resource" value="assets/embed/apworld/fridaynightfunkin.apworld@apworld" />
247248

source/Main.hx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,16 @@ class Main extends Sprite
8989

9090
public static function main():Void
9191
{
92-
trace(18.sum());
93-
trace([1, 2, 3].sum());
94-
trace([1, 2.3, 3.4].sum());
9592

96-
function testOneOrMore(o:OneOrMore<Float>):Void {
97-
if (o.isSingle()) {
98-
trace("Single value: " + o.getSingle());
99-
} else {
100-
trace("Multiple values: " + o.toArray());
101-
}
93+
try
94+
{
95+
trace(3.forceCast(Type.ValueType.TFloat));
10296
}
103-
testOneOrMore(39.asFloat());
104-
testOneOrMore([1, 2, 3].asFloat());
105-
97+
catch (e:Dynamic)
98+
{
99+
trace("Error: " + e);
100+
}
101+
trace("Finished testing forceCast.");
106102
Lib.current.addChild(new Main());
107103
//Stolen from Psych Online. Thanks for making the next hour of my life not hell.
108104
Lib.current.addChild(new archipelago.console.SideUI());

source/yutautil/CollectionUtils.hx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,95 @@ class CollectionUtils
16141614
return input;
16151615
}
16161616

1617+
public static inline function forceCast<T>(input:Dynamic, ?type:Suggestion<Class<T>>, ?catchError:Bool = false):T
1618+
{
1619+
try {
1620+
if (type == null || Std.is(input, type))
1621+
{
1622+
return cast input;
1623+
}
1624+
else
1625+
{
1626+
"If you get here, this will 100% throw an error.".NativeComment();
1627+
throw "Cannot force cast " + Std.string(input) + " (type: " + (Type.getClass(input) != null ? Type.getClassName(Type.getClass(input)) : Std.string(Type.typeof(input))) + ") to " + (type != null ? Type.getClassName(type) : "unknown type");
1628+
}
1629+
} catch (e:Dynamic) {
1630+
if (catchError) {
1631+
"Because this is an inline function, this will only appear in this C++ file if you actually have enabled the `catchError` parameter.".NativeComment();
1632+
trace('[forceCast] Error caught: ' + Std.string(e) + '. Hope you can handle a null value now.');
1633+
return null;
1634+
} else {
1635+
throw e;
1636+
}
1637+
}
1638+
}
1639+
1640+
// // Version of forceCast which checks basic types like Int, Float, String, etc.
1641+
// public static extern overload inline function forceCast<T>(input:BasicTypes, ?type:Suggestion<Type.ValueType>, ?catchError:Bool = false):T
1642+
// {
1643+
// try {
1644+
// if (type == null || Type.typeof(input) == type)
1645+
// {
1646+
// return switch (type) {
1647+
// case _ if (type == Type.ValueType.TUnknown):
1648+
// // If type is not specified or unknown, try a basic cast
1649+
// try {
1650+
// cast input;
1651+
// } catch (e:Dynamic) {
1652+
// throw "forceCast: Could not cast input to requested type (unknown/null type): " + Std.string(e);
1653+
// }
1654+
// case Type.ValueType.TInt:
1655+
// if (Std.is(input, Int)) cast input;
1656+
// var parsed = Std.parseInt(Std.string(input));
1657+
// if (parsed == null) throw "forceCast: Cannot cast input to Int: " + Std.string(input);
1658+
// cast parsed;
1659+
// case Type.ValueType.TFloat:
1660+
// if (Std.is(input, Float)) cast input;
1661+
// var parsed = Std.parseFloat(Std.string(input));
1662+
// if (Math.isNaN(parsed)) throw "forceCast: Cannot cast input to Float: " + Std.string(input);
1663+
// cast parsed;
1664+
// case Type.ValueType.TBool:
1665+
// if (Std.is(input, Bool)) cast input;
1666+
// var str = Std.string(input).toLowerCase();
1667+
// if (str == "true" || str == "1") true;
1668+
// if (str == "false" || str == "0") false;
1669+
// throw "forceCast: Cannot cast input to Bool: " + Std.string(input);
1670+
// case Type.ValueType.TNull:
1671+
// null;
1672+
// case Type.ValueType.TObject:
1673+
// if (Std.is(input, Dynamic)) input;
1674+
// try {
1675+
// cast input;
1676+
// } catch (e:Dynamic) {
1677+
// throw "forceCast: Cannot cast input to TObject: " + Std.string(e);
1678+
// }
1679+
// case Type.ValueType.TFunction:
1680+
// if (Std.is(input, haxe.Constraints.Function)) return cast input;
1681+
// throw "forceCast: Cannot cast input to Function: " + Std.string(input);
1682+
// case Type.ValueType.TClass(c):
1683+
// if (Std.is(input, c)) cast input;
1684+
// throw "forceCast: Cannot cast input to Class: " + Std.string(input);
1685+
// case Type.ValueType.TEnum(e):
1686+
// if (Std.is(input, e)) cast input;
1687+
// throw "forceCast: Cannot cast input to Enum: " + Std.string(input);
1688+
// default:
1689+
// cast input.forceCast(); // This will throw an error if the type is not handled
1690+
// }
1691+
// }
1692+
// else
1693+
// {
1694+
// throw "Cannot force cast " + Std.string(input) + " (type: " + (Type.getClass(input) != null ? Type.getClassName(Type.getClass(input)) : Std.string(Type.typeof(input))) + ") to " + (type != null ? Type.getClassName(type) : "unknown type");
1695+
// }
1696+
// } catch (e:Dynamic) {
1697+
// if (catchError) {
1698+
// trace('[forceCastBasic] Error caught: ' + Std.string(e) + '. Returning null.');
1699+
// return null;
1700+
// } else {
1701+
// throw e;
1702+
// }
1703+
// }
1704+
// }
1705+
16171706
public static function getInfinity(t:Dynamic, positive:Bool = true):Dynamic {
16181707
if (Std.isOfType(t, Float)) {
16191708
return positive ? Math.POSITIVE_INFINITY : Math.NEGATIVE_INFINITY;

source/yutautil/OneOrMany.hx

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
package yutautil;
22

3+
/**
4+
* OneOrMore is an alias for OneOrMany<T> where T is the type of the items.
5+
* It allows a variable to take either a single item of type T or an array of items of type T.
6+
* This is useful for functions that can accept either a single item or multiple items of the same type.
7+
*/
38
typedef OneOrMore<T> = OneOrMany<T>;
49

10+
/**
11+
* FlexibleNum is an abstract type that can represent either an Int or a Float.
12+
* It provides methods to convert between these types and to create instances from them.
13+
* This is useful for cases where you want to handle both Int and Float values uniformly.
14+
*/
515
abstract FlexibleNum(Float) from Int from Float to Float {
616
public inline function new(value:Float) {
717
this = value;
@@ -28,6 +38,137 @@ abstract FlexibleNum(Float) from Int from Float to Float {
2838
}
2939
}
3040

41+
/**
42+
* Suggestion is a wrapper which allows for suggesting a type for a Dynamic value.
43+
* It is used to provide type hints for Dynamic values, allowing them to be treated as a specific type.
44+
*/
45+
abstract Suggestion<T> (Dynamic) from Dynamic to Dynamic {
46+
public function new(value:Dynamic) {
47+
this = value;
48+
}
49+
}
50+
51+
private enum BasicTypesValue {
52+
TInt(v:Int);
53+
TFloat(v:Float);
54+
TString(v:String);
55+
TBool(v:Bool);
56+
}
57+
58+
/**
59+
* BasicTypes is an abstract type that can hold a value of Int, Float, String, or Bool.
60+
* It provides methods to convert between these types and to create instances from them.
61+
*/
62+
abstract BasicTypes(BasicTypesValue) {
63+
public function new(value:Dynamic) {
64+
if (value == null) {
65+
throw 'BasicTypes cannot be constructed with null value';
66+
}
67+
if (Std.isOfType(value, Int)) {
68+
this = TInt(value);
69+
} else if (Std.isOfType(value, Float)) {
70+
this = TFloat(value);
71+
} else if (Std.isOfType(value, String)) {
72+
this = TString(value);
73+
} else if (Std.isOfType(value, Bool)) {
74+
this = TBool(value);
75+
} else {
76+
throw 'BasicTypes can only be constructed with Int, Float, String, or Bool';
77+
}
78+
}
79+
80+
@:from
81+
public static inline function fromInt(value:Int):BasicTypes {
82+
return new BasicTypes(value);
83+
}
84+
85+
@:from
86+
public static inline function fromFloat(value:Float):BasicTypes {
87+
return new BasicTypes(value);
88+
}
89+
90+
@:from
91+
public static inline function fromString(value:String):BasicTypes {
92+
return new BasicTypes(value);
93+
}
94+
95+
@:from
96+
public static inline function fromBool(value:Bool):BasicTypes {
97+
return new BasicTypes(value);
98+
}
99+
100+
@:to
101+
public inline function toInt():Int {
102+
return switch (this) {
103+
case TInt(v): v;
104+
case TFloat(v): Std.int(v);
105+
case TString(v): Std.parseInt(v);
106+
case TBool(v): v ? 1 : 0;
107+
}
108+
}
109+
@:to
110+
public inline function toFloat():Float {
111+
return switch (this) {
112+
case TInt(v): v;
113+
case TFloat(v): v;
114+
case TString(v): Std.parseFloat(v);
115+
case TBool(v): v ? 1.0 : 0.0;
116+
}
117+
}
118+
@:to
119+
public inline function toString():String {
120+
return switch (this) {
121+
case TInt(v): Std.string(v);
122+
case TFloat(v): Std.string(v);
123+
case TString(v): v;
124+
case TBool(v): Std.string(v);
125+
}
126+
}
127+
@:to
128+
public inline function toBool():Bool {
129+
return switch (this) {
130+
case TInt(v): v != 0;
131+
case TFloat(v): v != 0.0;
132+
case TString(v): v != "" && v != "0" && v != "false";
133+
case TBool(v): v;
134+
}
135+
}
136+
}
137+
138+
/* * SuggestionArray is an alias for SuggestionArray<T> where T is the type of
139+
* the suggestions. It allows for a more concise syntax when working with
140+
* arrays of suggestions.
141+
*/
142+
typedef ArraySuggestion<T> = SuggestionArray<T>;
143+
144+
/*
145+
* SuggestionArray is an abstract type that wraps an Array<Suggestion<T>>.
146+
* It provides methods to convert from/to Array<Suggestion<T>> and to handle
147+
* single or multiple suggestions.
148+
*/
149+
abstract SuggestionArray<T>(Array<Suggestion<T>>) to Array<Suggestion<T>> {
150+
public function new(value:Array<Suggestion<T>>) {
151+
this = value;
152+
}
153+
154+
@:from
155+
public static inline function fromArray<T>(arr:Array<T>):SuggestionArray<T> {
156+
return cast arr;
157+
}
158+
159+
@:to
160+
public inline function toArray():Array<Suggestion<T>> {
161+
return this;
162+
}
163+
}
164+
165+
/**
166+
* OneOrMany is an abstract type that can represent either a single value or an array of values.
167+
* It provides methods to convert between a single value and an array, and to check if it contains
168+
* a single value or multiple values.
169+
* It is useful for cases where you want to handle both single and multiple items uniformly, especially in functions
170+
* that can accept either a single item or an array of items.
171+
*/
31172
abstract OneOrMany<T>(Array<T>) to Array<T> {
32173
// Construct from a single value
33174
public function new(value:T) {
@@ -42,13 +183,13 @@ abstract OneOrMany<T>(Array<T>) to Array<T> {
42183

43184
// Construct from an array
44185
@:from
45-
public static inline function fromArray<T>(arr:Array<T>):OneOrMany<T> {
186+
public static inline function asMore<T>(arr:Array<T>):OneOrMany<T> {
46187
return cast arr;
47188
}
48189

49190
// Construct from a single value (implicit)
50191
@:from
51-
public static inline function fromSingle<T>(value:T):OneOrMany<T> {
192+
public static inline function actAsArray<T>(value:T):OneOrMany<T> {
52193
return new OneOrMany(value);
53194
}
54195

source/yutautil/RuntimeTypedef.hx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package yutautil;
2+
3+
import haxe.macro.Context;
4+
import haxe.macro.Expr;
5+
import haxe.macro.Type;
6+
import haxe.macro.Printer;
7+
8+
class RuntimeTypedef {
9+
public static macro function processTypedef():Array<Field> {
10+
var fields = Context.getBuildFields();
11+
Context.onAfterTyping(function(_modules:Array<haxe.macro.ModuleType>) {
12+
var pos = Context.currentPos();
13+
var typedefE = Context.getLocalType();
14+
15+
switch (typedefE) {
16+
case TType(t, params):
17+
var tdef = t.get();
18+
var meta = tdef.meta.get();
19+
var hasMeta = false;
20+
for (m in meta) {
21+
if (m.name == ":runtime" || m.name == ":typed") {
22+
hasMeta = true;
23+
break;
24+
}
25+
}
26+
if (hasMeta) {
27+
// Generate abstract using a simplified approach
28+
var absName = tdef.name;
29+
var absType = haxe.macro.TypeTools.toComplexType(TType(t, params));
30+
31+
// Create a type alias instead of a complex abstract to avoid type conversion issues
32+
var aliasTypeDef:TypeDefinition = {
33+
pos: pos,
34+
name: absName,
35+
pack: tdef.pack,
36+
params: null,
37+
meta: null,
38+
kind: TDAlias(absType),
39+
fields: [],
40+
doc: null
41+
};
42+
Context.defineType(aliasTypeDef);
43+
44+
// Trace how to access the new abstract
45+
var fullName = (tdef.pack.length > 0 ? tdef.pack.join(".") + "." : "") + absName;
46+
Context.warning('Type alias "$fullName" generated. You can access it as $fullName in your code.', pos);
47+
}
48+
default:
49+
// Not a typedef, do nothing
50+
}
51+
});
52+
return fields;
53+
}
54+
}

0 commit comments

Comments
 (0)