From 0c36eb9d0cdee873d78ef3b92323e94e987fe332 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Thu, 25 Dec 2025 15:10:43 +0500 Subject: [PATCH 01/16] some fixes with new hscript --- rulescript/interps/RuleScriptInterp.hx | 2 +- rulescript/parsers/HxParser.hx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index fc340dd..5ee6050 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -592,7 +592,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp return prop; } - function resolveType(path:String):Dynamic + override function resolveType(path:String):Dynamic { if (context != null) return context.resolveType(path) diff --git a/rulescript/parsers/HxParser.hx b/rulescript/parsers/HxParser.hx index 255cd72..b501824 100644 --- a/rulescript/parsers/HxParser.hx +++ b/rulescript/parsers/HxParser.hx @@ -1644,6 +1644,7 @@ class HScriptParser extends hscript.Parser case TBrClose: "}"; case TDot: "."; case TQuestionDot: "?."; + case TQuestionDouble: "??"; case TComma: ","; case TSemicolon: ";"; case TBkOpen: "["; From ad81cac3d2ed7ff4bc85d04948bfd9d0d324ed6b Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Thu, 25 Dec 2025 15:13:58 +0500 Subject: [PATCH 02/16] bomp --- haxelib.json | 2 +- rulescript/interps/RuleScriptInterp.hx | 2 +- rulescript/parsers/HxParser.hx | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/haxelib.json b/haxelib.json index 7f935b8..54c1adf 100644 --- a/haxelib.json +++ b/haxelib.json @@ -6,7 +6,7 @@ "version": "0.3.0-alpha", "releasenote": "RuleScripted Classes, Script Properties, Rest and more.", "dependencies": { - "hscript":"" + "hscript": "" }, "contributors": ["Kriptel"] } \ No newline at end of file diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index 5ee6050..3de0950 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -592,7 +592,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp return prop; } - override function resolveType(path:String):Dynamic + #if rulescript_is_git_hscript override #end function resolveType(path:String):Dynamic { if (context != null) return context.resolveType(path) diff --git a/rulescript/parsers/HxParser.hx b/rulescript/parsers/HxParser.hx index b501824..25e446a 100644 --- a/rulescript/parsers/HxParser.hx +++ b/rulescript/parsers/HxParser.hx @@ -1644,7 +1644,9 @@ class HScriptParser extends hscript.Parser case TBrClose: "}"; case TDot: "."; case TQuestionDot: "?."; + #if rulescript_is_git_hscript case TQuestionDouble: "??"; + #end case TComma: ","; case TSemicolon: ";"; case TBkOpen: "["; From 2e0c4bea32fbfe487a28b8219a57ba8dabb9bf9d Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Thu, 25 Dec 2025 21:38:16 +0500 Subject: [PATCH 03/16] html5 fix --- rulescript/interps/RuleScriptInterp.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index 3de0950..c0a1cfc 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -787,8 +787,8 @@ class RuleScriptInterp extends hscript.Interp implements IInterp { obj: en, - hasEnumConstructor: enConstructs.contains, - enumHasParams: e -> !simpleEnums.contains(e), + hasEnumConstructor: function(v) return enConstructs.contains(v), + enumHasParams: function(e) return !simpleEnums.contains(e), getEnumConstructor: function(f:String):Dynamic { return if (simpleEnums.contains(f)) From 0fc57348873eb07392aae710bf3148237699b147 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:41:54 +0500 Subject: [PATCH 04/16] hscript git moment --- rulescript/parsers/HxParser.hx | 3 --- 1 file changed, 3 deletions(-) diff --git a/rulescript/parsers/HxParser.hx b/rulescript/parsers/HxParser.hx index 25e446a..255cd72 100644 --- a/rulescript/parsers/HxParser.hx +++ b/rulescript/parsers/HxParser.hx @@ -1644,9 +1644,6 @@ class HScriptParser extends hscript.Parser case TBrClose: "}"; case TDot: "."; case TQuestionDot: "?."; - #if rulescript_is_git_hscript - case TQuestionDouble: "??"; - #end case TComma: ","; case TSemicolon: ";"; case TBkOpen: "["; From daa556bac1419699cd53bdf68c03b549f35ded2a Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:08:07 +0500 Subject: [PATCH 05/16] better abstract compile macro --- rulescript/macro/AbstractMacro.hx | 322 +++++++++++++++++++++++++++++- 1 file changed, 316 insertions(+), 6 deletions(-) diff --git a/rulescript/macro/AbstractMacro.hx b/rulescript/macro/AbstractMacro.hx index 154cc7f..e9b6eb3 100644 --- a/rulescript/macro/AbstractMacro.hx +++ b/rulescript/macro/AbstractMacro.hx @@ -26,8 +26,7 @@ class AbstractMacro if (FileSystem.exists(dir + name)) for (abs in parseFile(File.getContent(dir + name))) { - if (abs.startsWith('#') || abs.startsWith('//') || abs.length == 0) // Comment - {} + if (abs.startsWith('#') || abs.startsWith('//') || abs.length == 0) {} // Comment else if (abs.startsWith('-#')) // Ignore ignoreList.push(abs); else if (!abstractsList.contains(abs)) // Add @@ -55,8 +54,319 @@ class AbstractMacro static function parseFile(content:String):Array { - final text:String = content.replace('\r', '').replace(' ', ''); - return text.split('\n'); + final lines = content.replace('\r', '').split('\n'); + var active = true; + var result:Array = []; + var conditionMet = false; + var stack:Array<{active:Bool, conditionMet:Bool}> = []; + + for (line in lines) + { + var trimmed = line.trim(); + + if (trimmed.startsWith('#if ')) + { + stack.push({active: active, conditionMet: conditionMet}); + var condition = trimmed.substr(4).trim(); + active = evaluateCondition(condition) && active; + conditionMet = active; + } + else if (trimmed.startsWith('#elseif ')) + { + if (conditionMet) + { + active = false; + } + else + { + var condition = trimmed.substr(8).trim(); + active = evaluateCondition(condition) && stack[stack.length - 1].active; + conditionMet = active; + } + } + else if (trimmed == '#else') + { + active = stack[stack.length - 1].active && !conditionMet; + conditionMet = true; + } + else if (trimmed == '#end') + { + var state = stack.pop(); + if (state != null) + { + active = state.active; + conditionMet = state.conditionMet; + } + } + else if (active && trimmed.length > 0) + { + if (trimmed.startsWith('-#')) + result.push(trimmed); + else if (!trimmed.startsWith('//')) + result.push(trimmed.replace(' ', '')); + } + } + + return result; + } + + static function evaluateCondition(condition:String):Bool + { + var tokens = tokenize(condition); + var pos = 0; + + function peek():{type:String, value:String} + { + return tokens[pos]; + } + + function consume(type:String, ?value:String):{type:String, value:String} + { + var token = peek(); + if (token == null) + Context.error('Unexpected end of condition: $condition', Context.currentPos()); + if (token.type != type) + Context.error('Expected token of type $type, but got ${token.type} in condition: $condition', Context.currentPos()); + if (value != null && token.value != value) + Context.error('Expected token with value $value, but got ${token.value} in condition: $condition', Context.currentPos()); + pos++; + return token; + } + + var parseExpression:Void->Bool = null; + var parseOr:Void->Bool = null; + var parseAnd:Void->Bool = null; + var parseComparison:Void->Bool = null; + var parseUnary:Void->Bool = null; + var parsePrimary:Void->Bool = null; + + parsePrimary = function():Bool + { + if (peek() == null) + Context.error('Unexpected end of condition in condition: $condition', Context.currentPos()); + + if (peek().type == "(") + { + consume("("); + var result = parseExpression(); + if (peek() == null || peek().type != ")") + Context.error('Expected closing parenthesis in condition: $condition', Context.currentPos()); + consume(")"); + return result; + } + else if (peek().type == "id") + { + var token = consume("id"); + return Context.defined(token.value); + } + else if (peek().type == "num") + { + var token = consume("num"); + return Std.parseFloat(token.value) != 0; + } + else if (peek().type == "str") + { + var token = consume("str"); + return token.value != ""; + } + else + { + Context.error('Unexpected token ${peek().type} in condition: $condition', Context.currentPos()); + return false; + } + } + + parseUnary = function():Bool + { + if (peek() != null && peek().type == "op" && peek().value == "!") + { + consume("op", "!"); + return !parseUnary(); + } + return parsePrimary(); + } + + parseComparison = function():Bool + { + var left = parseUnary(); + while (peek() != null && peek().type == "cmp") + { + var op = consume("cmp").value; + var right = parseUnary(); + + switch (op) + { + case "==": left = left == right; + case "!=": left = left != right; + case "<", ">", "<=", ">=": + Context.error('Comparison operators <, >, <=, >= are not supported for boolean values', Context.currentPos()); + left = false; + default: + Context.error('Unknown comparison operator: $op', Context.currentPos()); + } + } + return left; + } + + parseAnd = function():Bool + { + var left = parseComparison(); + while (peek() != null && peek().type == "op" && peek().value == "&&") + { + consume("op", "&&"); + var right = parseComparison(); + left = left && right; + } + return left; + } + + parseOr = function():Bool + { + var left = parseAnd(); + while (peek() != null && peek().type == "op" && peek().value == "||") + { + consume("op", "||"); + var right = parseAnd(); + left = left || right; + } + return left; + } + + parseExpression = function():Bool + { + return parseOr(); + } + + return parseExpression(); + } + + static function tokenize(condition:String):Array<{type:String, value:String}> + { + var tokens:Array<{type:String, value:String}> = []; + var i = 0; + var length = condition.length; + + while (i < length) + { + var c = condition.charAt(i); + + if (c == ' ') + { + i++; + continue; + } + + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') + { + var start = i; + while (i < length && + ((condition.charAt(i) >= 'a' && condition.charAt(i) <= 'z') || + (condition.charAt(i) >= 'A' && condition.charAt(i) <= 'Z') || + (condition.charAt(i) >= '0' && condition.charAt(i) <= '9') || + condition.charAt(i) == '_')) + { + i++; + } + var value = condition.substring(start, i); + tokens.push({type: "id", value: value}); + continue; + } + + if (c >= '0' && c <= '9') + { + var start = i; + var dot = false; + while (i < length && + (condition.charAt(i) >= '0' && condition.charAt(i) <= '9') || + (!dot && condition.charAt(i) == '.')) + { + if (condition.charAt(i) == '.') dot = true; + i++; + } + var value = condition.substring(start, i); + tokens.push({type: "num", value: value}); + continue; + } + + if (c == '"' || c == "'") + { + var quote = c; + var start = i; + i++; + while (i < length && condition.charAt(i) != quote) + { + i++; + } + if (i >= length) + Context.error('Unclosed string literal in condition: $condition', Context.currentPos()); + var value = condition.substring(start + 1, i); + tokens.push({type: "str", value: value}); + i++; + continue; + } + + if (c == '&' && i + 1 < length && condition.charAt(i + 1) == '&') + { + tokens.push({type: "op", value: "&&"}); + i += 2; + continue; + } + + if (c == '|' && i + 1 < length && condition.charAt(i + 1) == '|') + { + tokens.push({type: "op", value: "||"}); + i += 2; + continue; + } + + if (c == '!') + { + if (i + 1 < length && condition.charAt(i + 1) == '=') + { + tokens.push({type: "cmp", value: "!="}); + i += 2; + } + else + { + tokens.push({type: "op", value: "!"}); + i++; + } + continue; + } + + if (c == '=') + { + if (i + 1 < length && condition.charAt(i + 1) == '=') + { + tokens.push({type: "cmp", value: "=="}); + i += 2; + } + else + { + tokens.push({type: "op", value: "="}); + i++; + } + continue; + } + + if (c == '(') + { + tokens.push({type: "(", value: "("}); + i++; + continue; + } + + if (c == ')') + { + tokens.push({type: ")", value: ")"}); + i++; + continue; + } + + Context.error('Unexpected character $c in condition: $condition', Context.currentPos()); + } + + return tokens; } static function buildAbstract(typePath:TypePath):Expr @@ -66,7 +376,7 @@ class AbstractMacro case TAbstract(t, params): t.get(); default: - Context.error('Failed to build non-abstract type', Context.currentPos()); + Context.warning('Type ${typePath.fullPath} is not an abstract', Context.currentPos()); null; } @@ -114,4 +424,4 @@ class AbstractMacro return macro $v{alias ?? typePath.fullPath} => $e{value}; } } -#end +#end \ No newline at end of file From 2e5a28fac5d823bfa5fbf9042501862244bf1d2d Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:22:42 +0500 Subject: [PATCH 06/16] Map support --- rulescript/RuleScript.hx | 8 ++++++-- rulescript/interps/RuleScriptInterp.hx | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/rulescript/RuleScript.hx b/rulescript/RuleScript.hx index c28fddd..88c2a21 100644 --- a/rulescript/RuleScript.hx +++ b/rulescript/RuleScript.hx @@ -101,7 +101,7 @@ class RuleScript * * Structure: Package => Types. * - * @see https://haxe.org/manual/type-system-import-defaults.html/ + * @see https://haxe.org/documentation/introduction/stdlib-introduction.html#general-purpose-api */ public static var defaultImports:Map> = [ '' => [ @@ -118,8 +118,12 @@ class RuleScript 'Xml' => Xml, 'Int' => Int, 'String' => String, + 'StringBuf' => StringBuf, 'Float' => Float, - 'Bool' => Bool + 'Bool' => Bool, + 'Array' => Array, + 'Lambda' => Lambda, + 'EReg' => EReg ] ]; diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index 5985a12..98e461f 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -722,10 +722,8 @@ class RuleScriptInterp extends hscript.Interp implements IInterp #if (hscript >= "2.7.0") override function makeKeyValueIterator(v:Dynamic):KeyValueIterator { - #if hl - if (v is StringMap) + if (#if hl v is StringMap || #end v is haxe.Constraints.IMap) return new haxe.iterators.MapKeyValueIterator(v); - #end return super.makeKeyValueIterator(v); } @@ -850,6 +848,9 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function cnew(cl:String, args:Array):Dynamic { + if (cl == "Map" || cl == "haxe.ds.Map") + return new Map(); + var c:Dynamic = Type.resolveClass(cl); c ??= ScriptedTypeUtil.resolveScript(cl); From 3bf4f0899e53027f392d2e67d5990501fbb3bd39 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Sun, 24 May 2026 11:22:59 +0500 Subject: [PATCH 07/16] `final` variables support --- rulescript/interps/RuleScriptInterp.hx | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index 7da101a..defd27b 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -29,6 +29,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp public var imports:Map = []; public var usings:Map = []; + public var finalVariables:Map = []; public var superInstance(default, set):Dynamic; @@ -58,6 +59,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp imports = []; usings = []; typePaths = []; + finalVariables.clear(); } override public function posInfos():haxe.PosInfos @@ -119,11 +121,13 @@ class RuleScriptInterp extends hscript.Interp implements IInterp switch (hscript.Tools.expr(e1)) { case EIdent(id): - var l = locals.get(id); + var l:Dynamic = locals.get(id); if (l == null) setVar(id, v); else { + if (l.isFinal) throw new haxe.Exception('Cannot reassign final variable: ' + id); + if (l.r is Property) cast(l.r, Property).value = v; else @@ -160,12 +164,14 @@ class RuleScriptInterp extends hscript.Interp implements IInterp switch (hscript.Tools.expr(e1)) { case EIdent(id): - var l = locals.get(id); + var l:Dynamic = locals.get(id); v = fop(expr(e1), expr(e2)); if (l == null) setVar(id, v); else { + if (l.isFinal) throw new haxe.Exception('Cannot reassign final variable: ' + id); + if (l.r is Property) cast(l.r, Property).value = v; else @@ -209,7 +215,9 @@ class RuleScriptInterp extends hscript.Interp implements IInterp switch (e) { case EIdent(id): - var l = locals.get(id); + var l:Dynamic = locals.get(id); + if (l != null && l.isFinal) throw new haxe.Exception('Cannot increment/decrement final variable: ' + id); + var v:Dynamic = (l == null) ? resolve(id) : (l.r is Property ? cast(l.r, Property).value : l.r); if (prefix) @@ -285,6 +293,8 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function setVar(name:String, v:Dynamic) { + if (finalVariables.exists(name)) throw new haxe.Exception('Cannot reassign global final variable: ' + name); + if (superInstance != null && (superFields.contains(name) || superFields.contains('set_' + name))) Reflect.setProperty(superInstance, name, v); else if (context != null && context.staticVariables.exists(name)) @@ -411,16 +421,20 @@ class RuleScriptInterp extends hscript.Interp implements IInterp (onMeta != null) ? onMeta(n, args, e) : exprMeta(n, args, e); } - case EVar(n, _, e, global, _): + case EVar(n, _, e, global, isFinal): if (global) { - if (context == null || (!context.staticVariables.exists(n) && !context.publicVariables.exists(n))) + if (context == null || (!context.staticVariables.exists(n) && !context.publicVariables.exists(n))) { variables.set(n, (e == null) ? null : this.expr(e)); + if (isFinal) finalVariables.set(n, true); + } } else { declared.push({n: n, old: locals.get(n)}); - locals.set(n, {r: (e == null) ? null : this.expr(e)}); + var ref:Dynamic = {r: (e == null) ? null : this.expr(e)}; + if (isFinal) ref.isFinal = true; + locals.set(n, ref); } return null; From 0de669c593cf40e221fa8b8942fac94c3ce4877b Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Sun, 7 Jun 2026 21:33:06 +0500 Subject: [PATCH 08/16] fix error reports and optional parameters --- rulescript/interps/RuleScriptInterp.hx | 44 ++++++++++++++++++---- rulescript/macro/RuleScriptedClassMacro.hx | 18 +++++++-- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index defd27b..22bf223 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -118,6 +118,11 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function assign(e1:Expr, e2:Expr):Dynamic { var v = expr(e2); + + #if hscriptPos + curExpr = e1; + #end + switch (hscript.Tools.expr(e1)) { case EIdent(id): @@ -161,6 +166,11 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function evalAssignOp(op:String, fop:(Dynamic, Dynamic) -> Dynamic, e1:Expr, e2:Expr):Dynamic { var v; + + #if hscriptPos + curExpr = e1; + #end + switch (hscript.Tools.expr(e1)) { case EIdent(id): @@ -321,6 +331,17 @@ class RuleScriptInterp extends hscript.Interp implements IInterp } catch (exception:haxe.Exception) { + #if hscriptPos + var pos = posInfos(); + + @:privateAccess + if (pos != null && pos.lineNumber > 0 && !Std.isOfType(exception.unwrap(), hscript.Expr.Error)) + { + var msg = exception.message + ' (at ' + pos.fileName + ':' + pos.lineNumber + ')'; + exception = new haxe.Exception(msg, exception.previous != null ? exception.previous : exception); + } + #end + errorHandler(exception); } else @@ -520,7 +541,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp error(ECustom("Rest should only be used for the last function argument")); } - if (p.opt) + if (p.opt || p.value != null) hasOpt = true; else if (!isRest) minParams++; @@ -539,17 +560,15 @@ class RuleScriptInterp extends hscript.Interp implements IInterp error(ECustom(str)); } - if (params.length != args.length || hasRest) + if (hasOpt || hasRest || params.length != args.length) { final args2:Array = []; var argId = 0; var extraParams = args.length - minParams; - for (id => param in params) { var isRest = hasRest && id == params.length - 1 && Tools.isRest(param.t); - var arg:Dynamic = null; if (isRest) @@ -557,7 +576,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp arg = args.slice(argId); argId = args.length; } - else if (param.opt) + else if (param.opt || param.value != null) { if (extraParams > 0 && argId < args.length) { @@ -575,7 +594,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp } if (arg == null && param.value != null) - arg = this.expr(param.value); + arg = me.expr(param.value); args2.push(arg); } @@ -585,8 +604,19 @@ class RuleScriptInterp extends hscript.Interp implements IInterp var old = me.locals, depth = me.depth; me.depth++; me.locals = me.duplicate(capturedLocals); + for (i in 0...params.length) - me.locals.set(params[i].name, {r: args[i]}); + { + var pName = params[i].name; + if (pName != null) { + if (pName.indexOf(':') != -1) pName = pName.substring(0, pName.indexOf(':')); + if (pName.indexOf('=') != -1) pName = pName.substring(0, pName.indexOf('=')); + pName = StringTools.trim(pName); + } + + me.locals.set(pName, {r: args[i]}); + } + var r = null; var oldDecl = declared.length; if (inTry) diff --git a/rulescript/macro/RuleScriptedClassMacro.hx b/rulescript/macro/RuleScriptedClassMacro.hx index 91d2d6a..b8d7f6b 100644 --- a/rulescript/macro/RuleScriptedClassMacro.hx +++ b/rulescript/macro/RuleScriptedClassMacro.hx @@ -144,15 +144,27 @@ class RuleScriptedClassMacro }, 'variableExists' => macro function(name:String):Bool { - return __rulescript?.variables.exists(name); + return __rulescript?.variables.exists(name) || Reflect.hasField(this, name) || Reflect.getProperty(this, name) != null; }, 'getVariable' => macro function(name:String):Dynamic { - return __rulescript.variables[name]; + if (__rulescript.variables.exists(name)) + return __rulescript.variables[name]; + + return Reflect.getProperty(this, name); }, 'setVariable' => macro function(name:String, value:Dynamic):Dynamic { - return __rulescript.variables[name] = value; + try { + if (__rulescript.variables.exists(name) || (Reflect.getProperty(this, name) == null && !Reflect.hasField(this, name))) { + return __rulescript.variables[name] = value; + } + + Reflect.setProperty(this, name, value); + return value; + } catch(e:Dynamic) { + return __rulescript.variables[name] = value; + } }, 'get___rulescript_type' => macro function():rulescript.types.ScriptedType.TypeID { From 3e5b016136f495193a28dc049400d61ec05f0b69 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Mon, 8 Jun 2026 11:13:01 +0500 Subject: [PATCH 09/16] Update RuleScriptInterp.hx --- rulescript/interps/RuleScriptInterp.hx | 35 +++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index 22bf223..ebceb4d 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -331,17 +331,6 @@ class RuleScriptInterp extends hscript.Interp implements IInterp } catch (exception:haxe.Exception) { - #if hscriptPos - var pos = posInfos(); - - @:privateAccess - if (pos != null && pos.lineNumber > 0 && !Std.isOfType(exception.unwrap(), hscript.Expr.Error)) - { - var msg = exception.message + ' (at ' + pos.fileName + ':' + pos.lineNumber + ')'; - exception = new haxe.Exception(msg, exception.previous != null ? exception.previous : exception); - } - #end - errorHandler(exception); } else @@ -1042,7 +1031,29 @@ class RuleScriptInterp extends hscript.Interp implements IInterp { hasErrorHandler = v != null; - return errorHandler = v; + if (v != null) + { + errorHandler = (exception:haxe.Exception) -> + { + #if hscriptPos + var pos = posInfos(); + @:privateAccess + if (pos != null && pos.lineNumber > 0 && !Std.isOfType(exception.unwrap(), hscript.Expr.Error)) + { + var msg = exception.message + ' (at ' + pos.fileName + ':' + pos.lineNumber + ')'; + exception = new haxe.Exception(msg, exception.previous != null ? exception.previous : exception); + } + #end + + v(exception); + }; + } + else + { + errorHandler = null; + } + + return v; } @:noCompletion From b233d87d5bbaf3aa6894c7ae5c5538f1d1a21237 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Mon, 15 Jun 2026 22:59:45 +0500 Subject: [PATCH 10/16] fix for scripted classes --- rulescript/interps/RuleScriptInterp.hx | 15 +++- rulescript/macro/RuleScriptedClassMacro.hx | 72 ++++++++++++++----- .../scriptedClass/RuleScriptedClassUtil.hx | 12 ++++ 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index ebceb4d..2e833ae 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -60,6 +60,14 @@ class RuleScriptInterp extends hscript.Interp implements IInterp usings = []; typePaths = []; finalVariables.clear(); + + if (rulescript.scriptedClass.RuleScriptedClassUtil.autoWrappers != null) + { + for (nativeName => wrapperClass in rulescript.scriptedClass.RuleScriptedClassUtil.autoWrappers) + { + variables.set(nativeName, wrapperClass); + } + } } override public function posInfos():haxe.PosInfos @@ -83,8 +91,11 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function resolve(id:String):Dynamic { - if (id == 'this') - return this; + if (id == 'this') + { + if (superInstance != null) return superInstance; + return this; + } if (id == 'super' && superInstance != null) return superInstance; diff --git a/rulescript/macro/RuleScriptedClassMacro.hx b/rulescript/macro/RuleScriptedClassMacro.hx index b8d7f6b..fe656e6 100644 --- a/rulescript/macro/RuleScriptedClassMacro.hx +++ b/rulescript/macro/RuleScriptedClassMacro.hx @@ -97,7 +97,13 @@ class RuleScriptedClassMacro { final forceOverrideField = forceOverrideFields?.contains(name) ?? forceOverride; if (!ignoredFields.contains(name)) - fields.push(overrideField(field, forceOverrideField)); + { + if (field.params.length > 0) continue; + + final overriden = overrideField(field, forceOverrideField); + if (overriden != null) + fields.push(overriden); + } } if (constructor.isFinal) @@ -181,6 +187,22 @@ class RuleScriptedClassMacro meta: (name == 'get___rulescript_type') ? [{name: ':noCompletion', pos: pos}] : [] }); + var localClassName = curType.name; + var nativeClassName = curType.superClass.t.get().name; + + fields.push({ + name: '__init__', + access: [AStatic], + kind: FFun({ + args: [], + ret: macro :Void, + expr: macro { + rulescript.scriptedClass.RuleScriptedClassUtil.registerAutoWrapper($v{nativeClassName}, $i{localClassName}); + } + }), + pos: pos + }); + return fields; } @@ -193,15 +215,19 @@ class RuleScriptedClassMacro case TFun(_args, ret): args = _args; case TLazy(type): - switch (type()) - { - case TFun(_args, ret): - args = _args; - default: - }; + try { + switch (type()) + { + case TFun(_args, ret): + args = _args; + default: + } + } catch (e:Dynamic) {} default: } + if (args == null) args = []; + var fieldArgs = strict ? [for (argument in args) macro $i{argument.name}] : [macro args]; var scriptSuperCall = [ @@ -307,28 +333,30 @@ class RuleScriptedClassMacro ret: forceOverride ? (returnsVoid ? macro :StdTypes.Void : null) : getOverrideType(ret), expr: macro { - return if (!__rulescript.access.isSuperCall && __rulescript.access.variableExists($v{field.name})) + final _hasAccess = __rulescript != null && __rulescript.access != null; + + return if (_hasAccess && !__rulescript.access.isSuperCall && __rulescript.access.variableExists($v{field.name})) { __rulescript.access.getVariable($v{field.name})($a{fieldArgs}); } else { - final lastIsSuperCall:Bool = __rulescript.access.isSuperCall; + final lastIsSuperCall:Bool = _hasAccess ? __rulescript.access.isSuperCall : false; $ { if (returnsVoid) macro { - __rulescript.access.isSuperCall = false; + if (_hasAccess) __rulescript.access.isSuperCall = false; super.$fieldName($a{fieldArgs}); - __rulescript.access.isSuperCall = lastIsSuperCall; + if (_hasAccess) __rulescript.access.isSuperCall = lastIsSuperCall; } else macro { - __rulescript.access.isSuperCall = false; + if (_hasAccess) __rulescript.access.isSuperCall = false; final value = cast super.$fieldName($a{fieldArgs}); - __rulescript.access.isSuperCall = lastIsSuperCall; + if (_hasAccess) __rulescript.access.isSuperCall = lastIsSuperCall; value; } } @@ -349,15 +377,21 @@ class RuleScriptedClassMacro case TFun(args, ret): kind = tFunToExpr(args, ret); case TLazy(type): - switch (type()) - { - case TFun(args, ret): - kind = tFunToExpr(args, ret); - default: - }; + try { + switch (type()) + { + case TFun(args, ret): + kind = tFunToExpr(args, ret); + default: + } + } catch (e:Dynamic) { + return null; + } default: } + if (kind == null) return null; + return { name: field.name, access: [AOverride], diff --git a/rulescript/scriptedClass/RuleScriptedClassUtil.hx b/rulescript/scriptedClass/RuleScriptedClassUtil.hx index 5204c6f..b468427 100644 --- a/rulescript/scriptedClass/RuleScriptedClassUtil.hx +++ b/rulescript/scriptedClass/RuleScriptedClassUtil.hx @@ -29,6 +29,18 @@ class RuleScriptedClassUtil public static var buildBridge:(typePath:String, superInstance:Dynamic) -> RuleScript; + public static var autoWrappers:Map; + + public static function registerAutoWrapper(nativeName:String, wrapperClass:Dynamic):Void + { + if (autoWrappers == null) + { + autoWrappers = new Map(); + } + + autoWrappers.set(nativeName, wrapperClass); + } + public static function buildRuleScript(typePath:String, superInstance:Dynamic):RuleScript { return if (buildBridge != null) From 89cd0419b3540e3d097c0a8c988d9815c6b13105 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:35:37 +0500 Subject: [PATCH 11/16] ok --- rulescript/interps/RuleScriptInterp.hx | 24 +- rulescript/macro/RuleScriptedClassMacro.hx | 466 +++++------------- rulescript/scriptedClass/RuleScriptedClass.hx | 91 +++- .../scriptedClass/RuleScriptedClassUtil.hx | 10 + 4 files changed, 240 insertions(+), 351 deletions(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index 2e833ae..d07db48 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -892,27 +892,25 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function call(o:Dynamic, f:Dynamic, args:Array):Dynamic { - if (o == superInstance) - isSuperCall = true; - if (f == superInstance) return call(o, resolve('__super_new'), args); #if rulescript_use_hl_fixes - final result:Dynamic = Tools.__hl_callMethod(f, args); + return Tools.__hl_callMethod(f, args); #else - final result:Dynamic = super.call(o, f, args); + return super.call(o, f, args); #end - - isSuperCall = false; - - return result; } override function fcall(o:Dynamic, f:String, args:Array):Dynamic { - return call(o, ((o == superInstance - && (locals.exists('__super_$f') || variables.exists('__super_$f'))) ? (resolve('__super_$f')) : get(o, f)), args); + if (o == superInstance) + { + final nativeSuper = Reflect.field(o, '__super_' + f); + if (nativeSuper != null) return call(o, nativeSuper, args); + } + + return call(o, get(o, f), args); } override function cnew(cl:String, args:Array):Dynamic @@ -1120,10 +1118,10 @@ class RuleScriptInterp extends hscript.Interp implements IInterp preExpr = rulescript.Tools.toExpr(EBlock(exprs.slice(0, superID))); postExpr = rulescript.Tools.toExpr(EBlock(exprs.slice(superID + 1))); - superCallArgs = superID == exprs.length ? null : switch (rulescript.Tools.getExpr(exprs[superID])) + superCallArgs = superID == exprs.length ? [] : switch (rulescript.Tools.getExpr(exprs[superID])) { case ECall(_, params): params; - default: null; + default: []; }; } } diff --git a/rulescript/macro/RuleScriptedClassMacro.hx b/rulescript/macro/RuleScriptedClassMacro.hx index fe656e6..d0742d4 100644 --- a/rulescript/macro/RuleScriptedClassMacro.hx +++ b/rulescript/macro/RuleScriptedClassMacro.hx @@ -14,82 +14,57 @@ class RuleScriptedClassMacro public static macro function build():Array { + if (Context.getDisplayMode() != None) return null; + var pos = Context.currentPos(); var fields:Array = Context.getBuildFields(); - var typefields:Map = []; var curType = Context.getLocalClass().get(); - if (curType.meta.has(':noBuild')) - return fields; + if (curType.meta.has(':noBuild')) return fields; curType = curType.superClass.t.get(); - var constructor = curType.constructor?.get(); - var inlinedFields:Array = []; + while (curType != null) { for (field in curType.fields.get()) { if (!inlinedFields.contains(field.name) && field.kind.match(FMethod(MethInline))) - { inlinedFields.push(field.name); - } if (!typefields.exists(field.name) && !field.isFinal && field.kind.match(FMethod(_)) && !inlinedFields.contains(field.name)) typefields.set(field.name, field); } curType = curType.superClass?.t.get(); - constructor ??= curType.constructor?.get(); } createAliasMap(); - curType = Context.getLocalClass().get(); var ignoredFields:Array = []; - for (meta in curType.meta.extract(':ignoreFields')) - { - switch (meta.params[0].expr) - { + for (meta in curType.meta.extract(':ignoreFields')) { + switch (meta.params[0].expr) { case EArrayDecl(values): - for (value in values) - switch (value.expr) - { - case EConst(CIdent(s)): - ignoredFields.push(s); - default: - } - + for (value in values) switch (value.expr) { case EConst(CIdent(s)): ignoredFields.push(s); default: } default: } - }; + } final forceOverride = curType.meta.has(':forceOverride'); var forceOverrideFields:Array = null; - if (forceOverride) - { - for (meta in curType.meta.extract(':forceOverride')) - { - if (meta.params[0] != null) - switch (meta.params[0].expr) - { - case EArrayDecl(values): - forceOverrideFields ??= []; - - for (value in values) - switch (value.expr) - { - case EConst(CIdent(s)): - forceOverrideFields.push(s); - default: - } - default: - } + if (forceOverride) { + for (meta in curType.meta.extract(':forceOverride')) { + if (meta.params[0] != null) switch (meta.params[0].expr) { + case EArrayDecl(values): + forceOverrideFields ??= []; + for (value in values) switch (value.expr) { case EConst(CIdent(s)): forceOverrideFields.push(s); default: } + default: + } } } @@ -98,109 +73,53 @@ class RuleScriptedClassMacro final forceOverrideField = forceOverrideFields?.contains(name) ?? forceOverride; if (!ignoredFields.contains(name)) { + if (field.meta.has(':generic')) continue; if (field.params.length > 0) continue; + + var hasPrivateArg = false; + switch(field.type) { + case TFun(args, ret): + for(arg in args) { + switch(arg.t) { + case TInst(ty, _): if(ty.get().isPrivate) hasPrivateArg = true; + default: + } + } + default: + } + if (hasPrivateArg) continue; - final overriden = overrideField(field, forceOverrideField); - if (overriden != null) - fields.push(overriden); + final overridenArray = overrideField(field, forceOverrideField); + if (overridenArray != null) + for (f in overridenArray) fields.push(f); } } - if (constructor.isFinal) - Context.error("Constructor can't be final in RuleScriptedClass", pos); - + if (constructor.isFinal) Context.error("Constructor can't be final in RuleScriptedClass", pos); final strict = curType.meta.has(':strictScriptedConstructor') || curType.meta.has(':strictConstructor'); - final forceOverrideConstructor = forceOverrideFields?.contains('new') ?? forceOverride; - fields.push({ - name: 'new', - access: [APublic], - kind: FFun(createConstructor(constructor, strict, forceOverrideConstructor)), - pos: pos - }); - - fields.push({ - name: '__rulescript_strict', - access: [AStatic, AFinal], - kind: FVar(macro :Bool, macro $v{strict}), - pos: pos - }); - - fields.push({ - name: '__rulescript_type', - access: [APublic], - kind: FProp('get', 'never', macro :rulescript.types.ScriptedType.TypeID), - pos: pos, - meta: [{name: ':noCompletion', pos: pos}] - }); - - fields.push({ - name: '__rulescript', - access: [APublic], - kind: FVar(macro :rulescript.RuleScript), - pos: pos, - meta: [{name: ':noCompletion', pos: pos}] - }); + fields.push({ name: 'new', access: [APublic], kind: FFun(createConstructor(constructor, strict, forceOverrideConstructor)), pos: pos }); + fields.push({ name: '__rulescript_strict', access: [AStatic, AFinal], kind: FVar(macro :Bool, macro $v{strict}), pos: pos }); + fields.push({ name: '__rulescript_type', access: [APublic], kind: FProp('get', 'never', macro :rulescript.types.ScriptedType.TypeID), pos: pos, meta: [{name: ':noCompletion', pos: pos}] }); + fields.push({ name: '__rulescript', access: [APublic], kind: FVar(macro :rulescript.RuleScript), pos: pos, meta: [{name: ':noCompletion', pos: pos}] }); final functions = [ - 'getVariables' => macro function():Map - { - return __rulescript.variables; - }, - 'variableExists' => macro function(name:String):Bool - { - return __rulescript?.variables.exists(name) || Reflect.hasField(this, name) || Reflect.getProperty(this, name) != null; - }, - 'getVariable' => macro function(name:String):Dynamic - { - if (__rulescript.variables.exists(name)) - return __rulescript.variables[name]; - - return Reflect.getProperty(this, name); - }, - 'setVariable' => macro function(name:String, value:Dynamic):Dynamic - { - try { - if (__rulescript.variables.exists(name) || (Reflect.getProperty(this, name) == null && !Reflect.hasField(this, name))) { - return __rulescript.variables[name] = value; - } - - Reflect.setProperty(this, name, value); - return value; - } catch(e:Dynamic) { - return __rulescript.variables[name] = value; - } - }, - 'get___rulescript_type' => macro function():rulescript.types.ScriptedType.TypeID - { - return rulescript.types.ScriptedType.TypeID.CLASS; - } + 'getVariables' => macro function():Map { return __rulescript.variables; }, + 'variableExists' => macro function(name:String):Bool { return __rulescript?.variables.exists(name) || Reflect.hasField(this, name) || Reflect.getProperty(this, name) != null; }, + 'getVariable' => macro function(name:String):Dynamic { if (__rulescript.variables.exists(name)) return __rulescript.variables[name]; return Reflect.getProperty(this, name); }, + 'setVariable' => macro function(name:String, value:Dynamic):Dynamic { try { if (__rulescript.variables.exists(name) || (Reflect.getProperty(this, name) == null && !Reflect.hasField(this, name))) { return __rulescript.variables[name] = value; } Reflect.setProperty(this, name, value); return value; } catch(e:Dynamic) { return __rulescript.variables[name] = value; } }, + 'get___rulescript_type' => macro function():rulescript.types.ScriptedType.TypeID { return rulescript.types.ScriptedType.TypeID.CLASS; } ]; for (name => func in functions) - fields.push({ - name: name, - access: [APublic], - kind: FFun(MacroTools.toFunction(func)), - pos: pos, - meta: (name == 'get___rulescript_type') ? [{name: ':noCompletion', pos: pos}] : [] - }); + fields.push({ name: name, access: [APublic], kind: FFun(MacroTools.toFunction(func)), pos: pos, meta: (name == 'get___rulescript_type') ? [{name: ':noCompletion', pos: pos}] : [] }); var localClassName = curType.name; var nativeClassName = curType.superClass.t.get().name; fields.push({ - name: '__init__', - access: [AStatic], - kind: FFun({ - args: [], - ret: macro :Void, - expr: macro { - rulescript.scriptedClass.RuleScriptedClassUtil.registerAutoWrapper($v{nativeClassName}, $i{localClassName}); - } - }), - pos: pos + name: '__init__', access: [AStatic], kind: FFun({ args: [], ret: macro :Void, expr: macro { rulescript.scriptedClass.RuleScriptedClassUtil.registerAutoWrapper($v{nativeClassName}, $i{localClassName}); } }), pos: pos }); return fields; @@ -209,263 +128,146 @@ class RuleScriptedClassMacro static function createConstructor(constructor:ClassField, strict:Bool = false, forceOverride:Bool):Function { var args = null; - - switch (constructor.type) - { - case TFun(_args, ret): - args = _args; - case TLazy(type): - try { - switch (type()) - { - case TFun(_args, ret): - args = _args; - default: - } - } catch (e:Dynamic) {} + switch (constructor.type) { + case TFun(_args, ret): args = _args; + case TLazy(type): try { switch (type()) { case TFun(_args, ret): args = _args; default: } } catch (e:Dynamic) {} default: } - if (args == null) args = []; var fieldArgs = strict ? [for (argument in args) macro $i{argument.name}] : [macro args]; + var scriptSuperCall = [for (i in 0...args.length) macro superCallArgs[$v{i}]]; - var scriptSuperCall = [ - for (i in 0...args.length) - macro superCallArgs[$v{i}] - ]; - - var funcArgs:Array = [ - { - name: 'typeName', - type: macro :String - } - ]; - - if (strict) - funcArgs = funcArgs.concat([ - for (arg in args) - { - name: arg.name, - opt: arg.opt, - type: forceOverride ? macro :Dynamic : getOverrideType(arg.t) - } - ]); - else - funcArgs.push({ - name: 'args', - opt: true, - type: macro :Array - }); + var funcArgs:Array = [{ name: 'typeName', type: macro :String }]; + if (strict) funcArgs = funcArgs.concat([for (arg in args) { name: arg.name, opt: arg.opt, type: forceOverride ? macro :Dynamic : getOverrideType(arg.t) }]); + else funcArgs.push({ name: 'args', opt: true, type: macro :Array }); return { args: funcArgs, - expr: Context.getLocalClass().get().superClass != null ? macro - { - __rulescript = rulescript.scriptedClass.RuleScriptedClassUtil.buildRuleScript(typeName, this); - - $e{!strict ? macro args ??= [] : macro {}} // If args equals null - - if (__rulescript.access.hasConstructor) - { - final c = __rulescript.access.createConstructor($ - { - if (strict) - macro $a{fieldArgs} - else - macro args - }); - - c.preCall(); - - final superCallArgs:Array = c.lastSuperConstructor.getSuperArgs(); - - super($a{scriptSuperCall}); - c.postCall(); - } - else - { - super($a - { - strict ? fieldArgs : [for (i in 0...args.length) macro args[$v{i}]] - }); - } - } : macro {}, + expr: Context.getLocalClass().get().superClass != null ? macro { + __rulescript = rulescript.scriptedClass.RuleScriptedClassUtil.buildRuleScript(typeName, this); + $e{!strict ? macro args ??= [] : macro {}} + if (__rulescript.access.hasConstructor) { + final c = __rulescript.access.createConstructor(${if (strict) macro $a{fieldArgs} else macro args}); + c.preCall(); + final superCallArgs:Array = c.lastSuperConstructor.getSuperArgs(); + super($a{scriptSuperCall}); + c.postCall(); + } else { + super($a{strict ? fieldArgs : [for (i in 0...args.length) macro args[$v{i}]]}); + } + } : macro {}, params: forceOverride ? [] : [for (param in constructor.params) {name: param.name}] } } - static function overrideField(field:ClassField, forceOverride:Bool):Field + static function overrideField(field:ClassField, forceOverride:Bool):Array { var kind = null; - + var superKind = null; var fieldName = field.name; - var tFunToExpr:(Array, ret:haxe.macro.Type) -> Function = (args, ret) -> + var tFunToExpr:(Array, ret:haxe.macro.Type) -> {over: Function, sup: Function} = (args, ret) -> { - var fieldArgs = [ - for (argument in args) - macro $i{argument.name} + var fieldArgs = [for (argument in args) macro $i{argument.name}]; + final returnsVoid:Bool = getOverrideType(ret).match(TPath({name: 'StdTypes', params: [], sub: 'Void', pack: []})); + + var funcArgs:Array = [ + for (id => arg in args) { + name: arg.name, + type: forceOverride ? macro :Dynamic : getOverrideType(arg.t), + value: switch (Context.getTypedExpr(field.expr()).expr) { case EFunction(kind, f): f.args[id].value; default: null; } + } ]; - final returnsVoid:Bool = getOverrideType(ret).match(TPath({ - name: 'StdTypes', - params: [], - sub: 'Void', - pack: [] - })); + var funcRet:ComplexType = forceOverride ? (returnsVoid ? macro :StdTypes.Void : null) : getOverrideType(ret); - return { - args: [ - for (id => arg in args) - { - name: arg.name, - type: forceOverride ? macro :Dynamic : getOverrideType(arg.t), - value: switch (Context.getTypedExpr(field.expr()).expr) - { - case EFunction(kind, f): - f.args[id].value; - default: - null; - } - } - ], - ret: forceOverride ? (returnsVoid ? macro :StdTypes.Void : null) : getOverrideType(ret), - expr: macro - { - final _hasAccess = __rulescript != null && __rulescript.access != null; - - return if (_hasAccess && !__rulescript.access.isSuperCall && __rulescript.access.variableExists($v{field.name})) - { - __rulescript.access.getVariable($v{field.name})($a{fieldArgs}); + var overExpr = if (returnsVoid) macro { + if (__rulescript != null && __rulescript.access != null && __rulescript.access.variableExists($v{fieldName})) { + try { + __rulescript.access.getVariable($v{fieldName})($a{fieldArgs}); + } catch (e:Dynamic) { + trace("[RuleScript] Error: Purging broken function '" + $v{fieldName} + "' to prevent soft-lock -> " + e); + __rulescript.variables.remove($v{fieldName}); + super.$fieldName($a{fieldArgs}); } - else - { - final lastIsSuperCall:Bool = _hasAccess ? __rulescript.access.isSuperCall : false; - $ - { - if (returnsVoid) - macro - { - if (_hasAccess) __rulescript.access.isSuperCall = false; - super.$fieldName($a{fieldArgs}); - if (_hasAccess) __rulescript.access.isSuperCall = lastIsSuperCall; - } - else - macro - { - if (_hasAccess) __rulescript.access.isSuperCall = false; - final value = cast super.$fieldName($a{fieldArgs}); - if (_hasAccess) __rulescript.access.isSuperCall = lastIsSuperCall; - value; - } - } + } else { + super.$fieldName($a{fieldArgs}); + } + } else macro { + if (__rulescript != null && __rulescript.access != null && __rulescript.access.variableExists($v{fieldName})) { + try { + return cast __rulescript.access.getVariable($v{fieldName})($a{fieldArgs}); + } catch (e:Dynamic) { + trace("[RuleScript] Error: Purging broken function '" + $v{fieldName} + "' to prevent soft-lock -> " + e); + __rulescript.variables.remove($v{fieldName}); + return cast super.$fieldName($a{fieldArgs}); } - }, - params: if (forceOverride) - [] - else - [ - for (param in field.params) - {name: param.name} - ] - } + } else { + return cast super.$fieldName($a{fieldArgs}); + } + }; + + var superExpr = if (returnsVoid) macro { + super.$fieldName($a{fieldArgs}); + } else macro { + return cast super.$fieldName($a{fieldArgs}); + }; + + return { + over: { args: funcArgs, ret: funcRet, expr: overExpr, params: forceOverride ? [] : [for (param in field.params) {name: param.name}] }, + sup: { args: funcArgs, ret: funcRet, expr: superExpr, params: forceOverride ? [] : [for (param in field.params) {name: param.name}] } + }; } - switch (field.type) - { + switch (field.type) { case TFun(args, ret): - kind = tFunToExpr(args, ret); + var res = tFunToExpr(args, ret); + kind = res.over; superKind = res.sup; case TLazy(type): - try { - switch (type()) - { - case TFun(args, ret): - kind = tFunToExpr(args, ret); - default: - } - } catch (e:Dynamic) { - return null; - } + try { switch (type()) { case TFun(args, ret): var res = tFunToExpr(args, ret); kind = res.over; superKind = res.sup; default: } } catch (e:Dynamic) {} default: } if (kind == null) return null; - return { - name: field.name, - access: [AOverride], - kind: FFun(kind), - pos: Context.currentPos() - }; + return [ + { name: field.name, access: [AOverride], kind: FFun(kind), pos: Context.currentPos() }, + { name: '__super_' + field.name, access: [APublic], kind: FFun(superKind), pos: Context.currentPos(), meta: [{name: ':noCompletion', pos: Context.currentPos()}] } + ]; } - inline static function getOverrideType(type:haxe.macro.Type):ComplexType - { - return type != null ? Context.toComplexType(transformTypeParams(type)) : null; - } + inline static function getOverrideType(type:haxe.macro.Type):ComplexType { return type != null ? Context.toComplexType(transformTypeParams(type)) : null; } static function transformTypeParams(type:haxe.macro.Type):haxe.macro.Type { - switch (type) - { + switch (type) { case TInst(t, params): - var _t = t; - var _params = params; - + var _t = t; var _params = params; var className = Context.getLocalClass().get().name; - - while (aliasMap.exists(className + _t.toString())) - { - _t = switch (aliasMap.get(className + _t.toString())) - { - case TInst(t, params): - _params = params; - t; - default: null; - }; + while (aliasMap.exists(className + _t.toString())) { + _t = switch (aliasMap.get(className + _t.toString())) { case TInst(t, params): _params = params; t; default: null; }; } - - for (id => param in _params) - _params[id] = transformTypeParams(param); - + for (id => param in _params) _params[id] = transformTypeParams(param); type = TInst(_t, _params); case TFun(args, ret): - var _args = args; - var _ret = ret; - - for (arg in _args) - { - arg.t = transformTypeParams(arg.t); - } - + var _args = args; var _ret = ret; + for (arg in _args) arg.t = transformTypeParams(arg.t); type = TFun(_args, transformTypeParams(_ret)); case TAbstract(t, params): type = TAbstract(t, [for (param in params) transformTypeParams(param)]); - default: - null; + default: null; } - return type; } static function createAliasMap():Void { var t:ClassType = Context.getLocalClass().get(); - - while (t != null) - { - for (id => param in t.superClass?.params ?? []) - { - switch (param) - { - case TInst(_t, params): - aliasMap.set(Context.getLocalClass().get().name + switch (t.superClass?.t.get().params[id].t) - { - case TInst(t, params): - t.toString(); - default: null; - }, param); + while (t != null) { + for (id => param in t.superClass?.params ?? []) { + switch (param) { + case TInst(_t, params): aliasMap.set(Context.getLocalClass().get().name + switch (t.superClass?.t.get().params[id].t) { case TInst(t, params): t.toString(); default: null; }, param); default: } } @@ -473,4 +275,4 @@ class RuleScriptedClassMacro } } } -#end +#end \ No newline at end of file diff --git a/rulescript/scriptedClass/RuleScriptedClass.hx b/rulescript/scriptedClass/RuleScriptedClass.hx index 4beca44..972401c 100644 --- a/rulescript/scriptedClass/RuleScriptedClass.hx +++ b/rulescript/scriptedClass/RuleScriptedClass.hx @@ -135,6 +135,8 @@ abstract Access(RuleScriptedClass) classImpl: impl })); + rulescript.scriptedClass.RuleScriptedClassUtil.registerRuleScriptedClass(toString(), this); + if (impl.extend != null) { var type = Tools.typeToString(impl.extend); @@ -167,7 +169,7 @@ abstract Access(RuleScriptedClass) } initialize = if (nativeClass == null && (superClass == null || superClass is ScriptedClass)) - ScriptedInstance.new.bind(this, _) + function(args) { return new ScriptedInstance(this, args); } else if (nativeClass != null) { var type = toString(); @@ -204,21 +206,51 @@ abstract Access(RuleScriptedClass) public function getVariables():Map { - return interp.access.getVariables(); + init(); + var vars:Map = []; + for (k => v in interp.access.getVariables()) vars.set(k, v); + + if (module.context != null) { + for (k => v in module.context.staticVariables) vars.set(k, v); + for (k => v in module.context.publicVariables) vars.set(k, v); + } + return vars; } public function variableExists(name:String):Bool { - return interp.access.variableExists(name); + init(); + return interp.access.variableExists(name) || (module.context != null && (module.context.staticVariables.exists(name) || module.context.publicVariables.exists(name))); } public function getVariable(name:String):Dynamic { - return interp.access.getVariable(name); + init(); + if (interp.access.variableExists(name)) + return interp.access.getVariable(name); + + if (module.context != null) { + if (module.context.staticVariables.exists(name)) + return module.context.staticVariables.get(name); + if (module.context.publicVariables.exists(name)) + return module.context.publicVariables.get(name); + } + return null; } public function setVariable(name:String, value:Dynamic):Dynamic { + init(); + if (module.context != null) { + if (module.context.staticVariables.exists(name)) { + module.context.staticVariables.set(name, value); + return value; + } + if (module.context.publicVariables.exists(name)) { + module.context.publicVariables.set(name, value); + return value; + } + } return interp.access.setVariable(name, value); } @@ -229,9 +261,54 @@ abstract Access(RuleScriptedClass) return CLASS; } - public function createInstance(args:Array) + public function createInstance(args:Array):Dynamic { - return initialize(args ?? []); + init(); + + if (impl?.extend != null) + { + final extendName = rulescript.Tools.typeToString(impl.extend); + final shortName = extendName.split(".").pop(); + + if (rulescript.scriptedClass.RuleScriptedClassUtil.autoWrappers != null) + { + final wrapperClass = rulescript.scriptedClass.RuleScriptedClassUtil.autoWrappers.get(extendName) + ?? rulescript.scriptedClass.RuleScriptedClassUtil.autoWrappers.get(shortName); + + if (wrapperClass != null) + { + final isStrict:Bool = Reflect.field(wrapperClass, "__rulescript_strict") == true; + final scriptName = this.toString(); + + try { + if (isStrict) { + final wrapperArgs:Array = [scriptName]; + if (args != null) for (a in args) wrapperArgs.push(a); + return Type.createInstance(wrapperClass, wrapperArgs); + } else { + return Type.createInstance(wrapperClass, [scriptName, args ?? []]); + } + } catch(e:Dynamic) { + trace('[RuleScript] Error: Failed to create wrapper for ' + shortName + ' -> ' + e); + return null; + } + } else { + trace('[RuleScript] Warning: Wrapper for ' + shortName + ' not found in registry. It might have been removed by Dead Code Elimination (DCE).'); + } + } + } + + if (nativeClass != null) { + try { + return Type.createInstance(nativeClass, args ?? []); + } catch(e:Dynamic) { + trace('[RuleScript] Error: Failed to create native fallback class for ' + toString() + ' -> ' + e); + } + } + + initialize(args ?? []); + + return interp.access.superInstance ?? this; } @:access(rulescript.RuleScriptAccess) @@ -298,6 +375,8 @@ abstract Access(RuleScriptedClass) interp.access.superInstance = this; + interp.access.setVariable("this", this); + if (args != null) if (variableExists('new')) Reflect.callMethod(this, getVariable('new'), args); diff --git a/rulescript/scriptedClass/RuleScriptedClassUtil.hx b/rulescript/scriptedClass/RuleScriptedClassUtil.hx index b468427..cf7733b 100644 --- a/rulescript/scriptedClass/RuleScriptedClassUtil.hx +++ b/rulescript/scriptedClass/RuleScriptedClassUtil.hx @@ -83,6 +83,16 @@ class RuleScriptedClassUtil return cast types[typePath]; } + public static function listScriptClasses():Array + { + var result:Array = []; + for (key in types.keys()) + { + result.push(key); + } + return result; + } + public static function buildScriptedClass(cl:ScriptedClass, rulescript:RuleScript):Void { rulescript.access.setVariable('new', () -> {}); From fab6e8fec35039736972e0509ca1eae5a98853bc Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Wed, 17 Jun 2026 21:34:41 +0500 Subject: [PATCH 12/16] lol --- .gitignore | 1 + rulescript/interps/RuleScriptInterp.hx | 91 ++++++++++++++++++++++---- rulescript/parsers/HxParser.hx | 3 - 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 61c8867..d4cb69c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ hxformat.json /test/dump /.DS_Store /examples +rulescript/vscode-project.hxml diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index d07db48..f978e04 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -27,9 +27,12 @@ class RuleScriptInterp extends hscript.Interp implements IInterp public var access:RuleScriptAccess; + public var strictMode:Bool = true; + public var imports:Map = []; public var usings:Map = []; public var finalVariables:Map = []; + public var declaredVariableTypes:Map = []; public var superInstance(default, set):Dynamic; @@ -60,6 +63,7 @@ class RuleScriptInterp extends hscript.Interp implements IInterp usings = []; typePaths = []; finalVariables.clear(); + declaredVariableTypes.clear(); if (rulescript.scriptedClass.RuleScriptedClassUtil.autoWrappers != null) { @@ -138,6 +142,24 @@ class RuleScriptInterp extends hscript.Interp implements IInterp { case EIdent(id): var l:Dynamic = locals.get(id); + + if (strictMode) { + if (l == null && !variables.exists(id) && !finalVariables.exists(id) && + (context == null || (!context.staticVariables.exists(id) && !context.publicVariables.exists(id))) && + (superInstance == null || (!superFields.contains(id) && !superFields.contains('set_' + id)))) + { + throw new haxe.Exception('Strict Mode Error: Undeclared variable "$id". Did you forget to write "var $id"?'); + } + + if (declaredVariableTypes.exists(id)) { + var expected = declaredVariableTypes.get(id); + if (!checkRuntimeType(v, expected)) { + var got = Type.getClassName(Type.getClass(v)) ?? Std.string(Type.typeof(v)); + throw new haxe.Exception('Type Mismatch Error: Variable "$id" expects type $expected, but got $got'); + } + } + } + if (l == null) setVar(id, v); else @@ -442,16 +464,15 @@ class RuleScriptInterp extends hscript.Interp implements IInterp (onMeta != null) ? onMeta(n, args, e) : exprMeta(n, args, e); } - case EVar(n, _, e, global, isFinal): - if (global) - { + case EVar(n, tExpr, e, global, isFinal): + if (tExpr != null) declaredVariableTypes.set(n, rulescript.Tools.typeToString(tExpr)); + + if (global) { if (context == null || (!context.staticVariables.exists(n) && !context.publicVariables.exists(n))) { variables.set(n, (e == null) ? null : this.expr(e)); if (isFinal) finalVariables.set(n, true); } - } - else - { + } else { declared.push({n: n, old: locals.get(n)}); var ref:Dynamic = {r: (e == null) ? null : this.expr(e)}; if (isFinal) ref.isFinal = true; @@ -460,19 +481,20 @@ class RuleScriptInterp extends hscript.Interp implements IInterp return null; case EProp(n, g, s, type, e, global): + if (type != null) declaredVariableTypes.set(n, rulescript.Tools.typeToString(type)); + var prop = createScriptProperty(n, g, s, type); - if (global) - variables.set(n, prop); - else - { + if (global) variables.set(n, prop); + else { declared.push({n: n, old: locals.get(n)}); locals.set(n, {r: prop}); } - if (e != null) - prop._lazyValue = () -> this.expr(e); + if (e != null) prop._lazyValue = () -> this.expr(e); return null; + case EIdent(id): - return resolve(id); // wuh + return resolve(id); + case ECall(e, params): var args = new Array(); for (p in params) @@ -804,6 +826,9 @@ class RuleScriptInterp extends hscript.Interp implements IInterp */ override function get(o:Dynamic, f:String):Dynamic { + if (strictMode) + validateFieldAccess(o, f); + if (Tools.isEnum(o)) { if (Type.getEnumConstructs(o).contains(f)) @@ -858,6 +883,9 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function set(o:Dynamic, f:String, v:Dynamic):Dynamic { + if (strictMode) + validateFieldAccess(o, f); + if (o == null) error(EInvalidAccess(f)); @@ -945,6 +973,43 @@ class RuleScriptInterp extends hscript.Interp implements IInterp #end } + private function checkRuntimeType(value:Dynamic, expectedType:String):Bool { + if (value == null || expectedType == null || expectedType == "Dynamic" || expectedType == "Any") return true; + + switch(expectedType) { + case "Int": return Std.isOfType(value, Int); + case "Float": return Std.isOfType(value, Float) || Std.isOfType(value, Int); + case "Bool": return Std.isOfType(value, Bool); + case "String": return Std.isOfType(value, String); + default: + var cls = resolveType(expectedType); + return cls != null ? Std.isOfType(value, cls) : true; + } + } + + private function validateFieldAccess(obj:Dynamic, field:String):Void { + if (obj == null || obj == this) return; + + if (obj is RuleScriptedClass || obj is ScriptedType || obj is haxe.Constraints.IMap) return; + + final cls = Type.getClass(obj); + if (cls == null) return; + + final className = Type.getClassName(cls); + if (className == null || ["String", "Array"].contains(className)) return; + + function checkField(c:Class):Bool { + if (c == null) return false; + if (Type.getInstanceFields(c).contains(field) || Type.getClassFields(c).contains(field)) return true; + if (Type.getInstanceFields(c).contains('get_$field') || Type.getInstanceFields(c).contains('set_$field')) return true; + return checkField(Type.getSuperClass(c)); + } + + if (!checkField(cls)) { + throw new haxe.Exception('Strict Mode Error: Field "$field" does not exist on class $className'); + } + } + function caseMatch(ecase:Expr, evalue:Expr, value:Dynamic):Bool { if (value is ScriptedEnumInstance || Reflect.isEnumValue(value)) diff --git a/rulescript/parsers/HxParser.hx b/rulescript/parsers/HxParser.hx index 71ef0b7..36eae7c 100644 --- a/rulescript/parsers/HxParser.hx +++ b/rulescript/parsers/HxParser.hx @@ -220,11 +220,8 @@ class HScriptParser extends hscript.Parser override function parseString(s:String, ?origin:String = "hscript", ?position:Int = 0):Expr { isMainBlock = true; - var e = super.parseString(s, origin, position); - isMainBlock = false; - return e; } From 97f228a7ff6d0e7ba94a05bfe76578cf829de0a0 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Wed, 17 Jun 2026 23:59:33 +0500 Subject: [PATCH 13/16] bruh --- rulescript/macro/RuleScriptedClassMacro.hx | 4 ++-- rulescript/scriptedClass/RuleScriptedClass.hx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rulescript/macro/RuleScriptedClassMacro.hx b/rulescript/macro/RuleScriptedClassMacro.hx index d0742d4..d55c9bc 100644 --- a/rulescript/macro/RuleScriptedClassMacro.hx +++ b/rulescript/macro/RuleScriptedClassMacro.hx @@ -187,7 +187,7 @@ class RuleScriptedClassMacro try { __rulescript.access.getVariable($v{fieldName})($a{fieldArgs}); } catch (e:Dynamic) { - trace("[RuleScript] Error: Purging broken function '" + $v{fieldName} + "' to prevent soft-lock -> " + e); + trace("Scripted Class Error: Purging broken function '" + $v{fieldName} + "' to prevent soft-lock: " + e); __rulescript.variables.remove($v{fieldName}); super.$fieldName($a{fieldArgs}); } @@ -199,7 +199,7 @@ class RuleScriptedClassMacro try { return cast __rulescript.access.getVariable($v{fieldName})($a{fieldArgs}); } catch (e:Dynamic) { - trace("[RuleScript] Error: Purging broken function '" + $v{fieldName} + "' to prevent soft-lock -> " + e); + trace("Scripted Class Error: Purging broken function '" + $v{fieldName} + "' to prevent soft-lock: " + e); __rulescript.variables.remove($v{fieldName}); return cast super.$fieldName($a{fieldArgs}); } diff --git a/rulescript/scriptedClass/RuleScriptedClass.hx b/rulescript/scriptedClass/RuleScriptedClass.hx index 972401c..bbd650d 100644 --- a/rulescript/scriptedClass/RuleScriptedClass.hx +++ b/rulescript/scriptedClass/RuleScriptedClass.hx @@ -289,11 +289,11 @@ abstract Access(RuleScriptedClass) return Type.createInstance(wrapperClass, [scriptName, args ?? []]); } } catch(e:Dynamic) { - trace('[RuleScript] Error: Failed to create wrapper for ' + shortName + ' -> ' + e); + trace('Scripted Class Error: Failed to create wrapper for "' + shortName + '": ' + e); return null; } } else { - trace('[RuleScript] Warning: Wrapper for ' + shortName + ' not found in registry. It might have been removed by Dead Code Elimination (DCE).'); + trace('Scripted Class Warning: Wrapper for "' + shortName + '" not found in registry. It might have been removed by Dead Code Elimination (DCE).'); } } } @@ -302,7 +302,7 @@ abstract Access(RuleScriptedClass) try { return Type.createInstance(nativeClass, args ?? []); } catch(e:Dynamic) { - trace('[RuleScript] Error: Failed to create native fallback class for ' + toString() + ' -> ' + e); + trace('Scripted Class Error: Failed to create native fallback class for "' + toString() + '": ' + e); } } From b3e61a81222fcb230fe8b0ac6fd733196762a9c3 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Thu, 18 Jun 2026 01:38:07 +0500 Subject: [PATCH 14/16] blyadi --- rulescript/macro/RuleScriptedClassMacro.hx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rulescript/macro/RuleScriptedClassMacro.hx b/rulescript/macro/RuleScriptedClassMacro.hx index d55c9bc..89109b6 100644 --- a/rulescript/macro/RuleScriptedClassMacro.hx +++ b/rulescript/macro/RuleScriptedClassMacro.hx @@ -109,7 +109,8 @@ class RuleScriptedClassMacro 'variableExists' => macro function(name:String):Bool { return __rulescript?.variables.exists(name) || Reflect.hasField(this, name) || Reflect.getProperty(this, name) != null; }, 'getVariable' => macro function(name:String):Dynamic { if (__rulescript.variables.exists(name)) return __rulescript.variables[name]; return Reflect.getProperty(this, name); }, 'setVariable' => macro function(name:String, value:Dynamic):Dynamic { try { if (__rulescript.variables.exists(name) || (Reflect.getProperty(this, name) == null && !Reflect.hasField(this, name))) { return __rulescript.variables[name] = value; } Reflect.setProperty(this, name, value); return value; } catch(e:Dynamic) { return __rulescript.variables[name] = value; } }, - 'get___rulescript_type' => macro function():rulescript.types.ScriptedType.TypeID { return rulescript.types.ScriptedType.TypeID.CLASS; } + 'get___rulescript_type' => macro function():rulescript.types.ScriptedType.TypeID { return rulescript.types.ScriptedType.TypeID.CLASS; }, + '__super_new' => macro function(...args:Dynamic):Void {} ]; for (name => func in functions) From 7995c6cd291558858ccc52a8c408ef02427afd0e Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Thu, 18 Jun 2026 03:17:32 +0500 Subject: [PATCH 15/16] hueta --- rulescript/parsers/HxParser.hx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rulescript/parsers/HxParser.hx b/rulescript/parsers/HxParser.hx index 36eae7c..06a665b 100644 --- a/rulescript/parsers/HxParser.hx +++ b/rulescript/parsers/HxParser.hx @@ -1716,6 +1716,12 @@ class HScriptParser extends hscript.Parser while (true) { var tk = token(); + + if (tk == TApostr) { + parseStringInterpolation(); + continue; + } + if (preprocStack[spos] != obj) { push(tk); From 24ffd9872f62c70c8d83e125208d33c8811b8ab6 Mon Sep 17 00:00:00 2001 From: GreenColdTea <142627750+GreenColdTea@users.noreply.github.com> Date: Sat, 20 Jun 2026 00:34:01 +0500 Subject: [PATCH 16/16] smth --- rulescript/interps/RuleScriptInterp.hx | 139 +++++++++++++++++++++++-- rulescript/parsers/HxParser.hx | 130 +++++++++++++++++++++++ rulescript/types/ScriptedAbstract.hx | 87 ++++++++++++++-- 3 files changed, 335 insertions(+), 21 deletions(-) diff --git a/rulescript/interps/RuleScriptInterp.hx b/rulescript/interps/RuleScriptInterp.hx index ad499cd..24f4174 100644 --- a/rulescript/interps/RuleScriptInterp.hx +++ b/rulescript/interps/RuleScriptInterp.hx @@ -89,8 +89,50 @@ class RuleScriptInterp extends hscript.Interp implements IInterp override function initOps() { super.initOps(); - binops.set("??", (e1, e2) -> this.expr(e1) ?? this.expr(e2)); - assignOp("??=", function(v1:Dynamic, v2:Dynamic) return v1 ?? v2); + + var me = this; + + function resolveOp(op:String, v1:Dynamic, v2:Dynamic):Dynamic { + if (v1 is rulescript.types.ScriptedAbstract.ScriptedAbstractInstance) { + var inst = cast(v1, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance); + if (inst.impl.hasOperator(op)) return inst.impl.callOperator(op, inst, v2, false); + } + if (v2 is rulescript.types.ScriptedAbstract.ScriptedAbstractInstance) { + var inst = cast(v2, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance); + if (inst.impl.hasOperator(op)) return inst.impl.callOperator(op, inst, v1, true); + } + return null; + } + + binops.set("+", function(e1, e2):Dynamic { + var v1:Dynamic = me.expr(e1); + var v2:Dynamic = me.expr(e2); + var res:Dynamic = resolveOp("+", v1, v2); + if (res != null) return res; + if (Std.isOfType(v1, String) || Std.isOfType(v2, String)) return Std.string(v1) + Std.string(v2); + return v1 + v2; + }); + + binops.set("-", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("-", v1, v2); if (res != null) return res; return v1 - v2; }); + binops.set("*", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("*", v1, v2); if (res != null) return res; return v1 * v2; }); + binops.set("/", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("/", v1, v2); if (res != null) return res; return v1 / v2; }); + binops.set("%", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("%", v1, v2); if (res != null) return res; return v1 % v2; }); + + binops.set("==", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("==", v1, v2); if (res != null) return res; return v1 == v2; }); + binops.set("!=", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("!=", v1, v2); if (res != null) return res; return v1 != v2; }); + binops.set(">", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp(">", v1, v2); if (res != null) return res; return v1 > v2; }); + binops.set("<", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("<", v1, v2); if (res != null) return res; return v1 < v2; }); + binops.set(">=", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp(">=", v1, v2); if (res != null) return res; return v1 >= v2; }); + binops.set("<=", function(e1, e2):Dynamic { var v1:Dynamic = me.expr(e1); var v2:Dynamic = me.expr(e2); var res:Dynamic = resolveOp("<=", v1, v2); if (res != null) return res; return v1 <= v2; }); + + assignOp("+=", function(v1:Dynamic, v2:Dynamic):Dynamic { + var res:Dynamic = resolveOp("+", v1, v2); + if (res != null) return res; + if (Std.isOfType(v1, String) || Std.isOfType(v2, String)) return Std.string(v1) + Std.string(v2); + return v1 + v2; + }); + + assignOp("-=", function(v1:Dynamic, v2:Dynamic):Dynamic { var res:Dynamic = resolveOp("-", v1, v2); if (res != null) return res; return v1 - v2; }); } override function resolve(id:String):Dynamic @@ -140,6 +182,11 @@ class RuleScriptInterp extends hscript.Interp implements IInterp switch (hscript.Tools.expr(e1)) { case EIdent(id): + if (id == "this" && superInstance != null && Std.isOfType(superInstance, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance)) { + cast(superInstance, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance).value = v; + return v; + } + var l:Dynamic = locals.get(id); if (strictMode) { @@ -190,6 +237,10 @@ class RuleScriptInterp extends hscript.Interp implements IInterp case EArray(e, index): var arr:Dynamic = expr(e); var index:Dynamic = expr(index); + + if (Std.isOfType(arr, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance)) + arr = cast(arr, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance).value; + if (isMap(arr)) { setMapValue(arr, index, v); @@ -242,6 +293,10 @@ class RuleScriptInterp extends hscript.Interp implements IInterp case EArray(e, index): var arr:Dynamic = expr(e); var index:Dynamic = expr(index); + + if (Std.isOfType(arr, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance)) + arr = cast(arr, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance).value; + if (isMap(arr)) { v = fop(getMapValue(arr, index), expr(e2)); @@ -312,6 +367,10 @@ class RuleScriptInterp extends hscript.Interp implements IInterp case EArray(e, index): var arr:Dynamic = expr(e); var index:Dynamic = expr(index); + + if (Std.isOfType(arr, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance)) + arr = cast(arr, rulescript.types.ScriptedAbstract.ScriptedAbstractInstance).value; + if (isMap(arr)) { var v = getMapValue(arr, index); @@ -469,6 +528,29 @@ class RuleScriptInterp extends hscript.Interp implements IInterp } null; + case ':multiCatch': + final errInfo = locals.get("__err__"); + final err = errInfo?.r ?? null; + + @:privateAccess + final unwrappedErr = Std.isOfType(err, haxe.Exception) ? cast(err, haxe.Exception).unwrap() : err; + + for (catchNode in args) { + switch (catchNode.getExpr()) { + case EFunction(fargs, catchExpr, _, _): + final cname = fargs[0].name; + final expectedTypeStr = fargs[0].t != null ? rulescript.Tools.typeToString(fargs[0].t) : "Dynamic"; + + if (checkRuntimeType(unwrappedErr, expectedTypeStr)) { + declared.push({n: cname, old: locals.get(cname)}); + locals.set(cname, {r: unwrappedErr}); + return this.expr(catchExpr); + } + default: + } + } + #if hl hl.Api.rethrow(err); #else throw err; #end + default: (onMeta != null) ? onMeta(n, args, e) : exprMeta(n, args, e); } @@ -704,20 +786,27 @@ class RuleScriptInterp extends hscript.Interp implements IInterp var match = false; for (c in cases) { - var old = declared.length; + final old = declared.length; - for (v in c.values) + final isGuard = c.expr != null && c.expr.getExpr().match(EMeta(":guard", _, _)); + final guardCond = isGuard ? switch(c.expr.getExpr()) { case EMeta(_, args, _): args[0]; default: null; } : null; + final actualExpr = isGuard ? switch(c.expr.getExpr()) { case EMeta(_, _, e): e; default: null; } : c.expr; + + for (v in c.values) { if (caseMatch(v, e, val)) { - match = true; - break; + if (guardCond == null || this.expr(guardCond) == true) { + match = true; + break; + } } - else - restore(old); + restore(old); + } if (match) { - val = this.expr(c.expr); + val = this.expr(actualExpr); + restore(old); break; } @@ -971,7 +1060,20 @@ class RuleScriptInterp extends hscript.Interp implements IInterp case CLASS: return cast(c, ScriptedClass).createInstance(args); case ABSTRACT: - return cast(c, ScriptedAbstract).createInstance(args); + final inst = cast(c, ScriptedAbstract).createInstance(args); + + final ctor = inst.getVariable("new"); + if (ctor != null) { + final oldSuper = this.superInstance; + + this.superInstance = inst; + + Reflect.callMethod(inst, ctor, args); + + this.superInstance = oldSuper; + } + + return inst; default: } @@ -1021,6 +1123,23 @@ class RuleScriptInterp extends hscript.Interp implements IInterp function caseMatch(ecase:Expr, evalue:Expr, value:Dynamic):Bool { + if (ecase != null) { + switch (ecase.getExpr()) { + case EIdent("_"): + return true; + case EIdent(id): + if (id != "true" && id != "false" && id != "null") { + final charCode = id.charCodeAt(0); + if (charCode >= 97 && charCode <= 122) { + declared.push({n: id, old: locals.get(id)}); + locals.set(id, {r: value}); + return true; + } + } + default: + } + } + if (value is ScriptedEnumInstance || Reflect.isEnumValue(value)) { final isEnumValue:Bool = Reflect.isEnumValue(value); diff --git a/rulescript/parsers/HxParser.hx b/rulescript/parsers/HxParser.hx index 06a665b..6c6d498 100644 --- a/rulescript/parsers/HxParser.hx +++ b/rulescript/parsers/HxParser.hx @@ -689,6 +689,136 @@ class HScriptParser extends hscript.Parser parseContext(id, false); case 'static' if (mode == DEFAULT && allowStaticVariables): parseContext(id, true); + case "switch": + var e = parseExpr(); + var def = null, cases = []; + ensure(TBrOpen); + while (true) + { + var tk = token(); + switch (tk) + { + case TId("case"): + var c:Dynamic = {values: [], expr: null}; + cases.push(c); + + var guardCond:Expr = null; + + while (true) + { + var e = parseExpr(); + c.values.push(e); + tk = token(); + switch (tk) + { + case TComma: + case TId("if"): + ensure(TPOpen); + guardCond = parseExpr(); + ensure(TPClose); + ensure(TDoubleDot); + break; + case TDoubleDot: + break; + default: + unexpected(tk); + break; + } + if (tk == TDoubleDot || Type.enumEq(tk, TId("if"))) break; + } + + var exprs = []; + while (true) + { + tk = token(); + push(tk); + switch (tk) + { + case TId("case"), TId("default"), TBrClose: + break; + case TEof if (resumeErrors): + break; + default: + parseFullExpr(exprs); + } + } + + var caseExpr = if (exprs.length == 1) + exprs[0]; + else if (exprs.length == 0) + mk(EBlock([]), tokenMin, tokenMin); + else + mk(EBlock(exprs), pmin(exprs[0]), pmax(exprs[exprs.length - 1])); + + if (guardCond != null) + caseExpr = mk(EMeta(":guard", [guardCond], caseExpr), pmin(caseExpr), pmax(caseExpr)); + c.expr = caseExpr; + + case TId("default"): + if (def != null) unexpected(tk); + ensure(TDoubleDot); + var exprs = []; + while (true) + { + tk = token(); + push(tk); + switch (tk) + { + case TId("case"), TId("default"), TBrClose: + break; + case TEof if (resumeErrors): + break; + default: + parseFullExpr(exprs); + } + } + def = if (exprs.length == 1) + exprs[0]; + else if (exprs.length == 0) + mk(EBlock([]), tokenMin, tokenMin); + else + mk(EBlock(exprs), pmin(exprs[0]), pmax(exprs[exprs.length - 1])); + case TBrClose: + break; + default: + unexpected(tk); + break; + } + } + mk(ESwitch(e, cases, def), p1, tokenMax); + + case "try": + var e = parseExpr(); + var catches = []; + + while (true) { + var tk = token(); + if (!Type.enumEq(tk, TId("catch"))) { + push(tk); + break; + } + ensure(TPOpen); + + var vname = getIdent(); + ensure(TDoubleDot); + + var t = allowTypes ? parseType() : null; + if (!allowTypes) ensureToken(TId("Dynamic")); + + ensure(TPClose); + + var ec = parseExpr(); + catches.push(mk(EFunction([{name: vname, t: t}], ec, null, null))); + } + + if (catches.length == 0) unexpected(TId("try")); + + if (catches.length == 1) { + var f = switch (catches[0].getExpr()) { case EFunction(args, expr, _, _): {n: args[0].name, t: args[0].t, e: expr}; default: null; }; + return mk(ETry(e, f.n, f.t, f.e), p1, tokenMax); + } else { + return mk(ETry(e, "__err__", null, mk(EMeta(":multiCatch", catches, mk(EBlock([]))))), p1, tokenMax); + } default: super.parseStructure(id); } diff --git a/rulescript/types/ScriptedAbstract.hx b/rulescript/types/ScriptedAbstract.hx index 2744a72..da3436c 100644 --- a/rulescript/types/ScriptedAbstract.hx +++ b/rulescript/types/ScriptedAbstract.hx @@ -11,6 +11,8 @@ class ScriptedAbstract implements ScriptedType public var module:ScriptedModule; public var impl:AbstractDecl; + var opMap:Map; + var pack:String; public function new(impl:AbstractDecl, module:ScriptedModule) @@ -74,6 +76,53 @@ class ScriptedAbstract implements ScriptedType { return new ScriptedAbstractInstance(this, args); } + + function buildOpMap() { + if (opMap != null) return; + opMap = new Map(); + + for (field in impl.fields) { + if (field.meta != null) { + for (m in field.meta) { + if (m.name == ":op" || m.name == "op") { + if (m.params != null && m.params.length > 0) { + var exprDef = rulescript.Tools.getExpr(m.params[0]); + var opStr = null; + + switch (exprDef) { + case EBinop(o, _, _), EUnop(o, _, _): opStr = o; + default: + } + + if (opStr != null) { + var isStatic = field.access != null && field.access.contains(AStatic); + opMap.set(opStr, {field: field.name, isStatic: isStatic}); + } + } + } + } + } + } + } + + public function hasOperator(op:String):Bool { + if (opMap == null) buildOpMap(); + return opMap.exists(op); + } + + public function callOperator(op:String, a:Dynamic, b:Dynamic, isRight:Bool):Dynamic { + if (opMap == null) buildOpMap(); + + final opData = opMap.get(op); + if (opData == null) throw new haxe.Exception('Operator overload for "$op" not found in abstract ${impl.name}'); + + final inst:ScriptedAbstractInstance = cast a; + + final func:Dynamic = __impl.getVariable(opData.field); + if (func == null) throw new haxe.Exception('Function "${opData.field}" for operator "$op" is null or not found.'); + + return opData.isStatic ? Reflect.callMethod(null, func, isRight ? [b, inst] : [inst, b]) : Reflect.callMethod(inst, func, [b]); + } } @:noBuild @@ -89,29 +138,45 @@ class ScriptedAbstractInstance implements RuleScriptedClass return ABSTRACT; } - public function new(impl:ScriptedAbstract, value:Dynamic) + public function new(impl:ScriptedAbstract, args:Array) { this.impl = impl; - this.value = value; } - public function getVariables():Map - { - return null; - } + public function getVariables():Map return null; public function variableExists(name:String):Bool { - return impl.__impl.variableExists(name); + if (impl.__impl.variableExists(name)) return true; + if (value != null) { + try { return Reflect.hasField(value, name) || Reflect.getProperty(value, name) != null; } catch(e:Dynamic) {} + } + return false; } public function getVariable(name:String):Dynamic { - return impl.__impl.getVariable(name); + if (impl.__impl.variableExists(name)) return impl.__impl.getVariable(name); + + if (value != null) { + try { return Reflect.getProperty(value, name); } catch(e:Dynamic) {} + } + return null; } - public function setVariable(name:String, value:Dynamic):Dynamic + public function setVariable(name:String, val:Dynamic):Dynamic { - return null; + if (impl.__impl.variableExists(name)) { + var field = impl.__impl.getVariable(name); + if (field is rulescript.types.Property) { + cast(field, rulescript.types.Property).value = val; + return val; + } + } + + if (value != null) { + try { Reflect.setProperty(value, name, val); } catch(e:Dynamic) {} + } + return val; } -} +} \ No newline at end of file