From e28b5a6de541972bb543430953f8865c121df582 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Sun, 15 Jun 2025 23:03:46 -0700 Subject: [PATCH] Property Variables Now property variables fully works. --- hscript/Interp.hx | 104 ++++++++++++++++++++++++++++++++++++-------- hscript/Parser.hx | 7 +-- hscript/Property.hx | 84 +++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+), 21 deletions(-) create mode 100644 hscript/Property.hx diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 0552893..db72627 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -107,7 +107,7 @@ class Interp { public var errorHandler:Error->Void; public var importFailedCallback:Array->Bool; - public var customClasses:Map; + public var customClasses:Map; public var variables:Map; public var publicVariables:Map; public var staticVariables:Map; @@ -145,7 +145,7 @@ class Interp { } private function resetVariables():Void { - customClasses = new Map(); + customClasses = new Map(); variables = new Map(); publicVariables = new Map(); staticVariables = new Map(); @@ -281,8 +281,16 @@ class Interp { setVar(id, v); } } else { + var obj = resolve(id, false, false); + if (obj != null && obj is Property) { + var prop:Property = cast obj; + return prop.callSetter(id, v); + } setVar(id, v); } + } else if (l.r is Property) { + var prop:Property = cast l.r; + return prop.callSetter(id, v); } else { l.r = v; if (l.depth == 0) { @@ -356,11 +364,20 @@ class Interp { setVar(id, v); } } else { + var obj = resolve(id, true, false); + if (obj != null && obj is Property) { + var prop:Property = cast obj; + return prop.callSetter(id, v); + } setVar(id, v); } } else { var l = locals.get(id); + if (l.r is Property) { + var prop:Property = cast l.r; + return prop.callSetter(id, v); + } l.r = v; if (l.depth == 0) { setVar(id, v); @@ -399,19 +416,45 @@ class Interp { var l = locals.get(id); if(l != null) { var v:Dynamic = l.r; + var prop:Property = null; + if (v is Property) { + prop = cast v; + v = prop.callGetter(id); + } + if (prefix) { v += delta; - l.r = v; - } else - l.r = v + delta; + if (prop != null) + prop.callSetter(id, v); + else + l.r = v; + } else { + if (prop != null) + prop.callSetter(id, v + delta); + else + l.r = v + delta; + } return v; } else { - var v:Dynamic = resolve(id); + var v:Dynamic = resolve(id, true, false); + var prop:Property = null; + if (v is Property) { + prop = cast v; + v = prop.callGetter(id); + } + if (prefix) { v += delta; - setVar(id, v); - } else - setVar(id, v + delta); + if (prop != null) + prop.callSetter(id, v); + else + setVar(id, v); + } else { + if (prop != null) + prop.callSetter(id, v + delta); + else + setVar(id, v + delta); + } return v; } case EField(e, f, s): @@ -533,23 +576,37 @@ class Interp { #end } - public function resolve(id:String, doException:Bool = true):Dynamic { + public function resolve(id:String, doException:Bool = true, getProperty:Bool = true):Dynamic { if (id == null) return null; id = StringTools.trim(id); if (locals.exists(id)) { var l = locals.get(id); - if(l != null) - return l.r; + if(l != null) { + if(l.r != null && l.r is Property && getProperty) + return cast(l.r, Property).callGetter(id); + else + return l.r; + } } + var r:Dynamic = null; + if(variables.exists(id)) - return variables.get(id); - if(publicVariables.exists(id)) - return publicVariables.get(id); - if(staticVariables.exists(id)) - return staticVariables.get(id); + r = variables.get(id); + else if(publicVariables.exists(id)) + r = publicVariables.get(id); + else if(staticVariables.exists(id)) + r = staticVariables.get(id); + + if(r != null) { + if(r is Property && getProperty) + return cast(r, Property).callGetter(id); + else + return r; + } + if(customClasses.exists(id)) return customClasses.get(id); @@ -710,8 +767,19 @@ class Interp { return null; } declared.push({n: n, old: locals.get(n), depth: depth}); + var r:Dynamic = (e == null) ? null : expr(e); + var declProp:Property = null; + if (hasGetSet) { + declProp = { + r: r, + getter: getter, + setter: setter, + isVar: isVar, + interp: this, + } + } var declVar:DeclaredVar = { - r: (e == null) ? null : expr(e), + r: (declProp == null) ? r : declProp, depth: depth }; locals.set(n, declVar); diff --git a/hscript/Parser.hx b/hscript/Parser.hx index de7832f..a4e301c 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -832,7 +832,8 @@ class Parser { } } var ident = getIdent(); - var get = ADefault, set = ADefault; + var get:Null = null; + var set:Null = null; var hasGetSet:Bool = false; var oldIsVar:Bool = isVar; @@ -843,7 +844,7 @@ class Parser { var getId = getIdent(); switch (getId) { - case 'default': // Do nothing + case 'default': get = ADefault; case 'get': get = AGet; case 'null': get = ANull; case 'dynamic': get = ADynamic; @@ -853,7 +854,7 @@ class Parser { ensure(TComma); var setId = getIdent(); switch (setId) { - case 'default': // Do nothing + case 'default': set = ADefault; case 'set': set = ASet; case 'null': set = ANull; case 'dynamic': set = ADynamic; diff --git a/hscript/Property.hx b/hscript/Property.hx new file mode 100644 index 0000000..c01d40a --- /dev/null +++ b/hscript/Property.hx @@ -0,0 +1,84 @@ +package hscript; + +import hscript.utils.UnsafeReflect; +import hscript.Interp; +import hscript.Expr.FieldPropertyAccess; + +@:access(hscript.Interp) +@:structInit +class Property { + public var r:Dynamic; + public var getter:FieldPropertyAccess; + public var setter:FieldPropertyAccess; + + var isVar:Bool; + var interp:Interp; + + var __allowReadAccess:Bool = false; + var __allowWriteAccess:Bool = false; + + public function callGetter(name:String) { + switch (getter) { + case AGet | ADynamic: + var fName:String = 'get_$name'; + if (!__allowReadAccess && !interp.isBypassAccessor) { + if (interp.varExists(fName)) { + return callAccessor(fName); + } else + interp.error(ECustom('Method $fName required by property $name is missing')); + } else { + if ((setter == ADefault || setter == ANull) || isVar) + return r; + else + interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}')); + } + case ANever: + interp.error(ECustom('This expression cannot be accessed for reading')); + default: + } + + return r; + } + + public function callSetter(name:String, val:Dynamic) { + switch (setter) { + case ASet | ADynamic: + var fName:String = 'set_$name'; + if (!__allowWriteAccess && !interp.isBypassAccessor) { + if (interp.varExists(fName)) + return callAccessor(fName, [val], true); + else + interp.error(ECustom('Method $fName required by property $name is missing')); + } else { + if ((getter == ADefault || getter == ANull) || isVar) + return r = val; + else + interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}')); + } + case ANever: + interp.error(ECustom('This expression cannot be accessed for writing')); + default: + } + + return r = val; + } + + private function callAccessor(f:String, ?args:Array, isWrite:Bool = false):Dynamic { + var fn = interp.variables.get(f); + var rt:Dynamic = null; + if (fn != null && Reflect.isFunction(fn)) { + if (isWrite) __allowWriteAccess = true; + else __allowReadAccess = true; + + rt = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args); + + if (isWrite) __allowWriteAccess = false; + else __allowReadAccess = false; + + return rt; + } else + interp.error(ECustom('Method $f required by property ${f.substr(3)} is missing')); + + return rt; + } +}