diff --git a/hscript/Expr.hx b/hscript/Expr.hx index d84f57d0..1d626598 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -1,244 +1,246 @@ -/* - * Copyright (C)2008-2017 Haxe Foundation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -package hscript; - -typedef Int8 = #if cpp cpp.Int8 #elseif java java.Int8 #elseif cs cs.Int8 #else Int #end; -typedef Int16 = #if cpp cpp.Int16 #elseif java java.Int16 #elseif cs cs.Int16 #else Int #end; -typedef Int32 = #if cpp cpp.Int32 #else Int #end; -typedef Int64 = #if cpp cpp.Int64 #elseif java java.Int64 #elseif cs cs.Int64 #else Int #end; - -typedef UInt8 = #if cpp cpp.UInt8 #elseif cs cs.UInt8 #else Int #end; -typedef UInt16 = #if cpp cpp.UInt16 #elseif cs cs.UInt16 #else Int #end; -typedef UInt32 = #if cpp cpp.UInt32 #else Int #end; -typedef UInt64 = #if cpp cpp.UInt64 #else Int #end; - -enum Const { - CInt( v : Int ); - CFloat( f : Float ); - CString( s : String, ?i : Bool ); -} - -#if hscriptPos -@:structInit -final class Expr { - public var e : ExprDef; - public var pmin : Int; - public var pmax : Int; - public var origin : String; - public var line : Int; -} -enum ExprDef { -#else -typedef ExprDef = Expr; -enum Expr { -#end - EConst( c : Const ); - EIdent( v : String ); - EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess, ?isVar:Bool ); - EParent( e : Expr ); - EBlock( e : Array ); - EField( e : Expr, f : String , ?safe : Bool ); - EBinop( op : String, e1 : Expr, e2 : Expr ); - EUnop( op : String, prefix : Bool, e : Expr ); - ECall( e : Expr, params : Array ); - EIf( cond : Expr, e1 : Expr, ?e2 : Expr ); - EWhile( cond : Expr, e : Expr ); - EFor( v : String, it : Expr, e : Expr, ?ithv: String); - EBreak; - EContinue; - EFunction( args : Array, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool ); - EReturn( ?e : Expr ); - EArray( e : Expr, index : Expr ); - EArrayDecl( e : Array, ?wantedType: CType ); - ENew( cl : String, params : Array, ?paramType:Array ); - EThrow( e : Expr ); - ETry( e : Expr, v : String, t : Null, ecatch : Expr ); - EObject( fl : Array ); - ETernary( cond : Expr, e1 : Expr, e2 : Expr ); - ESwitch( e : Expr, cases : Array, ?defaultExpr : Expr ); - EDoWhile( cond : Expr, e : Expr); - EMeta( name : String, args : Array, e : Expr ); - ECheckType( e : Expr, t : CType ); - - EPackage( ?n:String ); - EImport( c : String, ?asname:String, ?isUsing:Bool ); - EClass( name:String, fields:Array, ?extend:String, interfaces:Array, ?isFinal:Bool, ?isPrivate:Bool ); - EEnum( en:EnumDecl, ?isAbstract:Bool ); - ECast(e:Expr, ?t:CType); - ERegex(e:String, flags:String); -} - -@:structInit -final class ObjectField { - public var name : String; - public var e : Expr; -} - -@:structInit -final class SwitchCase { - public var values : Array; - public var expr : Expr; -} - -@:structInit -final class Argument { - public var name : String; - public var t : CType; - public var opt : Bool; - public var value : Expr; -} - -@:structInit -final class MetadataEntry { - public var name : String; - public var params : Array; -} - -typedef Metadata = Array; - -@:structInit -final class EnumDecl { - public var name : String; - public var fields : Array; -} - -@:structInit -final class EnumField { - public var name : String; - public var args : Array; -} - -enum CType { - CTPath( path : Array, ?params : Array ); - CTFun( args : Array, ret : CType ); - CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> ); - CTParent( t : CType ); - CTOpt( t : CType ); - CTNamed( n : String, t : CType ); - CTExpr( e : Expr ); // for type parameters only -} - -#if hscriptPos -class Error { - public var e : ErrorDef; - public var pmin : Int; - public var pmax : Int; - public var origin : String; - public var line : Int; - public function new(e, pmin, pmax, origin, line) { - this.e = e; - this.pmin = pmin; - this.pmax = pmax; - this.origin = origin; - this.line = line; - } - public function toString(): String { - return Printer.errorToString(this); - } -} -enum ErrorDef { -#else -enum Error { -#end - EInvalidChar( c : Int ); - EUnexpected( s : String ); - EUnterminatedString; - EUnterminatedComment; - EInvalidPreprocessor( msg : String ); - EUnknownVariable( v : String ); - EInvalidIterator( v : String ); - EInvalidOp( op : String ); - EInvalidAccess( f : String ); - ECustom( msg : String ); - EInvalidClass( className : String); - EAlreadyExistingClass( className : String); -} - - -enum ModuleDecl { - DPackage( path : Array ); - DImport( path : Array, ?everything : Bool ); - DClass( c : ClassDecl ); - DTypedef( c : TypeDecl ); -} - -typedef ModuleType = { - var name : String; - var params : {}; // TODO : not yet parsed - var meta : Metadata; - var isPrivate : Bool; -} - -typedef ClassDecl = {> ModuleType, - var extend : Null; - var implement : Array; - var fields : Array; - var isExtern : Bool; -} - -typedef TypeDecl = {> ModuleType, - var t : CType; -} - -typedef FieldDecl = { - var name : String; - var meta : Metadata; - var kind : FieldKind; - var access : Array; -} - -enum abstract FieldAccess(UInt8) { - var APublic; - var APrivate; - var AInline; - var AOverride; - var AStatic; - var AMacro; -} - -enum abstract FieldPropertyAccess(UInt8) { - var ADefault; - var ANull; - var AGet; - var ASet; - var ADynamic; - var ANever; -} - -enum FieldKind { - KFunction( f : FunctionDecl ); - KVar( v : VarDecl ); -} - -@:structInit -final class FunctionDecl { - public var args : Array; - public var body : Expr; - public var ret : Null; -} - -typedef VarDecl = { - var get : Null; - var set : Null; - var expr : Null; - var type : Null; -} +/* + * Copyright (C)2008-2017 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package hscript; + +typedef Int8 = #if cpp cpp.Int8 #elseif java java.Int8 #elseif cs cs.Int8 #else Int #end; +typedef Int16 = #if cpp cpp.Int16 #elseif java java.Int16 #elseif cs cs.Int16 #else Int #end; +typedef Int32 = #if cpp cpp.Int32 #else Int #end; +typedef Int64 = #if cpp cpp.Int64 #elseif java java.Int64 #elseif cs cs.Int64 #else Int #end; + +typedef UInt8 = #if cpp cpp.UInt8 #elseif cs cs.UInt8 #else Int #end; +typedef UInt16 = #if cpp cpp.UInt16 #elseif cs cs.UInt16 #else Int #end; +typedef UInt32 = #if cpp cpp.UInt32 #else Int #end; +typedef UInt64 = #if cpp cpp.UInt64 #else Int #end; + +enum Const { + CInt( v : Int ); + CFloat( f : Float ); + CString( s : String, ?i : Bool ); +} + +#if hscriptPos +@:structInit +final class Expr { + public var e : ExprDef; + public var pmin : Int; + public var pmax : Int; + public var origin : String; + public var line : Int; +} +enum ExprDef { +#else +typedef ExprDef = Expr; +enum Expr { +#end + EConst( c : Const ); + EIdent( v : String ); + EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess, ?isVar:Bool ); + EParent( e : Expr ); + EBlock( e : Array ); + EField( e : Expr, f : String , ?safe : Bool ); + EBinop( op : String, e1 : Expr, e2 : Expr ); + EUnop( op : String, prefix : Bool, e : Expr ); + ECall( e : Expr, params : Array ); + EIf( cond : Expr, e1 : Expr, ?e2 : Expr ); + EWhile( cond : Expr, e : Expr ); + EFor( v : String, it : Expr, e : Expr, ?ithv: String); + EBreak; + EContinue; + EFunction( args : Array, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool ); + EReturn( ?e : Expr ); + EArray( e : Expr, index : Expr ); + EArrayDecl( e : Array, ?wantedType: CType ); + ENew( cl : String, params : Array, ?paramType:Array ); + EThrow( e : Expr ); + ETry( e : Expr, v : String, t : Null, ecatch : Expr ); + EObject( fl : Array ); + ETernary( cond : Expr, e1 : Expr, e2 : Expr ); + ESwitch( e : Expr, cases : Array, ?defaultExpr : Expr ); + EDoWhile( cond : Expr, e : Expr); + EMeta( name : String, args : Array, e : Expr ); + ECheckType( e : Expr, t : CType ); + + EPackage( ?n:String ); + EImport( c : String, ?asname:String, ?isUsing:Bool ); + EClass( name:String, fields:Array, ?extend:String, interfaces:Array, ?isFinal:Bool, ?isPrivate:Bool ); + EEnum( en:EnumDecl, ?isAbstract:Bool ); + ECast(e:Expr, ?t:CType); + ERegex(e:String, flags:String); +} + +@:structInit +final class ObjectField { + public var name : String; + public var e : Expr; +} + +@:structInit +final class SwitchCase { + public var values : Array; + public var expr : Expr; +} + +@:structInit +final class Argument { + public var name : String; + public var t : CType; + public var opt : Bool; + public var value : Expr; +} + +@:structInit +final class MetadataEntry { + public var name : String; + public var params : Array; +} + +typedef Metadata = Array; + +@:structInit +final class EnumDecl { + public var name : String; + public var fields : Array; + public var underlyingType : Null; +} + +@:structInit +final class EnumField { + public var name : String; + public var args : Array; + public var value : Null; +} + +enum CType { + CTPath( path : Array, ?params : Array ); + CTFun( args : Array, ret : CType ); + CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> ); + CTParent( t : CType ); + CTOpt( t : CType ); + CTNamed( n : String, t : CType ); + CTExpr( e : Expr ); // for type parameters only +} + +#if hscriptPos +class Error { + public var e : ErrorDef; + public var pmin : Int; + public var pmax : Int; + public var origin : String; + public var line : Int; + public function new(e, pmin, pmax, origin, line) { + this.e = e; + this.pmin = pmin; + this.pmax = pmax; + this.origin = origin; + this.line = line; + } + public function toString(): String { + return Printer.errorToString(this); + } +} +enum ErrorDef { +#else +enum Error { +#end + EInvalidChar( c : Int ); + EUnexpected( s : String ); + EUnterminatedString; + EUnterminatedComment; + EInvalidPreprocessor( msg : String ); + EUnknownVariable( v : String ); + EInvalidIterator( v : String ); + EInvalidOp( op : String ); + EInvalidAccess( f : String ); + ECustom( msg : String ); + EInvalidClass( className : String); + EAlreadyExistingClass( className : String); +} + + +enum ModuleDecl { + DPackage( path : Array ); + DImport( path : Array, ?everything : Bool ); + DClass( c : ClassDecl ); + DTypedef( c : TypeDecl ); +} + +typedef ModuleType = { + var name : String; + var params : {}; // TODO : not yet parsed + var meta : Metadata; + var isPrivate : Bool; +} + +typedef ClassDecl = {> ModuleType, + var extend : Null; + var implement : Array; + var fields : Array; + var isExtern : Bool; +} + +typedef TypeDecl = {> ModuleType, + var t : CType; +} + +typedef FieldDecl = { + var name : String; + var meta : Metadata; + var kind : FieldKind; + var access : Array; +} + +enum abstract FieldAccess(UInt8) { + var APublic; + var APrivate; + var AInline; + var AOverride; + var AStatic; + var AMacro; +} + +enum abstract FieldPropertyAccess(UInt8) { + var ADefault; + var ANull; + var AGet; + var ASet; + var ADynamic; + var ANever; +} + +enum FieldKind { + KFunction( f : FunctionDecl ); + KVar( v : VarDecl ); +} + +@:structInit +final class FunctionDecl { + public var args : Array; + public var body : Expr; + public var ret : Null; +} + +typedef VarDecl = { + var get : Null; + var set : Null; + var expr : Null; + var type : Null; +} diff --git a/hscript/HEnum.hx b/hscript/HEnum.hx index 3290958b..a34dd2a6 100644 --- a/hscript/HEnum.hx +++ b/hscript/HEnum.hx @@ -2,13 +2,13 @@ package hscript; import hscript.utils.UnsafeReflect; -// TODO: EnumTools for scripted enums /** * Wrapper class for enums, both for real and scripted. + * Use EnumTools and EnumValueTools for enum operations. */ @:structInit class HEnum implements IHScriptCustomBehaviour { - private var enumValues(default, null) = {}; + var enumValues(default, null) = {}; public function setEnum(name:String, enumValue:Dynamic):Void { UnsafeReflect.setField(enumValues, name, enumValue); @@ -20,6 +20,10 @@ class HEnum implements IHScriptCustomBehaviour { return null; } + public function getEnumValues():Dynamic { + return enumValues; + } + public function hget(name:String):Dynamic { return getEnum(name); } @@ -57,10 +61,122 @@ class HEnumValue { if (args.length != other.args.length) return false; - for (i in 0...args.length) // TODO: allow deep comparison, like arrays + for (i in 0...args.length) if (args[i] != other.args[i]) return false; return true; } } + +@:nullSafety +class EnumTools { + public static function getConstructors(e:Dynamic):Array { + if(Std.isOfType(e, HEnum)) { + var henum:HEnum = cast e; + return UnsafeReflect.fields(henum.getEnumValues()); + } + return Type.getEnumConstructs(cast e); + } + + public static function createByName(e:Dynamic, constr:String, ?params:Array):Dynamic { + if(Std.isOfType(e, HEnum)) { + var henum:HEnum = cast e; + var constructor = henum.getEnum(constr); + if(constructor == null) + throw 'Constructor $constr not found in enum'; + if(Std.isOfType(constructor, HEnumValue)) + return constructor; + if(Reflect.isFunction(constructor)) + return constructor(params == null ? [] : params); + throw 'Invalid constructor type'; + } + return Type.createEnum(cast e, constr, params); + } + + public static function createByIndex(e:Dynamic, index:Int, ?params:Array):Dynamic { + if(Std.isOfType(e, HEnum)) { + var constructors = getConstructors(e); + if(index < 0 || index >= constructors.length) + throw 'Index $index out of bounds for enum'; + return createByName(e, constructors[index], params); + } + return Type.createEnumIndex(cast e, index, params); + } +} + +@:nullSafety +class EnumValueTools { + public static function getType(e:Dynamic):Null { + if(Std.isOfType(e, HEnumValue)) { + var hv:HEnumValue = cast e; + return hv.enumName; + } + var en = Type.getEnum(e); + if(en != null) + return Type.getEnumName(en); + return null; + } + + public static function getName(e:Dynamic):String { + if(Std.isOfType(e, HEnumValue)) { + var hv:HEnumValue = cast e; + return hv.fieldName; + } + return Type.enumConstructor(e); + } + + public static function getParameters(e:Dynamic):Array { + if(Std.isOfType(e, HEnumValue)) { + var hv:HEnumValue = cast e; + return hv.args.copy(); + } + return Type.enumParameters(e); + } + + public static function getIndex(e:Dynamic):Int { + if(Std.isOfType(e, HEnumValue)) { + var hv:HEnumValue = cast e; + return hv.index; + } + return Type.enumIndex(e); + } + + public static function equals(a:Dynamic, b:Dynamic):Bool { + if(Std.isOfType(a, HEnumValue) && Std.isOfType(b, HEnumValue)) { + var hva:HEnumValue = cast a; + var hvb:HEnumValue = cast b; + return hva.compare(hvb); + } + return Type.enumEq(a, b); + } + + public static function match(e:Dynamic, pattern:Dynamic):Bool { + if(Std.isOfType(e, HEnumValue)) { + var hv:HEnumValue = cast e; + if(Std.isOfType(pattern, HEnumValue)) { + var hp:HEnumValue = cast pattern; + if(hv.enumName != hp.enumName || hv.fieldName != hp.fieldName) + return false; + if(hp.args.length == 0) + return true; + if(hv.args.length != hp.args.length) + return false; + for(i in 0...hp.args.length) { + var pa = hp.args[i]; + if(pa != null && hv.args[i] != pa) + return false; + } + return true; + } + if(Reflect.isObject(pattern)) { + var patternName = UnsafeReflect.hasField(pattern, "name") ? UnsafeReflect.field(pattern, "name") : null; + if(patternName != null && patternName != hv.fieldName) + return false; + return true; + } + return false; + } + return Type.enumEq(e, pattern); + } +} diff --git a/hscript/Interp.hx b/hscript/Interp.hx index f4fad10c..09fbf7f6 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -687,6 +687,10 @@ class Interp { return UnsafeReflect.getProperty(scriptObject, 'get_$id')(); } } + var cl = Type.resolveClass(id); + if(cl != null) return cl; + var en = Type.resolveEnum(id); + if(en != null) return en; if (doException) error(EUnknownVariable(id)); return null; @@ -845,70 +849,81 @@ 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); + case EEnum(en, isAbstract): + if(isAbstract) { + var enumObj:Dynamic = {}; + var enumName = en.name; + var enumFields = en.fields; + for (i => ef in enumFields) { + var fieldName = ef.name; + var fieldValue = ef.value != null ? exprReturn(ef.value) : i; + UnsafeReflect.setField(enumObj, fieldName, fieldValue); } - 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):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 { + variables.set(enumName, enumObj); + } else { + 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: args + 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):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)); + } + 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); + var f = Reflect.makeVarArgs(f); - enumThingy.setEnum(fieldName, f); + enumThingy.setEnum(fieldName, f); + } } - } - variables.set(en.name, enumThingy); + variables.set(en.name, enumThingy); + } case ERegex(e, f): return new EReg(e, f); case EConst(c): @@ -964,9 +979,33 @@ class Interp { restore(old); return v; case EField(e, f, s): - var field:Null = expr(e); - if(s && field == null) - return null; + var field:Null; + try { + field = expr(e); + } catch(exc:Dynamic) { + var path = getExprPath(e); + if(path != null) { + var fullPath = path + "." + f; + var cl = Type.resolveClass(fullPath); + if(cl != null) return cl; + var en = Type.resolveEnum(fullPath); + if(en != null) return en; + if(s) return null; + error(EUnknownVariable(path)); + } + throw exc; + } + if(field == null) { + var path = getExprPath(e); + if(path != null) { + var fullPath = path + "." + f; + var cl = Type.resolveClass(fullPath); + if(cl != null) return cl; + var en = Type.resolveEnum(fullPath); + if(en != null) return en; + } + if(s) return null; + } return get(field, f); case EBinop(op, e1, e2): var fop = binops.get(op); @@ -1447,6 +1486,20 @@ class Interp { }; } + function getExprPath(e:Expr):Null { + switch(Tools.expr(e)) { + case EIdent(id): + return id; + case EField(e2, f, _): + var parent = getExprPath(e2); + if(parent != null) + return parent + "." + f; + return null; + default: + return null; + } + } + function get(o:Dynamic, f:String):Dynamic { if (o == null) error(EInvalidAccess(f)); diff --git a/hscript/Parser.hx b/hscript/Parser.hx index e0a0cbf6..5391097d 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -1117,38 +1117,62 @@ class Parser { push(tk); mk(EClass(name, fields, extend, interfaces, nextIsFinal, nextIsPrivate), p1); - case "enum": // TODO: enum abstract + case "enum": + var isAbstract = maybe(TId("abstract")); var name = getIdent(); + var underlyingType:CType = null; + + if(isAbstract) { + ensure(TPOpen); + if(allowTypes) { + underlyingType = parseType(); + ensure(TPClose); + } else { + var t = token(); + while(t != TPClose && t != TEof) { + t = token(); + } + } + } ensure(TBrOpen); - // TODO: optimize this var fields:Array = []; var fieldName:String = ''; var enumArgs:Array = null; - //var tk = token(); + var fieldValue:Expr = null; while(!maybe(TBrClose)) { var tk = token(); switch(tk) { - //case TBrClose: - // break; case TSemicolon | TComma: if(fieldName.trim().length == 0) continue; fields.push({ name: fieldName, - args: enumArgs == null ? [] : enumArgs + args: enumArgs == null ? [] : enumArgs, + value: fieldValue }); fieldName = ''; enumArgs = null; + fieldValue = null; case TPOpen: + if(isAbstract) { + error(ECustom("Enum abstract fields cannot have parameters"), tokenMin, tokenMax); + break; + } if(enumArgs != null) { error(ECustom("Cannot have multiple argument lists in one enum constructor"), tokenMin, tokenMax); break; } enumArgs = parseFunctionArgs(true); + case TOp("="): + if(!isAbstract) { + error(ECustom("Only enum abstract fields can have values"), tokenMin, tokenMax); + break; + } + fieldValue = parseExpr(); default: if(fieldName.trim().length != 0) { error(ECustom("Expected comma or semicolon"), tokenMin, tokenMax); @@ -1159,7 +1183,7 @@ class Parser { } } - mk(EEnum({ name: name, fields: fields }, false), p1); + mk(EEnum({ name: name, fields: fields, underlyingType: underlyingType }, isAbstract), p1); case "cast": var tk = token(); var e:Expr = null; diff --git a/hscript/Printer.hx b/hscript/Printer.hx index 34fb765b..b153fddf 100644 --- a/hscript/Printer.hx +++ b/hscript/Printer.hx @@ -148,34 +148,58 @@ class Printer { 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); + case EEnum(en, isAbstract): + if(isAbstract) { + add('enum abstract ${en.name}('); + if(en.underlyingType != null) + type(en.underlyingType); + else + add('Int'); + add(')'); + if(en.fields.length == 0) { + add(' {}'); + return; + } + tabs += "\t"; + add(" {\n"); + for(e in en.fields) { + add(tabs); + add(e.name); + if(e.value != null) { + add(" = "); + expr(e.value); } - add(')'); + add(";\n"); } - add(";\n"); + tabs = tabs.substr(1); + add("}"); + } else { + 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("}"); } - - tabs = tabs.substr(1); - add("}"); case ECast(e, t): var safe = t != null; add("cast ");