Scripted Enums
This commit is contained in:
@@ -128,6 +128,8 @@ class Bytes {
|
||||
// TODO
|
||||
case EClass(_, _, _, _):
|
||||
// TODO
|
||||
case EEnum(en):
|
||||
// TODO
|
||||
case EConst(c):
|
||||
doEncodeConst(c);
|
||||
case EIdent(v):
|
||||
|
||||
@@ -81,6 +81,7 @@ enum Expr {
|
||||
|
||||
EImport( c : String, ?asname:String, ?isUsing:Bool );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
|
||||
EEnum( en:EnumDecl, ?isAbstract:Bool );
|
||||
}
|
||||
|
||||
@:structInit
|
||||
@@ -111,6 +112,18 @@ final class MetadataEntry {
|
||||
|
||||
typedef Metadata = Array<MetadataEntry>;
|
||||
|
||||
@:structInit
|
||||
final class EnumDecl {
|
||||
public var name : String;
|
||||
public var fields : Array<EnumField>;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
final class EnumField {
|
||||
public var name : String;
|
||||
public var args : Array<Argument>;
|
||||
}
|
||||
|
||||
enum CType {
|
||||
CTPath( path : Array<String>, ?params : Array<CType> );
|
||||
CTFun( args : Array<CType>, ret : CType );
|
||||
|
||||
+56
-16
@@ -2,25 +2,65 @@ package hscript;
|
||||
|
||||
import hscript.utils.UnsafeReflect;
|
||||
|
||||
// TODO: scripted enums
|
||||
// TODO: EnumTools for scripted enums
|
||||
/**
|
||||
* Wrapper class for enums, both for real and scripted.
|
||||
*/
|
||||
@:structInit
|
||||
class HEnum implements IHScriptCustomBehaviour{
|
||||
private var enumValues(default, null) = {};
|
||||
class HEnum implements IHScriptCustomBehaviour {
|
||||
private var enumValues(default, null) = {};
|
||||
|
||||
public function setEnum(name:String, enumValue:Dynamic):Void {
|
||||
UnsafeReflect.setField(enumValues, name, enumValue);
|
||||
}
|
||||
public function setEnum(name:String, enumValue:Dynamic):Void {
|
||||
UnsafeReflect.setField(enumValues, name, enumValue);
|
||||
}
|
||||
|
||||
public function getEnum(name:String):Null<Dynamic> {
|
||||
if(Reflect.hasField(enumValues, name)) return Reflect.field(enumValues, name);
|
||||
return null;
|
||||
}
|
||||
public function getEnum(name:String):Null<Dynamic> {
|
||||
if (UnsafeReflect.hasField(enumValues, name))
|
||||
return UnsafeReflect.field(enumValues, name);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hget(name:String):Dynamic {
|
||||
return getEnum(name);
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
return getEnum(name);
|
||||
}
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
@:structInit
|
||||
class HEnumValue {
|
||||
public var enumName:String;
|
||||
public var fieldName:String;
|
||||
public var index:Int;
|
||||
public var args:Array<Dynamic>;
|
||||
|
||||
public function toString():String {
|
||||
return '$enumName.$fieldName${args.length > 0 ? '(${[for (a in args) a].join(", ")})' : ''}';
|
||||
}
|
||||
|
||||
public inline function getEnumName():String
|
||||
return this.enumName;
|
||||
|
||||
public inline function getConstructorArgs():Array<Dynamic>
|
||||
return this.args;
|
||||
|
||||
public function compare(other:HEnumValue):Bool {
|
||||
if (enumName != other.enumName || fieldName != other.fieldName)
|
||||
return false;
|
||||
if (args.length == 0 && other.args.length == 0)
|
||||
return true;
|
||||
if (args.length == 0 || other.args.length == 0)
|
||||
return false;
|
||||
if (args.length != other.args.length)
|
||||
return false;
|
||||
|
||||
for (i in 0...args.length) // TODO: allow deep comparison, like arrays
|
||||
if (args[i] != other.args[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+111
-4
@@ -28,6 +28,7 @@
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
import hscript.HEnum.HEnumValue;
|
||||
import haxe.CallStack;
|
||||
import hscript.utils.UsingHandler;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
@@ -817,6 +818,70 @@ class Interp {
|
||||
|
||||
return null;
|
||||
|
||||
case EEnum(en, _): // TODO: enum abstracts
|
||||
var enumThingy:HEnum = {};
|
||||
var enumName = en.name;
|
||||
var enumFields = en.fields;
|
||||
for (i => ef in enumFields) {
|
||||
var fieldName = ef.name;
|
||||
|
||||
if(ef.args.length < 1) {
|
||||
var enumValue:HEnumValue = {
|
||||
enumName: enumName,
|
||||
fieldName: fieldName,
|
||||
index: i,
|
||||
args: []
|
||||
}
|
||||
|
||||
enumThingy.setEnum(fieldName, enumValue);
|
||||
}
|
||||
else {
|
||||
var params = ef.args;
|
||||
var hasOpt = false, minParams = 0;
|
||||
for (p in params) {
|
||||
if (p.opt)
|
||||
hasOpt = true;
|
||||
else
|
||||
minParams++;
|
||||
}
|
||||
|
||||
var f = function(args:Array<Dynamic>):HEnumValue {
|
||||
if (((args == null) ? 0 : args.length) != params.length) {
|
||||
if (args.length < minParams) {
|
||||
var str = "Invalid number of parameters. Got " + args.length + ", required " + minParams;
|
||||
if (enumName != null)
|
||||
str += " for enum '" + enumName + "'";
|
||||
error(ECustom(str));
|
||||
}
|
||||
// make sure mandatory args are forced
|
||||
var args2 = [];
|
||||
var extraParams = args.length - minParams;
|
||||
var pos = 0;
|
||||
for (p in params)
|
||||
if (p.opt) {
|
||||
if (extraParams > 0) {
|
||||
args2.push(args[pos++]);
|
||||
extraParams--;
|
||||
} else
|
||||
args2.push(null);
|
||||
} else
|
||||
args2.push(args[pos++]);
|
||||
args = args2;
|
||||
}
|
||||
return {
|
||||
enumName: enumName,
|
||||
fieldName: fieldName,
|
||||
index: i,
|
||||
args: args
|
||||
};
|
||||
};
|
||||
var f = Reflect.makeVarArgs(f);
|
||||
|
||||
enumThingy.setEnum(fieldName, f);
|
||||
}
|
||||
}
|
||||
|
||||
variables.set(en.name, enumThingy);
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
case CInt(v): return v;
|
||||
@@ -1146,14 +1211,55 @@ class Interp {
|
||||
case ETernary(econd, e1, e2):
|
||||
return if (expr(econd) == true) expr(e1) else expr(e2);
|
||||
case ESwitch(e, cases, def):
|
||||
var old = declared.length;
|
||||
var val:Dynamic = expr(e);
|
||||
var match = false;
|
||||
for (c in cases) {
|
||||
for (v in c.values)
|
||||
if (expr(v) == val) {
|
||||
match = true;
|
||||
break;
|
||||
for (v in c.values) {
|
||||
// https://github.com/FunkinCrew/hscript/blob/funkin-dev/hscript/Interp.hx#L531
|
||||
switch (Tools.expr(v)) {
|
||||
case ECall(e, params):
|
||||
switch (Tools.expr(e)) {
|
||||
case EField(_, f):
|
||||
var isScripted:Bool = val is HEnumValue;
|
||||
var valStr:String = '';
|
||||
var valEnum:HEnumValue = null;
|
||||
if(isScripted) {
|
||||
valEnum = cast val;
|
||||
valStr = valEnum.fieldName;
|
||||
}
|
||||
else {
|
||||
valStr = cast val;
|
||||
valStr = valStr.substring(0, valStr.indexOf("("));
|
||||
}
|
||||
|
||||
if(valStr == f) {
|
||||
var valParams = isScripted ? valEnum.getConstructorArgs() : Type.enumParameters(val);
|
||||
for (i => p in params) {
|
||||
switch (Tools.expr(p)) {
|
||||
case EIdent(n):
|
||||
declared.push({
|
||||
n: n,
|
||||
old: {r: locals.get(n), depth: depth},
|
||||
depth: depth
|
||||
});
|
||||
locals.set(n, {r: valParams[i], depth: depth});
|
||||
default:
|
||||
}
|
||||
}
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
default:
|
||||
if (expr(v) == val) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
val = expr(c.expr);
|
||||
break;
|
||||
@@ -1161,6 +1267,7 @@ class Interp {
|
||||
}
|
||||
if (!match)
|
||||
val = def == null ? null : expr(def);
|
||||
restore(old);
|
||||
return val;
|
||||
case EMeta(a, b, e):
|
||||
var oldAccessor = isBypassAccessor;
|
||||
|
||||
+50
-3
@@ -23,6 +23,8 @@ package hscript;
|
||||
import haxe.rtti.Meta;
|
||||
import hscript.Expr;
|
||||
|
||||
using StringTools;
|
||||
|
||||
enum Token {
|
||||
TEof;
|
||||
TConst( c : Const );
|
||||
@@ -362,7 +364,7 @@ class Parser {
|
||||
var p1 = tokenMin;
|
||||
#end
|
||||
switch( tk ) {
|
||||
case TId(id):
|
||||
case TId(id): // TODO: direct access to classes by typing their path
|
||||
var e = parseStructure(id, oldPos);
|
||||
if( e == null )
|
||||
e = mk(EIdent(id));
|
||||
@@ -643,7 +645,7 @@ class Parser {
|
||||
if( semic ) push(TSemicolon);
|
||||
}
|
||||
mk(EIf(cond,e1,e2),p1,(e2 == null) ? tokenMax : pmax(e2));
|
||||
case "override":
|
||||
case "override": // BIG TODO: OPTIMIZE ALL THIS BELOW UNTIL "inline"
|
||||
nextIsOverride = true;
|
||||
var nextToken = token();
|
||||
switch(nextToken) {
|
||||
@@ -1101,6 +1103,50 @@ class Parser {
|
||||
push(tk);
|
||||
mk(EClass(name, fields, extend, interfaces, nextIsFinal, nextIsPrivate), p1);
|
||||
|
||||
case "enum": // TODO: enum abstract
|
||||
var name = getIdent();
|
||||
|
||||
ensure(TBrOpen);
|
||||
|
||||
// TODO: optimize this
|
||||
var fields:Array<EnumField> = [];
|
||||
var fieldName:String = '';
|
||||
var enumArgs:Array<Argument> = null;
|
||||
var tk = token();
|
||||
|
||||
while(!maybe(TBrClose)) {
|
||||
tk = token();
|
||||
|
||||
switch(tk) {
|
||||
//case TBrClose:
|
||||
// break;
|
||||
case TSemicolon | TComma:
|
||||
if(fieldName.trim().length == 0) continue;
|
||||
|
||||
fields.push({
|
||||
name: fieldName,
|
||||
args: enumArgs == null ? [] : enumArgs
|
||||
});
|
||||
fieldName = '';
|
||||
enumArgs = null;
|
||||
case TPOpen:
|
||||
if(enumArgs != null) {
|
||||
error(ECustom("Cannot have multiple argument lists in one enum constructor"), tokenMin, tokenMax);
|
||||
break;
|
||||
}
|
||||
enumArgs = parseFunctionArgs(true);
|
||||
default:
|
||||
if(fieldName.trim().length != 0) {
|
||||
error(ECustom("Expected comma or semicolon"), tokenMin, tokenMax);
|
||||
break;
|
||||
}
|
||||
push(tk);
|
||||
fieldName = getIdent();
|
||||
}
|
||||
}
|
||||
|
||||
mk(EEnum({ name: name, fields: fields }, false), p1);
|
||||
|
||||
case "return":
|
||||
var tk = token();
|
||||
push(tk);
|
||||
@@ -1309,7 +1355,7 @@ class Parser {
|
||||
}
|
||||
}
|
||||
|
||||
function parseFunctionArgs():Array<Argument> {
|
||||
function parseFunctionArgs(?enumArgs:Bool = false):Array<Argument> {
|
||||
var args:Array<Argument> = [];
|
||||
var tk = token();
|
||||
if( tk != TPClose ) {
|
||||
@@ -1335,6 +1381,7 @@ class Parser {
|
||||
if( maybe(TDoubleDot) )
|
||||
arg.t = parseType();
|
||||
if( maybe(TOp("="))) {
|
||||
if(enumArgs) unexpected(TOp("="));
|
||||
arg.value = parseExpr();
|
||||
arg.opt = true;
|
||||
}
|
||||
|
||||
@@ -141,6 +141,34 @@ class Printer {
|
||||
// expr(field);
|
||||
//}
|
||||
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
case EEnum(en, _): // TODO: enum abstracts
|
||||
add('enum ${en.name}');
|
||||
if(en.fields.length == 0) {
|
||||
add(' {}');
|
||||
return;
|
||||
}
|
||||
tabs += "\t";
|
||||
add(" {\n");
|
||||
|
||||
for(e in en.fields) {
|
||||
add(tabs);
|
||||
add(e.name);
|
||||
if(e.args.length > 0) {
|
||||
add("(");
|
||||
var first = true;
|
||||
for( a in e.args ) {
|
||||
if( first ) first = false else add(", ");
|
||||
if( a.opt ) add("?");
|
||||
add(a.name);
|
||||
addType(a.t);
|
||||
}
|
||||
add(')');
|
||||
}
|
||||
add(";\n");
|
||||
}
|
||||
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
case EConst(c):
|
||||
|
||||
+2
-1
@@ -59,7 +59,7 @@ class Tools {
|
||||
if( def != null ) f(def);
|
||||
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
||||
case ECheckType(e,_): f(e);
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ class Tools {
|
||||
case ECheckType(e,t): ECheckType(f(e), t);
|
||||
case EImport(c): EImport(c);
|
||||
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
||||
default: expr(e);
|
||||
}
|
||||
return mk(edef, e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user