diff --git a/hscript/Expr.hx b/hscript/Expr.hx index dd638c6..1d727f0 100644 --- a/hscript/Expr.hx +++ b/hscript/Expr.hx @@ -233,9 +233,10 @@ enum EnumType { enum abstract FieldPropertyAccess(UInt8) { var ADefault; var ANull; - var ANever; var AGet; var ASet; + var ADynamic; + var ANever; } enum AccessContext { diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 6629bcc..4364d48 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -65,9 +65,10 @@ enum abstract ScriptObjectType(UInt8) { class DeclaredVar { public var r:Dynamic; public var depth:Int; + public var isFinal:Null = null; // TODO: getter/setter for variables - public var getter:FieldPropertyAccess = null; - public var setter:FieldPropertyAccess = null; + public var getter:Null = null; + public var setter:Null = null; } @:structInit @@ -156,6 +157,7 @@ class Interp { var isBypassAccessor:Bool = false; var isPrivateAccess:Bool = false; + //var isVar:Bool = false; public var importEnabled:Bool = true; @@ -379,6 +381,7 @@ class Interp { setVar(id, v); } } else { + // TODO: call "set_" if available l.r = v; if (l.depth == 0) { setVar(id, v); @@ -509,6 +512,7 @@ class Interp { } else { var l = locals.get(id); + // TODO: call "set_" if available l.r = v; if (l.depth == 0) { setVar(id, v); @@ -568,6 +572,7 @@ class Interp { switch (e) { case EIdent(id): var l = locals.get(id); + // TODO: call "set_" if available if(l != null) { var v:Dynamic = l.r; if (prefix) { @@ -725,6 +730,7 @@ class Interp { if (locals.exists(id)) { var l = locals.get(id); + // TODO: call get or set if available. if(l != null) return l.r; } @@ -1024,8 +1030,19 @@ class Interp { case EIdent(id): return resolve(id); case EVar(n, _, e, isPublic, isStatic, _, isFinal, _, getter, setter): + if(depth > 0 && (getter != null || setter != null)) { + error(ECustom("Property Accessor for local variables is not allowed")); + return null; + } declared.push({n: n, old: locals.get(n), depth: depth}); - locals.set(n, {r: (e == null) ? null : expr(e), depth: depth}); + var declVar:DeclaredVar = { + r: (e == null) ? null : expr(e), + depth: depth, + isFinal: isFinal, + getter: getter, + setter: setter + }; + locals.set(n, declVar); if (depth == 0) { if(allowStaticVariables && isStatic == true) { if(!staticVariables.exists(n)) // make it so it only sets it once @@ -1394,6 +1411,7 @@ class Interp { return val; case EMeta(a, b, e): var val:Dynamic = null; + switch(a) { case ":bypassAccessor": var oldAccessor = isBypassAccessor; @@ -1408,6 +1426,9 @@ class Interp { isPrivateAccess = oldPrivate; return val; } + + val = expr(e); + return val; /* var oldAccessor = isBypassAccessor; if(a == ":bypassAccessor") { @@ -1418,7 +1439,6 @@ class Interp { isBypassAccessor = oldAccessor; return val; */ - return val; case ECheckType(e, _): return expr(e); case EUsing(name): diff --git a/hscript/Parser.hx b/hscript/Parser.hx index e1f618b..4cbea9f 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -103,6 +103,8 @@ class Parser { var disableOrOp : Bool = false; + var isVar : Bool = false; + #if hscriptPos var origin : String; var tokenMin : Int; @@ -857,16 +859,20 @@ class Parser { var ident = getIdent(); var get = ADefault, set = ADefault; var hasGetSet:Bool = false; + var tk = token(); - // TODO: throw "Missing ;" if tried to use (get, set) on final - if( tk == TPOpen && id != "final" ) { + if( tk == TPOpen) { + if( id == "final" ) + unexpected(tk); + var getId = getIdent(); switch (getId) { case 'default': // Do nothing case 'get': get = AGet; case 'null': get = ANull; + case 'dynamic': get = ADynamic; case 'never': get = ANever; - default: unexpected(TId(getId)); + default: error(ECustom("Custom property accessor is not supported"), p1, tokenMax); } ensure(TComma); var setId = getIdent(); @@ -874,8 +880,9 @@ class Parser { case 'default': // Do nothing case 'set': set = ASet; case 'null': set = ANull; + case 'dynamic': set = ADynamic; case 'never': set = ANever; - default: unexpected(TId(getId)); + default: error(ECustom("Custom property accessor is not supported"), p1, tokenMax); } ensure(TPClose); @@ -898,8 +905,8 @@ class Parser { else push(tk); - if(hasGetSet && (e == null || (allowTypes && t == null))) - error(ECustom("Property requires type-hint or initialization"), p1, tokenMax); + if(hasGetSet) + checkAccess(get, set, e, t); nextType = null; mk(EVar(ident, t, e, nextIsPublic, nextIsStatic, nextIsPrivate, id == "final", nextIsInline, get, set), p1, (e == null) ? tokenMax : pmax(e)); @@ -1604,6 +1611,24 @@ class Parser { return args; } + function checkAccess(get:FieldPropertyAccess, set:FieldPropertyAccess, ?expr:Expr, ?type:CType) { + #if hscriptPos + var p1 = tokenMin; + #end + switch([get, set]) { + case [AGet, ASet] | [AGet, ANever] | [ANever, ASet]: + if(expr != null && !isVar) + error(ECustom("Attempt to assign on field that is not a real variable"), p1, pmax(expr)); + else if(type == null) + error(ECustom("Property requires type-hint"), p1, tokenMax); + case [ANever, ANever]: + error(ECustom("Unsupported property combination"), p1, (expr == null) ? tokenMax : pmax(expr)); + default: + if(expr == null && type == null) + error(ECustom("Property requires type-hint or initialization"), p1, tokenMax); + } + } + // ------------------------ module ------------------------------- public function parseModule( content : String, ?origin : String = "hscript" ):Array { @@ -1812,15 +1837,19 @@ class Parser { var name = getIdent(); var get = ADefault, set = ADefault; var hasGetSet:Bool = false; - // TODO: throw "Missing ;" if tried to use (get, set) on final - if(maybe(TPOpen) && id != "final") { + + if(maybe(TPOpen)) { + if(id == "final") + unexpected(TPOpen); + var getId = getIdent(); switch(getId) { case 'default': // Do nothing case 'get': get = AGet; case 'null': get = ANull; + case 'dynamic': get = ADynamic; case 'never': get = ANever; - default: unexpected(TId(getId)); + default: error(ECustom("Custom property accessor is not supported"), tokenMin, tokenMax); } ensure(TComma); var setId = getIdent(); @@ -1828,17 +1857,18 @@ class Parser { case 'default': // Do nothing case 'set': set = ASet; case 'null': set = ANull; + case 'dynamic': set = ADynamic; case 'never': set = ANever; - default: unexpected(TId(getId)); + default: error(ECustom("Custom property accessor is not supported"), tokenMin, tokenMax); } ensure(TPClose); hasGetSet = true; } - var type = maybe(TDoubleDot) ? parseType() : null; - var expr = maybe(TOp("=")) ? parseExpr() : null; + var type:Null = maybe(TDoubleDot) ? parseType() : null; + var expr:Null = maybe(TOp("=")) ? parseExpr() : null; - if(hasGetSet && (expr == null || type == null)) - error(ECustom("Property requires type-hint or initialization"), p1, tokenMax); + if(hasGetSet) + checkAccess(get, set, expr, type); if( expr != null ) { if( isBlock(expr) )