From bac9f1ea7e33e07384d45fa365fd22b6717ff259 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Sat, 8 Nov 2025 20:25:55 -0700 Subject: [PATCH 01/13] Full Class Parsing --- hscript/Ast.hx | 15 +++++++ hscript/Interp.hx | 2 + hscript/Lexer.hx | 7 ++- hscript/Parser.hx | 79 +++++++++++++++++++++++++++++++++- hscript/anaylzers/ConstEval.hx | 2 + hscript/anaylzers/Inliner.hx | 2 + hscript/anaylzers/Unravel.hx | 9 ++-- hscript/utils/ExprPrinter.hx | 16 +++++++ hscript/utils/ExprUtils.hx | 4 ++ 9 files changed, 130 insertions(+), 6 deletions(-) diff --git a/hscript/Ast.hx b/hscript/Ast.hx index 3750749..daf8a73 100644 --- a/hscript/Ast.hx +++ b/hscript/Ast.hx @@ -60,6 +60,7 @@ enum ExprDef { EDoWhile(cond:Expr, body:Expr); EMeta(name:String, args:Array, expr:Expr); EImport(path:String, mode:EImportMode); + EClass(name:VariableType, decl:ClassDecl); EInfo(info:VariableInfo, expr:Expr); EEmpty; @@ -276,6 +277,20 @@ enum EImportMode { All; // import haxe.*; } +class ClassDecl { + public var name:String; + public var extend:Null; + public var implement:Array; + public var body:Expr; // Will be always EInfo along with a EBlock + + public function new(name:String, ?extend:String, implement:Array, body:Expr) { + this.name = name; + this.extend = extend; + this.implement = implement; + this.body = body; + } +} + typedef VariableType = Int; typedef VariableInfo = Array; diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 2ca0ad3..6a38286 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -446,6 +446,8 @@ class Interp extends ScriptRuntime { var importValue:Dynamic = interpImport(path, mode); if (importValue == null) error(EInvalidClass(path), expr.line); return importValue; + case EClass(name, decl): + null; // TODO case EInfo(info, _): error(ECustom("Invalid EInfo()"), expr.line); case EEmpty: null; } diff --git a/hscript/Lexer.hx b/hscript/Lexer.hx index 0298d23..f60928a 100644 --- a/hscript/Lexer.hx +++ b/hscript/Lexer.hx @@ -186,10 +186,12 @@ enum abstract LKeyword(String) from String { var CASE:LKeyword = "case"; var CAST:LKeyword = "cast"; var CATCH:LKeyword = "catch"; + var HCLASS:LKeyword = "class"; var CONTINUE:LKeyword = "continue"; var DEFAULT:LKeyword = "default"; var DO:LKeyword = "do"; var ELSE:LKeyword = "else"; + var HEXTENDS:LKeyword = "extends"; var FALSE:LKeyword = "false"; var FINAL:LKeyword = "final"; var FOR:LKeyword = "for"; @@ -198,6 +200,7 @@ enum abstract LKeyword(String) from String { var HIMPORT:LKeyword = "import"; // HIMPORT to prevent conflicts with lua libs var IN:LKeyword = "in"; var HINLINE:LKeyword = "inline"; // HINLINE to prevent conflicts with lua libs + var HIMPLEMENTS:LKeyword = "implements"; var NEW:LKeyword = "new"; var NULL:LKeyword = "null"; var OVERRIDE:LKeyword = "override"; @@ -214,8 +217,8 @@ enum abstract LKeyword(String) from String { var WHILE:LKeyword = "while"; public static final ALL_KEYWORDS:Array = [ - AS, BREAK, CASE, CAST, CATCH, CONTINUE, DEFAULT, DO, ELSE, - FALSE, FINAL, FOR, FUNCTION, IF, HIMPORT, IN, HINLINE, + AS, BREAK, CASE, CAST, CATCH, HCLASS, CONTINUE, DEFAULT, DO, ELSE, HEXTENDS, + FALSE, FINAL, FOR, FUNCTION, IF, HIMPORT, IN, HINLINE, HIMPLEMENTS, NEW, NULL, OVERRIDE, PRIVATE, PUBLIC, RETURN, STATIC, SWITCH, THIS, THROW, TRUE, TRY, VAR, WHILE ]; diff --git a/hscript/Parser.hx b/hscript/Parser.hx index 8d723c6..f3578de 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -1,5 +1,6 @@ package hscript; +import hscript.Ast.ClassDecl; import hscript.utils.ExprUtils; import hscript.Interp.StaticInterp; import haxe.ds.StringMap; @@ -564,6 +565,8 @@ class Parser { } create(EImport(identifiers.join("."), mode)); + case HCLASS: create(parseClass()); + case HEXTENDS | HIMPLEMENTS: unexpected(); // if used outside of the class declaration case THROW: create(EThrow(parseExpr())); default: // parse keywords like null and static as variables because they are used like that sometimes -lunar @@ -595,7 +598,7 @@ class Parser { } } - private function parseClassName():String { // haxe.Unserializer + private function parseClassName(c:Bool = false):String { // haxe.Unserializer var identifiers:Array = []; identifiers.push(parseIdent()); @@ -603,6 +606,7 @@ class Parser { switch (readToken()) { case LTDot: identifiers.push(parseIdent()); case LTOp(LT): parseClassArgs(); // Class args + case LTOpenCB if(c): reverseToken(); break; case LTOpenP: break; default: unexpected(); break; } @@ -949,6 +953,78 @@ class Parser { return parseNextExpr(create(EObject(fields))); } + private function parseClass():ExprDef { + var className:String = parseIdent(); + if(maybe(LTOp(LT))) // class MyClass + parseClassArgs(); + + var extend:Null = null; + if(maybe(LTKeyWord(HEXTENDS))) + extend = parseClassName(true); + + var implement:Array = []; + if(maybe(LTKeyWord(HIMPLEMENTS))) { + // handled manually for multiple implements + while(true) { + switch(readToken()) { + case LTKeyWord(HIMPLEMENTS): continue; + case LTIdentifier(id): + var ident:Array = []; + ident.push(id); + + while(true) { + switch(readToken()) { + case LTDot: ident.push(parseIdent()); + case LTOp(LT): parseClassArgs(); + default: + reverseToken(); + break; + } + } + + implement.push(ident.join(".")); + default: + reverseToken(); + break; + } + } + } + + ensure(LTOpenCB); + + var oldFileName:String = this.fileName; + var oldVariablesList:Array = this.variablesList.copy(); + var oldUniqueID:Int = uniqueID; + + this.fileName = oldFileName.length == 0 ? className : '${haxe.io.Path.withoutExtension(oldFileName)}.$className'; // myScript.MyClass + this.variablesList.resize(0); + this.uniqueID = 0; + + var fieldsExpr:Array = []; + parseClassFields(fieldsExpr); + + var clsDecl:ClassDecl = new ClassDecl(className, extend, implement, create(EInfo(variablesList, create(EBlock(fieldsExpr))))); + + this.fileName = oldFileName; + this.variablesList = oldVariablesList; + this.uniqueID = oldUniqueID; + + return EClass(variableID(className), clsDecl); + } + + private function parseClassFields(fields:Array) { + while(true) { + switch(readToken()) { + case LTKeyWord(PUBLIC) | LTKeyWord(STATIC) | LTKeyWord(HINLINE) | LTKeyWord(OVERRIDE) | LTKeyWord(FUNCTION) | LTKeyWord(VAR) | LTKeyWord(FINAL): + reverseToken(); + fields.push(parseExpr()); + case LTSemiColon: continue; + case LTCloseCB: break; + default: unexpected(); + } + } + } + private function functionExpr(expr:Expr):Expr { return switch (expr.expr) { case EObject([]): return new Expr(EBlock([]), expr.line); @@ -1000,6 +1076,7 @@ class Parser { case EReturn(expr): expr != null && isBlock(expr); case ETry(_, _, expr): isBlock(expr); case EMeta(_, _, expr): isBlock(expr); + case EClass(_, _): true; default: false; } } diff --git a/hscript/anaylzers/ConstEval.hx b/hscript/anaylzers/ConstEval.hx index bd105e5..156e0e9 100644 --- a/hscript/anaylzers/ConstEval.hx +++ b/hscript/anaylzers/ConstEval.hx @@ -1,5 +1,6 @@ package hscript.anaylzers; +import hscript.Ast.ClassDecl; import hscript.Ast.VariableInfo; import hscript.Ast.SwitchCase; import hscript.Ast.ObjectField; @@ -40,6 +41,7 @@ using hscript.Ast.ExprBinop; case EDoWhile(cond, body): EDoWhile(guarantee(cond != null ? eval(cond, vars) : null), guarantee(body != null ? eval(body, vars) : null)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) guarantee(arg != null ? eval(arg, vars) : null)], guarantee(expr != null ? eval(expr, vars) : null)); case EInfo(info, expr): EInfo(info, guarantee(expr != null ? eval(expr, info) : null)); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, vars))); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; case ENew(className, args): var mapIndexes:Array = vars == null ? [] : [ diff --git a/hscript/anaylzers/Inliner.hx b/hscript/anaylzers/Inliner.hx index 4b07b0f..1633aef 100644 --- a/hscript/anaylzers/Inliner.hx +++ b/hscript/anaylzers/Inliner.hx @@ -1,5 +1,6 @@ package hscript.anaylzers; +import hscript.Ast.ClassDecl; import hscript.Ast.Argument; import hscript.Ast.ObjectField; import hscript.Ast.SwitchCase; @@ -38,6 +39,7 @@ using hscript.utils.ExprUtils; case EDoWhile(cond, body): EDoWhile(eval(cond, vars), eval(body, vars)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) eval(arg, vars)], eval(expr, vars)); case EInfo(info, expr): EInfo(info, eval(expr, info)); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, vars))); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; case EUnop(op, isPrefix, expr): EUnop(op, isPrefix, eval(expr, vars)); case EIf(cond, thenExpr, elseExpr): EIf(eval(cond, vars), eval(thenExpr, vars), elseExpr != null ? eval(elseExpr, vars) : null); diff --git a/hscript/anaylzers/Unravel.hx b/hscript/anaylzers/Unravel.hx index a82c960..f8fe27d 100644 --- a/hscript/anaylzers/Unravel.hx +++ b/hscript/anaylzers/Unravel.hx @@ -1,5 +1,6 @@ package hscript.anaylzers; +import hscript.Ast.ClassDecl; import hscript.Ast.SwitchCase; import hscript.Ast.ObjectField; import hscript.Ast.Argument; @@ -10,12 +11,13 @@ using hscript.utils.ExprUtils; @:nullSafety(Strict) class Unravel { public static final SAFE_FOR_UNLOOP:Int = 16; - public static function eval(expr:Expr):Expr { + public static function eval(expr:Expr, cl:Bool = false):Expr { return new Expr(switch (expr.expr) { case EVar(name, init, isPublic, isStatic): EVar(name, if (init != null) eval(init) else null, isPublic, isStatic); case EParent(expr): EParent(eval(expr)); case EBlock(exprs): - if (exprs.length == 1 && exprs[0] != null) return exprs[0]; + // forces to use EBlock for class declarations + if (exprs.length == 1 && !cl && exprs[0] != null) return exprs[0]; // Stop code after returns from being in the AST var optimizedExprs:Array = []; @@ -99,7 +101,8 @@ using hscript.utils.ExprUtils; ); case EDoWhile(cond, body): EDoWhile(eval(cond), eval(body)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) eval(arg)], eval(expr)); - case EInfo(info, expr): EInfo(info, eval(expr)); + case EInfo(info, expr): EInfo(info, eval(expr, cl)); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, true))); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; case EUnop(op, isPrefix, expr): EUnop(op, isPrefix, eval(expr)); case EIf(cond, thenExpr, elseExpr): EIf(eval(cond), eval(thenExpr), elseExpr != null ? eval(elseExpr) : null); diff --git a/hscript/utils/ExprPrinter.hx b/hscript/utils/ExprPrinter.hx index b8896b5..aa93b72 100644 --- a/hscript/utils/ExprPrinter.hx +++ b/hscript/utils/ExprPrinter.hx @@ -319,6 +319,22 @@ class ExprPrinter { add(".*"); default: } + case EClass(_, decl): + add('class ${decl.name} '); + if(decl.extend != null) + add('extends ${decl.extend} '); + if(decl.implement.length > 0) + for(i in decl.implement) + add('implements $i '); + switch(decl.body.expr) { + case EInfo(info, expr): + var oldVariableName = variableNames.copy(); + loadTables(info); + printExpr(expr); + this.variableNames = oldVariableName; + default: + add("{}"); + } case EInfo(_, _): add(""); case EEmpty: diff --git a/hscript/utils/ExprUtils.hx b/hscript/utils/ExprUtils.hx index f465f5c..87466e0 100644 --- a/hscript/utils/ExprUtils.hx +++ b/hscript/utils/ExprUtils.hx @@ -1,5 +1,6 @@ package hscript.utils; +import hscript.Ast.ClassDecl; import hscript.Ast.SwitchCase; import hscript.Ast.ObjectField; import hscript.Ast.Argument; @@ -39,6 +40,7 @@ import hscript.Ast.Expr; case EDoWhile(cond, body): iterate(cond, iter); iterate(body, iter); case EMeta(name, args, expr): for (arg in args) {iterate(arg, iter);} iterate(expr, iter); case EInfo(info, expr): iterate(expr, iter); + case EClass(name, decl): iterate(decl.body, iter); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: // Cause compilier error if these arent updated along with Ast.hx -lunar } } @@ -76,6 +78,7 @@ import hscript.Ast.Expr; case EDoWhile(cond, body): transverse(cond, iter); transverse(body, iter); case EMeta(name, args, expr): for (arg in args) {transverse(arg, iter);} transverse(expr, iter); case EInfo(info, expr): transverse(expr, iter); + case EClass(name, decl): transverse(decl.body, iter); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: } } @@ -113,6 +116,7 @@ import hscript.Ast.Expr; case EDoWhile(cond, body): EDoWhile(map(cond, iter), map(body, iter)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) map(arg, iter)], map(expr, iter)); case EInfo(info, expr): EInfo(info, map(expr, iter)); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, map(decl.body, iter))); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; }, expr.line) else null; From 0a67217e8e79f73bca18154d2b12217b1a20e747 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Wed, 17 Dec 2025 13:05:00 -0700 Subject: [PATCH 02/13] custom class progress NOT READY TO USE --- hscript/Interp.hx | 34 ++++++++++++------- hscript/misc/classes/ClassHandler.hx | 47 ++++++++++++++++++++++++++ hscript/misc/classes/ClassInterp.hx | 18 ++++++++++ hscript/misc/classes/Instance.hx | 16 +++++++++ hscript/misc/classes/InstanceInterp.hx | 18 ++++++++++ 5 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 hscript/misc/classes/ClassHandler.hx create mode 100644 hscript/misc/classes/ClassInterp.hx create mode 100644 hscript/misc/classes/Instance.hx create mode 100644 hscript/misc/classes/InstanceInterp.hx diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 6a38286..74226e2 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -212,6 +212,20 @@ class ScriptRuntime { return ref.r; } + private function declarePublic(name:VariableType, value:Dynamic):Bool { + if(publicVariables == null) return false; + var varName:String = variableNames[name]; + publicVariables.set(varName, value); + return true; + } + + private function declareStatic(name:VariableType, value:Dynamic):Bool { + var varName:String = variableNames[name]; + if(StaticInterp.staticVariables.exists(varName)) return false; + StaticInterp.staticVariables.set(varName, value); + return true; + } + private inline function assign(name:VariableType, value:Dynamic):Dynamic { variablesDeclared[name] = true; variablesValues[name].r = value; @@ -322,15 +336,10 @@ class Interp extends ScriptRuntime { case EIdent(name): if (variablesDeclared[name]) variablesValues[name].r; else resolveGlobal(name); case EVar(name, init, isPublic, isStatic): if (depth == 0) { - var varName:String = variableNames[name]; - if (isStatic && !StaticInterp.staticVariables.exists(varName)) { - StaticInterp.staticVariables.set(varName, interpExpr(init)); + if (isStatic && declareStatic(name, interpExpr(init))) return null; - } - if (isPublic && publicVariables != null) { - publicVariables.set(varName, interpExpr(init)); + if (isPublic && declarePublic(name, interpExpr(init))) return null; - } } declare(name, init == null ? null : interpExpr(init)); return null; @@ -602,13 +611,12 @@ class Interp extends ScriptRuntime { functionRef.r = reflectiveFunction; if (name != -1) { if (depth == 0) { - var varName:String = variableNames[name]; - if (isStatic && !StaticInterp.staticVariables.exists(varName)) { - StaticInterp.staticVariables.set(varName, reflectiveFunction); + if (isStatic) { + declareStatic(name, reflectiveFunction); return reflectiveFunction; } - if (isPublic && publicVariables != null) { - publicVariables.set(variableNames[name], reflectiveFunction); + if (isPublic) { + declarePublic(name, reflectiveFunction); return reflectiveFunction; } } @@ -1077,4 +1085,4 @@ class InterpLocalsImpl { public inline function exists(key:String):Bool { return parent.variablesLookup != null && parent.variablesLookup.exists(key) && parent.variablesDeclared[parent.variablesLookup.get(key)]; } -} \ No newline at end of file +} diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx new file mode 100644 index 0000000..24f9e80 --- /dev/null +++ b/hscript/misc/classes/ClassHandler.hx @@ -0,0 +1,47 @@ +package hscript.misc.classes; + +import hscript.Ast; +import hscript.Ast.ClassDecl; +import hscript.Interp; +import hscript.Ast.IHScriptCustomBehaviour; + +/** + * Provides handlers for static class fields and instantiation. + */ +class ClassHandler implements IHScriptCustomBehaviour { + + public static function createInstance(cl:ClassHandler, args:Array) { + return cl.create(args); + } + + public final name:String; + + private var module:ScriptRuntime; + private var classInterp:Interp; + + public function new(clsDecl:ClassDecl, module:ScriptRuntime) { + this.module = module; + this.name = clsDecl.name; + build(clsDecl); + } + + private function build(clsDecl:ClassDecl) { + + } + + private function create(args:Array):Dynamic { + return null; + } + + public function hget(field:String):Dynamic { + throw "Not implemented"; + } + + public function hset(field:String, value:Dynamic):Dynamic { + throw "Not implemented"; + } + + public function toString() { + return name; + } +} diff --git a/hscript/misc/classes/ClassInterp.hx b/hscript/misc/classes/ClassInterp.hx new file mode 100644 index 0000000..34b3abd --- /dev/null +++ b/hscript/misc/classes/ClassInterp.hx @@ -0,0 +1,18 @@ +package hscript.misc.classes; + +import hscript.Ast.VariableType; + +class ClassInterp extends Interp { + + public function new(name:String) { + super(name); + } + + override function declarePublic(name:VariableType, value:Dynamic):Bool { + return false; + } + + override function declareStatic(name:VariableType, value:Dynamic):Bool { + return false; + } +} diff --git a/hscript/misc/classes/Instance.hx b/hscript/misc/classes/Instance.hx new file mode 100644 index 0000000..dc34e1a --- /dev/null +++ b/hscript/misc/classes/Instance.hx @@ -0,0 +1,16 @@ +package hscript.misc.classes; + +import hscript.Ast.IHScriptCustomBehaviour; + +class Instance implements IHScriptCustomBehaviour { + + public function new(args:Array, classHandler:ClassHandler) {} + + public function hget(field:String):Dynamic { + throw "Not implemented"; + } + + public function hset(field:String, value:Dynamic):Dynamic { + throw "Not implemented"; + } +} diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx new file mode 100644 index 0000000..965dc5b --- /dev/null +++ b/hscript/misc/classes/InstanceInterp.hx @@ -0,0 +1,18 @@ +package hscript.misc.classes; + +import hscript.Ast.VariableType; + +class InstanceInterp extends Interp { + + public function new(name:String) { + super(name); + } + + override function declarePublic(name:VariableType, value:Dynamic):Bool { + return false; + } + + override function declareStatic(name:VariableType, value:Dynamic):Bool { + return false; + } +} From 1fcaec9b79c91bd6e662ef91a968ab2f4d9cdc3a Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Sat, 10 Jan 2026 19:43:13 -0700 Subject: [PATCH 03/13] Update ByteCompiler.hx --- hscript/bytecode/ByteCompiler.hx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hscript/bytecode/ByteCompiler.hx b/hscript/bytecode/ByteCompiler.hx index f7e24a5..6899f57 100644 --- a/hscript/bytecode/ByteCompiler.hx +++ b/hscript/bytecode/ByteCompiler.hx @@ -397,6 +397,7 @@ class ByteCompiler { write(body); bake(endPointer); + case EClass(_): // TODO case EEmpty: } @@ -449,7 +450,7 @@ class ByteCompiler { private function appendsOntoStack(expr:Expr):Bool { return switch (expr.expr) { - case EInfo(_) | EVar(_) | EParent(_) | EUnop(_) | EWhile(_) | EDoWhile(_) | EBreak | EContinue | EFor(_) | EForKeyValue(_) | EThrow(_) | EMeta(_) | EEmpty: false; + case EInfo(_) | EVar(_) | EParent(_) | EUnop(_) | EWhile(_) | EDoWhile(_) | EBreak | EContinue | EFor(_) | EForKeyValue(_) | EThrow(_) | EMeta(_) | EEmpty | EClass(_): false; case EIdent(_) | EConst(_) | EBlock(_) | EField(_) | EArray(_) | ECall(_) | EArrayDecl(_) | EMapDecl(_) | ENew(_) | EObject(_) | ESwitch(_) | ETry(_) | EIf(_) | EFunction(_) | EImport(_) | ETernary(_): true; case EReturn(expr): expr != null; case EBinop(op, _): From 18ce9869bd10c7ee1c4b43b772bbe6e28d487821 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Tue, 20 Jan 2026 23:18:39 -0700 Subject: [PATCH 04/13] Working Custom Class System still in progress --- hscript/Interp.hx | 18 +++++++-- hscript/misc/classes/ClassHandler.hx | 28 +++++++++++--- hscript/misc/classes/ClassInterp.hx | 25 +++++++++---- hscript/misc/classes/Instance.hx | 33 +++++++++++++++-- hscript/misc/classes/InstanceInterp.hx | 28 +++++++++++--- test/Main.hx | 51 +++++++++++++++----------- 6 files changed, 135 insertions(+), 48 deletions(-) diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 74226e2..27fa627 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -1,5 +1,7 @@ package hscript; +import hscript.misc.classes.ClassHandler; +import hscript.Ast.ClassDecl; import haxe.ds.ObjectMap; import haxe.ds.Map; import haxe.ds.StringMap; @@ -212,14 +214,14 @@ class ScriptRuntime { return ref.r; } - private function declarePublic(name:VariableType, value:Dynamic):Bool { + private inline function declarePublic(name:VariableType, value:Dynamic):Bool { if(publicVariables == null) return false; var varName:String = variableNames[name]; publicVariables.set(varName, value); return true; } - private function declareStatic(name:VariableType, value:Dynamic):Bool { + private inline function declareStatic(name:VariableType, value:Dynamic):Bool { var varName:String = variableNames[name]; if(StaticInterp.staticVariables.exists(varName)) return false; StaticInterp.staticVariables.set(varName, value); @@ -455,8 +457,9 @@ class Interp extends ScriptRuntime { var importValue:Dynamic = interpImport(path, mode); if (importValue == null) error(EInvalidClass(path), expr.line); return importValue; - case EClass(name, decl): - null; // TODO + case EClass(name, decl): + interpClass(name, decl); + return null; case EInfo(info, _): error(ECustom("Invalid EInfo()"), expr.line); case EEmpty: null; } @@ -496,6 +499,7 @@ class Interp extends ScriptRuntime { return null; } + // TODO: resolve custom class private function interpImport(path:String, mode:EImportMode):Dynamic { if (mode == All) return null; // not implemented @@ -759,9 +763,15 @@ class Interp extends ScriptRuntime { if (classType == null) classType = resolveGlobal(className); var params:Array = [for (arg in args) interpExpr(arg)]; + if(classType is ClassHandler) + return ClassHandler.createInstance(classType, params); return Type.createInstance(classType, params); } + private inline function interpClass(name:VariableType, decl:ClassDecl) { + declare(name, new ClassHandler(decl, this)); + } + private function assignExpr(left:Expr, right:Expr):Dynamic { var assignValue:Dynamic = interpExpr(right); switch (left.expr) { diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx index 24f9e80..c3e04a1 100644 --- a/hscript/misc/classes/ClassHandler.hx +++ b/hscript/misc/classes/ClassHandler.hx @@ -8,6 +8,7 @@ import hscript.Ast.IHScriptCustomBehaviour; /** * Provides handlers for static class fields and instantiation. */ + @:allow(hscript.misc.classes.Instance) class ClassHandler implements IHScriptCustomBehaviour { public static function createInstance(cl:ClassHandler, args:Array) { @@ -18,27 +19,42 @@ class ClassHandler implements IHScriptCustomBehaviour { private var module:ScriptRuntime; private var classInterp:Interp; + private final clsDecl:ClassDecl; + private final constructor:Dynamic; public function new(clsDecl:ClassDecl, module:ScriptRuntime) { this.module = module; this.name = clsDecl.name; - build(clsDecl); + this.classInterp = new ClassInterp(this.name); + this.clsDecl = clsDecl; + this.constructor = Reflect.makeVarArgs(function(args) return this.create(args)); + build(); } - private function build(clsDecl:ClassDecl) { - + private function build() { + classInterp.errorHandler = module.errorHandler; + classInterp.publicVariables = module.publicVariables; + classInterp.execute(clsDecl.body); } private function create(args:Array):Dynamic { - return null; + return new Instance(args, this); + } + + private inline function hasField(name:String) { + return classInterp.variables.exists(name); } public function hget(field:String):Dynamic { - throw "Not implemented"; + if(field == 'new') + return constructor; + + return classInterp.variables.get(field); } public function hset(field:String, value:Dynamic):Dynamic { - throw "Not implemented"; + classInterp.variables.set(field, value); + return value; } public function toString() { diff --git a/hscript/misc/classes/ClassInterp.hx b/hscript/misc/classes/ClassInterp.hx index 34b3abd..d826cf6 100644 --- a/hscript/misc/classes/ClassInterp.hx +++ b/hscript/misc/classes/ClassInterp.hx @@ -1,18 +1,29 @@ package hscript.misc.classes; -import hscript.Ast.VariableType; - class ClassInterp extends Interp { public function new(name:String) { super(name); } - override function declarePublic(name:VariableType, value:Dynamic):Bool { - return false; - } + override function interpExpr(expr:Ast.Expr):Dynamic { + if (expr == null) return null; + + this.lineNumber = expr.line; - override function declareStatic(name:VariableType, value:Dynamic):Bool { - return false; + return switch(expr.expr) { + case EVar(name, init, _, isStatic): + if(depth == 0 && !isStatic) // Instance variable, ignore it. + return null; + declare(name, init == null ? null : interpExpr(init)); + return null; + case EFunction(args, body, name, _, isStatic): + if(name != -1) { + if(depth == 0 && !isStatic) // Instance function, ignore it. + return null; + } + interpFunction(args, body, name, false, false); + default: super.interpExpr(expr); + } } } diff --git a/hscript/misc/classes/Instance.hx b/hscript/misc/classes/Instance.hx index dc34e1a..aed7aa2 100644 --- a/hscript/misc/classes/Instance.hx +++ b/hscript/misc/classes/Instance.hx @@ -1,16 +1,43 @@ package hscript.misc.classes; +import hscript.Interp.StaticInterp; +import hscript.Interp.ScriptRuntime; import hscript.Ast.IHScriptCustomBehaviour; +/** + * Provides handlers for custom classes. + * + * @author Jamextreme140 + */ class Instance implements IHScriptCustomBehaviour { - public function new(args:Array, classHandler:ClassHandler) {} + private var instanceInterp:Interp; + private var classHandler:ClassHandler; // TODO: static fields lookup + private var module:ScriptRuntime; + + public function new(args:Array, classHandler:ClassHandler) { + this.module = classHandler.module; + this.classHandler = classHandler; + build(); + + if(instanceInterp.variables.exists('new')) { + StaticInterp.callObjectField(null, instanceInterp.variables.get('new'), args); + } + } + + private function build() { + instanceInterp = new InstanceInterp(classHandler.name); + instanceInterp.errorHandler = module.errorHandler; + instanceInterp.publicVariables = module.publicVariables; + instanceInterp.execute(classHandler.clsDecl.body); + } public function hget(field:String):Dynamic { - throw "Not implemented"; + return instanceInterp.variables.get(field); } public function hset(field:String, value:Dynamic):Dynamic { - throw "Not implemented"; + instanceInterp.variables.set(field, value); + return value; } } diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx index 965dc5b..cbe12bb 100644 --- a/hscript/misc/classes/InstanceInterp.hx +++ b/hscript/misc/classes/InstanceInterp.hx @@ -1,18 +1,34 @@ package hscript.misc.classes; -import hscript.Ast.VariableType; - class InstanceInterp extends Interp { public function new(name:String) { super(name); } - override function declarePublic(name:VariableType, value:Dynamic):Bool { - return false; + override function interpExpr(expr:Ast.Expr):Dynamic { + if (expr == null) return null; + + this.lineNumber = expr.line; + + return switch(expr.expr) { + case EVar(name, init, _, isStatic): + if(depth == 0 && isStatic) // Class variable, ignore it. + return null; + declare(name, init == null ? null : interpExpr(init)); + return null; + case EFunction(args, body, name, _, isStatic): + if(name != -1) { + if(depth == 0 && isStatic) // Class function, ignore it. + return null; + } + interpFunction(args, body, name, false, false); + default: super.interpExpr(expr); + } } - override function declareStatic(name:VariableType, value:Dynamic):Bool { - return false; + // TODO: Lookup for static fields + override function resolveGlobal(ident:Ast.VariableType):Dynamic { + return super.resolveGlobal(ident); } } diff --git a/test/Main.hx b/test/Main.hx index 6c0e31a..d385131 100644 --- a/test/Main.hx +++ b/test/Main.hx @@ -16,31 +16,38 @@ class Main { public static function main() { var parser = new Parser(); var expr = parser.parseString(" - var a = []; - for (i in 0...1000) a.push(i * 2 + 1 / 6); - if(true == true) a.push(1); - a[0]; + class MyClass { + public static function getSum(a:Int, b:Int) { + return a + b + 2; + } + + public var a:Int; + public var b:Int; + public function new(n1:Int, n2:Int) { + trace('hello! :3'); + a = n1; + b = n2; + } + + public function sum() { + var r = a + b; + if(r == 67) return -1; + return r; + } + } + + trace(MyClass.getSum(9, 10)); + var myInstance = new MyClass(50, 17); + var v = myInstance.sum(); + trace(v == -1 ? 'nope :>' : v); "); - // expr = Analyzer.optimize(expr); - - var compiler:ByteCompiler = new ByteCompiler(); - var bytes:ByteChunk = compiler.compile(expr); - - var vm = new ByteVM("Main.hx"); - - var time:Float = Timer.stamp(); - for (i in 0...2000) - vm.execute(bytes); - trace(Timer.stamp() - time); - - + expr = Analyzer.optimize(expr); + trace("------ HSCRIPT AST ------"); + trace(hscript.utils.ExprUtils.print(expr)); + trace("--------- OUTPUT --------"); var interp:Interp = new Interp("Main.hx"); interp.errorHandler = (error:Error) -> {Sys.println(error);} - - var time:Float = Timer.stamp(); - for (i in 0...2000) - interp.execute(expr); - trace(Timer.stamp() - time); + interp.execute(expr); } } \ No newline at end of file From e09888c7fdfe1ad0d3b37caf41c9560bd0902747 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Wed, 21 Jan 2026 18:20:02 -0700 Subject: [PATCH 05/13] Better instance field handling encapsulated "instance level" fields. `this.a = a;` will not assigns itself. --- hscript/misc/classes/Instance.hx | 10 +-- hscript/misc/classes/InstanceInterp.hx | 97 ++++++++++++++++++++++++-- test/Main.hx | 16 +++-- 3 files changed, 108 insertions(+), 15 deletions(-) diff --git a/hscript/misc/classes/Instance.hx b/hscript/misc/classes/Instance.hx index aed7aa2..9aa1bde 100644 --- a/hscript/misc/classes/Instance.hx +++ b/hscript/misc/classes/Instance.hx @@ -11,7 +11,7 @@ import hscript.Ast.IHScriptCustomBehaviour; */ class Instance implements IHScriptCustomBehaviour { - private var instanceInterp:Interp; + private var instanceInterp:InstanceInterp; private var classHandler:ClassHandler; // TODO: static fields lookup private var module:ScriptRuntime; @@ -26,18 +26,18 @@ class Instance implements IHScriptCustomBehaviour { } private function build() { - instanceInterp = new InstanceInterp(classHandler.name); + instanceInterp = new InstanceInterp(classHandler.name, classHandler); instanceInterp.errorHandler = module.errorHandler; instanceInterp.publicVariables = module.publicVariables; + instanceInterp.scriptParent = this; instanceInterp.execute(classHandler.clsDecl.body); } public function hget(field:String):Dynamic { - return instanceInterp.variables.get(field); + return instanceInterp.resolveField(field); } public function hset(field:String, value:Dynamic):Dynamic { - instanceInterp.variables.set(field, value); - return value; + return instanceInterp.assignField(field, value); } } diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx index cbe12bb..e4b9af3 100644 --- a/hscript/misc/classes/InstanceInterp.hx +++ b/hscript/misc/classes/InstanceInterp.hx @@ -1,21 +1,41 @@ package hscript.misc.classes; +import hscript.Ast; +import hscript.Interp; +import haxe.ds.Vector; + class InstanceInterp extends Interp { - public function new(name:String) { + private var instanceVariablesDeclared:Vector; + private var instanceVariablesValues:Vector; + + private var classHandler:ClassHandler; + + public function new(name:String, classHandler:ClassHandler) { + this.classHandler = classHandler; super(name); } - override function interpExpr(expr:Ast.Expr):Dynamic { + private override function loadTables(info:VariableInfo) { + instanceVariablesDeclared = new Vector(info.length); + instanceVariablesValues = new Vector(info.length); + super.loadTables(info); + } + + private override function interpExpr(expr:Ast.Expr):Dynamic { if (expr == null) return null; this.lineNumber = expr.line; return switch(expr.expr) { + case EIdent(name): + if (variablesDeclared[name]) variablesValues[name].r; + else if(instanceVariablesDeclared[name]) instanceVariablesValues[name].r; + else resolveGlobal(name); case EVar(name, init, _, isStatic): if(depth == 0 && isStatic) // Class variable, ignore it. return null; - declare(name, init == null ? null : interpExpr(init)); + __declare(name, init == null ? null : interpExpr(init)); return null; case EFunction(args, body, name, _, isStatic): if(name != -1) { @@ -26,9 +46,78 @@ class InstanceInterp extends Interp { default: super.interpExpr(expr); } } + // can't override since is inlined :'3 + private inline function __declare(name:VariableType, value:Dynamic):Dynamic { + var v:IVariableReference = {r: value}; + if (depth != 0) changes.push({ + name: name, + oldDeclared: variablesDeclared[name], + oldValue: variablesValues[name] + }); + else { + // instance field declaration + instanceVariablesDeclared[name] = true; + instanceVariablesValues[name] = v; + } + + variablesDeclared[name] = true; + variablesValues[name] = v; + + return value; + } + + private inline function __declareFunction(name:VariableType, ref:IVariableReference) { + if (depth != 0) changes.push({ + name: name, + oldDeclared: variablesDeclared[name], + oldValue: variablesValues[name] + }); + else { + // instance field declaration + instanceVariablesDeclared[name] = true; + instanceVariablesValues[name] = ref; + } + + variablesDeclared[name] = true; + variablesValues[name] = ref; + + return ref.r; + } + + private override function interpFunction(args:Array, body:Expr, name:VariableType, ?isPublic:Bool, ?isStatic:Bool) { + var fn:Dynamic = super.interpFunction(args, body, name, isPublic, isStatic); + var fnRef:IVariableReference = {r: null}; + if(name != -1 && depth == 0) { + // instance function declaration + fnRef.r = fn; + __declareFunction(name, fnRef); + } + return fn; + } + + public function resolveField(ident:String):Dynamic { + if(!variablesLookup.exists(ident)) return null; + var name = variablesLookup.get(ident); + return instanceVariablesDeclared[name] ? instanceVariablesValues[name].r : null; + } + + public function assignField(ident:String, value:Dynamic):Dynamic { + if(!variablesLookup.exists(ident)) return null; + var name = variablesLookup.get(ident); + instanceVariablesDeclared[name] = true; + instanceVariablesValues[name].r = value; + + return assign(name, value); + } // TODO: Lookup for static fields - override function resolveGlobal(ident:Ast.VariableType):Dynamic { + private override function resolveGlobal(ident:Ast.VariableType):Dynamic { return super.resolveGlobal(ident); } + + private override function resolve(varName:String):Dynamic { + if(varName == 'this') + return scriptParent; + return super.resolve(varName); + } } diff --git a/test/Main.hx b/test/Main.hx index d385131..978d960 100644 --- a/test/Main.hx +++ b/test/Main.hx @@ -21,12 +21,16 @@ class Main { return a + b + 2; } - public var a:Int; - public var b:Int; - public function new(n1:Int, n2:Int) { + public var a:Int = 50; + public var b:Int = 17; + public function new(a:Int, b:Int) { trace('hello! :3'); - a = n1; - b = n2; + trace(this.a); + trace(a); + trace(this.b); + trace(b); + this.a = a; + this.b = b; } public function sum() { @@ -37,7 +41,7 @@ class Main { } trace(MyClass.getSum(9, 10)); - var myInstance = new MyClass(50, 17); + var myInstance = new MyClass(6, 7); var v = myInstance.sum(); trace(v == -1 ? 'nope :>' : v); "); From 4796c9044fe431fa6ba6f5252b29f0a74505641e Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Thu, 22 Jan 2026 12:06:33 -0700 Subject: [PATCH 06/13] Static fields lookup --- hscript/misc/classes/ClassHandler.hx | 6 +++--- hscript/misc/classes/Instance.hx | 8 ++++++-- hscript/misc/classes/InstanceInterp.hx | 7 +++++++ test/Main.hx | 17 ++++++++++------- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx index c3e04a1..f46e67f 100644 --- a/hscript/misc/classes/ClassHandler.hx +++ b/hscript/misc/classes/ClassHandler.hx @@ -16,9 +16,9 @@ class ClassHandler implements IHScriptCustomBehaviour { } public final name:String; - + public final classInterp:Interp; + private var module:ScriptRuntime; - private var classInterp:Interp; private final clsDecl:ClassDecl; private final constructor:Dynamic; @@ -41,7 +41,7 @@ class ClassHandler implements IHScriptCustomBehaviour { return new Instance(args, this); } - private inline function hasField(name:String) { + public function hasField(name:String) { return classInterp.variables.exists(name); } diff --git a/hscript/misc/classes/Instance.hx b/hscript/misc/classes/Instance.hx index 9aa1bde..3c299e3 100644 --- a/hscript/misc/classes/Instance.hx +++ b/hscript/misc/classes/Instance.hx @@ -12,7 +12,7 @@ import hscript.Ast.IHScriptCustomBehaviour; class Instance implements IHScriptCustomBehaviour { private var instanceInterp:InstanceInterp; - private var classHandler:ClassHandler; // TODO: static fields lookup + private var classHandler:ClassHandler; private var module:ScriptRuntime; public function new(args:Array, classHandler:ClassHandler) { @@ -25,7 +25,7 @@ class Instance implements IHScriptCustomBehaviour { } } - private function build() { + private inline function build() { instanceInterp = new InstanceInterp(classHandler.name, classHandler); instanceInterp.errorHandler = module.errorHandler; instanceInterp.publicVariables = module.publicVariables; @@ -34,10 +34,14 @@ class Instance implements IHScriptCustomBehaviour { } public function hget(field:String):Dynamic { + if(classHandler.hasField(field)) + throw 'The field $field should be accessed in a static way'; return instanceInterp.resolveField(field); } public function hset(field:String, value:Dynamic):Dynamic { + if(classHandler.hasField(field)) + throw 'The field $field should be accessed in a static way'; return instanceInterp.assignField(field, value); } } diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx index e4b9af3..1bdbf6d 100644 --- a/hscript/misc/classes/InstanceInterp.hx +++ b/hscript/misc/classes/InstanceInterp.hx @@ -22,6 +22,11 @@ class InstanceInterp extends Interp { super.loadTables(info); } + public override function loadBaseVariables() { + variables.set(classHandler.name, classHandler); + super.loadBaseVariables(); + } + private override function interpExpr(expr:Ast.Expr):Dynamic { if (expr == null) return null; @@ -118,6 +123,8 @@ class InstanceInterp extends Interp { private override function resolve(varName:String):Dynamic { if(varName == 'this') return scriptParent; + if(classHandler.hasField(varName)) + return classHandler.classInterp.variables.get(varName); return super.resolve(varName); } } diff --git a/test/Main.hx b/test/Main.hx index 978d960..790d618 100644 --- a/test/Main.hx +++ b/test/Main.hx @@ -17,30 +17,33 @@ class Main { var parser = new Parser(); var expr = parser.parseString(" class MyClass { + public static var N:Int = 100; public static function getSum(a:Int, b:Int) { - return a + b + 2; + trace('static getSum'); + return a + b + N; } public var a:Int = 50; public var b:Int = 17; public function new(a:Int, b:Int) { trace('hello! :3'); - trace(this.a); - trace(a); - trace(this.b); - trace(b); + trace('---- locals check ----'); + trace('this.a: ${this.a}'); + trace('a: $a'); + trace('this.b: ${this.b}'); + trace('b: $b'); this.a = a; this.b = b; } public function sum() { - var r = a + b; + var r = MyClass.getSum(a, b) * N; if(r == 67) return -1; return r; } } - trace(MyClass.getSum(9, 10)); + trace(MyClass.getSum(9, 10) + 2); var myInstance = new MyClass(6, 7); var v = myInstance.sum(); trace(v == -1 ? 'nope :>' : v); From fc77659dc50bf4884fa9048df909b7304c9f0bc7 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Thu, 22 Jan 2026 14:27:15 -0700 Subject: [PATCH 07/13] Super Class handling (WIP) --- hscript/misc/classes/ClassHandler.hx | 25 +++++++++++++++++++++++-- hscript/misc/classes/Instance.hx | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx index f46e67f..1e47122 100644 --- a/hscript/misc/classes/ClassHandler.hx +++ b/hscript/misc/classes/ClassHandler.hx @@ -11,16 +11,20 @@ import hscript.Ast.IHScriptCustomBehaviour; @:allow(hscript.misc.classes.Instance) class ClassHandler implements IHScriptCustomBehaviour { + private static inline var CLASS_POSFIX:String = "_HSX"; // BIG TODO + public static function createInstance(cl:ClassHandler, args:Array) { return cl.create(args); } public final name:String; + public final isFinal:Bool = false; // TODO: final class public final classInterp:Interp; private var module:ScriptRuntime; private final clsDecl:ClassDecl; private final constructor:Dynamic; + private var inheritance:Null = null; public function new(clsDecl:ClassDecl, module:ScriptRuntime) { this.module = module; @@ -28,16 +32,33 @@ class ClassHandler implements IHScriptCustomBehaviour { this.classInterp = new ClassInterp(this.name); this.clsDecl = clsDecl; this.constructor = Reflect.makeVarArgs(function(args) return this.create(args)); + if(clsDecl.extend != null) + createInheritance(); build(); } - private function build() { + private function createInheritance() { + var extend:String = clsDecl.extend; + if(module.variables.exists(extend)) { + var cls:ClassHandler = module.variables.get(extend); + if(cls.isFinal) + throw 'Cannot extend a final class'; + inheritance = cls; + } + else + inheritance = Type.resolveClass('${extend}$CLASS_POSFIX'); + + if(inheritance == null) + throw 'Invalid class: ${extend} was not found.'; + } + + private inline function build() { classInterp.errorHandler = module.errorHandler; classInterp.publicVariables = module.publicVariables; classInterp.execute(clsDecl.body); } - private function create(args:Array):Dynamic { + private function create(args:Array):Instance { return new Instance(args, this); } diff --git a/hscript/misc/classes/Instance.hx b/hscript/misc/classes/Instance.hx index 3c299e3..29ef6c3 100644 --- a/hscript/misc/classes/Instance.hx +++ b/hscript/misc/classes/Instance.hx @@ -14,14 +14,24 @@ class Instance implements IHScriptCustomBehaviour { private var instanceInterp:InstanceInterp; private var classHandler:ClassHandler; private var module:ScriptRuntime; + private var constructor:Dynamic = null; + private var superClass:Dynamic = null; public function new(args:Array, classHandler:ClassHandler) { this.module = classHandler.module; this.classHandler = classHandler; build(); + var hasSuperClass = classHandler.inheritance != null; if(instanceInterp.variables.exists('new')) { + constructor = hasSuperClass ? createConstructor() : null; StaticInterp.callObjectField(null, instanceInterp.variables.get('new'), args); + + if(hasSuperClass && this.superClass == null) + throw 'super() not called'; + } + else if(hasSuperClass) { + createSuperClass(args); } } @@ -33,6 +43,22 @@ class Instance implements IHScriptCustomBehaviour { instanceInterp.execute(classHandler.clsDecl.body); } + private inline function createConstructor():Dynamic { + return Reflect.makeVarArgs(createSuperClass); + } + + private function createSuperClass(args:Array) { + if(classHandler.inheritance is ClassHandler) { + var instance:Instance = ClassHandler.createInstance(classHandler.inheritance, args); + superClass = instance; + // TODO: fetch superclass fields to concatenate with "scriptParentFields" + } + else { + superClass = Type.createInstance(classHandler.inheritance, args); + // TODO: fetch superclass fields to concatenate with "scriptParentFields" + } + } + public function hget(field:String):Dynamic { if(classHandler.hasField(field)) throw 'The field $field should be accessed in a static way'; From f132f2bf9052fc7baeb74a93ebb5ccc85fb49e8d Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Mon, 26 Jan 2026 10:48:20 -0700 Subject: [PATCH 08/13] final class + other things :3 UNTESTED --- hscript/Ast.hx | 9 ++++++++- hscript/Parser.hx | 6 ++++-- hscript/misc/classes/ClassHandler.hx | 24 ++++++++++++++++++------ hscript/misc/classes/Instance.hx | 23 +++++++++++++++-------- hscript/misc/classes/InstanceInterp.hx | 8 ++------ 5 files changed, 47 insertions(+), 23 deletions(-) diff --git a/hscript/Ast.hx b/hscript/Ast.hx index a86028b..d985964 100644 --- a/hscript/Ast.hx +++ b/hscript/Ast.hx @@ -272,12 +272,14 @@ class ClassDecl { public var extend:Null; public var implement:Array; public var body:Expr; // Will be always EInfo along with a EBlock + public var isFinal:Bool; - public function new(name:String, ?extend:String, implement:Array, body:Expr) { + public function new(name:String, ?extend:String, implement:Array, body:Expr, isFinal:Bool) { this.name = name; this.extend = extend; this.implement = implement; this.body = body; + this.isFinal = isFinal; } } @@ -287,4 +289,9 @@ typedef VariableInfo = Array; interface IHScriptCustomBehaviour { public function hset(name:String, value:Dynamic):Dynamic; public function hget(name:String):Dynamic; +} + +//@:build(insert macro here...) +interface IHScriptClass extends IHScriptCustomBehaviour { + public var instance:hscript.misc.classes.Instance; } \ No newline at end of file diff --git a/hscript/Parser.hx b/hscript/Parser.hx index f3578de..46d6f7c 100644 --- a/hscript/Parser.hx +++ b/hscript/Parser.hx @@ -356,6 +356,8 @@ class Parser { @:haxe.warning("-WUnusedPattern") // get rid of (WUnusedPattern) This case is unused on INLINE case private function parseKeyword(keyword:LKeyword) { return switch (keyword) { + case FINAL if(maybe(LTKeyWord(HCLASS))): + create(parseClass(true)); case VAR | FINAL: var variableName:String = parseIdent(); if (maybe(LTColon)) parseType(); // var:Type @@ -953,7 +955,7 @@ class Parser { return parseNextExpr(create(EObject(fields))); } - private function parseClass():ExprDef { + private function parseClass(finalModifier:Bool = false):ExprDef { var className:String = parseIdent(); if(maybe(LTOp(LT))) // class MyClass parseClassArgs(); @@ -1003,7 +1005,7 @@ class Parser { var fieldsExpr:Array = []; parseClassFields(fieldsExpr); - var clsDecl:ClassDecl = new ClassDecl(className, extend, implement, create(EInfo(variablesList, create(EBlock(fieldsExpr))))); + var clsDecl:ClassDecl = new ClassDecl(className, extend, implement, create(EInfo(variablesList, create(EBlock(fieldsExpr)))), finalModifier); this.fileName = oldFileName; this.variablesList = oldVariablesList; diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx index 1e47122..240c747 100644 --- a/hscript/misc/classes/ClassHandler.hx +++ b/hscript/misc/classes/ClassHandler.hx @@ -11,24 +11,36 @@ import hscript.Ast.IHScriptCustomBehaviour; @:allow(hscript.misc.classes.Instance) class ClassHandler implements IHScriptCustomBehaviour { - private static inline var CLASS_POSFIX:String = "_HSX"; // BIG TODO + private static inline var CLASS_POSFIX:String = "_HSX"; // BIG TODO: generate special classes with macro + /** + * Creates an instance of custom class `cl`, using `args` as arguments to the + * class constructor. + */ public static function createInstance(cl:ClassHandler, args:Array) { return cl.create(args); } + /** + * Returns the custom class of `o`, if `o` is a custom class instance + */ + public static function getClass(o:Instance):ClassHandler { + return @:privateAccess o.classHandler; + } + public final name:String; - public final isFinal:Bool = false; // TODO: final class + public final isFinal:Bool; public final classInterp:Interp; private var module:ScriptRuntime; private final clsDecl:ClassDecl; private final constructor:Dynamic; - private var inheritance:Null = null; + private var classReference:Null = null; public function new(clsDecl:ClassDecl, module:ScriptRuntime) { this.module = module; this.name = clsDecl.name; + this.isFinal = clsDecl.isFinal; this.classInterp = new ClassInterp(this.name); this.clsDecl = clsDecl; this.constructor = Reflect.makeVarArgs(function(args) return this.create(args)); @@ -43,12 +55,12 @@ class ClassHandler implements IHScriptCustomBehaviour { var cls:ClassHandler = module.variables.get(extend); if(cls.isFinal) throw 'Cannot extend a final class'; - inheritance = cls; + classReference = cls; } else - inheritance = Type.resolveClass('${extend}$CLASS_POSFIX'); + classReference = Type.resolveClass('${extend}$CLASS_POSFIX'); // BIG TODO - if(inheritance == null) + if(classReference == null) throw 'Invalid class: ${extend} was not found.'; } diff --git a/hscript/misc/classes/Instance.hx b/hscript/misc/classes/Instance.hx index 29ef6c3..1854109 100644 --- a/hscript/misc/classes/Instance.hx +++ b/hscript/misc/classes/Instance.hx @@ -22,13 +22,13 @@ class Instance implements IHScriptCustomBehaviour { this.classHandler = classHandler; build(); - var hasSuperClass = classHandler.inheritance != null; + var hasSuperClass = classHandler.classReference != null; if(instanceInterp.variables.exists('new')) { constructor = hasSuperClass ? createConstructor() : null; StaticInterp.callObjectField(null, instanceInterp.variables.get('new'), args); if(hasSuperClass && this.superClass == null) - throw 'super() not called'; + throw 'missing super constructor call'; } else if(hasSuperClass) { createSuperClass(args); @@ -48,14 +48,21 @@ class Instance implements IHScriptCustomBehaviour { } private function createSuperClass(args:Array) { - if(classHandler.inheritance is ClassHandler) { - var instance:Instance = ClassHandler.createInstance(classHandler.inheritance, args); - superClass = instance; - // TODO: fetch superclass fields to concatenate with "scriptParentFields" + if(classHandler.classReference is ClassHandler) { + var superInstance:Instance = ClassHandler.createInstance(classHandler.classReference, args); + superClass = superInstance; + for(f => name in superInstance.instanceInterp.variablesLookup) { + @:privateAccess + if(superInstance.instanceInterp.instanceVariablesDeclared[name]) + this.instanceInterp.scriptParentFields.set(f, true); + } } else { - superClass = Type.createInstance(classHandler.inheritance, args); - // TODO: fetch superclass fields to concatenate with "scriptParentFields" + // create macro-generated super class instance + superClass = Type.createInstance(classHandler.classReference, args); + for(f in Type.getInstanceFields(classHandler.classReference)) + this.instanceInterp.scriptParentFields.set(f, true); + superClass.instance = this; } } diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx index 1bdbf6d..74b6b48 100644 --- a/hscript/misc/classes/InstanceInterp.hx +++ b/hscript/misc/classes/InstanceInterp.hx @@ -115,16 +115,12 @@ class InstanceInterp extends Interp { return assign(name, value); } - // TODO: Lookup for static fields private override function resolveGlobal(ident:Ast.VariableType):Dynamic { - return super.resolveGlobal(ident); - } - - private override function resolve(varName:String):Dynamic { + var varName:String = variableNames[ident]; if(varName == 'this') return scriptParent; if(classHandler.hasField(varName)) return classHandler.classInterp.variables.get(varName); - return super.resolve(varName); + return super.resolveGlobal(ident); } } From 8363b2695f86cc864561b90b625f03378140ea81 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Tue, 27 Jan 2026 10:39:44 -0700 Subject: [PATCH 09/13] forgot this btw --- hscript/anaylzers/ConstEval.hx | 2 +- hscript/anaylzers/Inliner.hx | 2 +- hscript/anaylzers/Unravel.hx | 2 +- hscript/misc/classes/ClassHandler.hx | 4 ++++ hscript/misc/classes/InstanceInterp.hx | 4 ++-- hscript/utils/ExprUtils.hx | 2 +- test/Main.hx | 2 +- 7 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hscript/anaylzers/ConstEval.hx b/hscript/anaylzers/ConstEval.hx index 156e0e9..3a55f6e 100644 --- a/hscript/anaylzers/ConstEval.hx +++ b/hscript/anaylzers/ConstEval.hx @@ -41,7 +41,7 @@ using hscript.Ast.ExprBinop; case EDoWhile(cond, body): EDoWhile(guarantee(cond != null ? eval(cond, vars) : null), guarantee(body != null ? eval(body, vars) : null)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) guarantee(arg != null ? eval(arg, vars) : null)], guarantee(expr != null ? eval(expr, vars) : null)); case EInfo(info, expr): EInfo(info, guarantee(expr != null ? eval(expr, info) : null)); - case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, vars))); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, vars), decl.isFinal)); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; case ENew(className, args): var mapIndexes:Array = vars == null ? [] : [ diff --git a/hscript/anaylzers/Inliner.hx b/hscript/anaylzers/Inliner.hx index 1633aef..478f577 100644 --- a/hscript/anaylzers/Inliner.hx +++ b/hscript/anaylzers/Inliner.hx @@ -39,7 +39,7 @@ using hscript.utils.ExprUtils; case EDoWhile(cond, body): EDoWhile(eval(cond, vars), eval(body, vars)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) eval(arg, vars)], eval(expr, vars)); case EInfo(info, expr): EInfo(info, eval(expr, info)); - case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, vars))); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, vars), decl.isFinal)); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; case EUnop(op, isPrefix, expr): EUnop(op, isPrefix, eval(expr, vars)); case EIf(cond, thenExpr, elseExpr): EIf(eval(cond, vars), eval(thenExpr, vars), elseExpr != null ? eval(elseExpr, vars) : null); diff --git a/hscript/anaylzers/Unravel.hx b/hscript/anaylzers/Unravel.hx index f8fe27d..824df12 100644 --- a/hscript/anaylzers/Unravel.hx +++ b/hscript/anaylzers/Unravel.hx @@ -102,7 +102,7 @@ using hscript.utils.ExprUtils; case EDoWhile(cond, body): EDoWhile(eval(cond), eval(body)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) eval(arg)], eval(expr)); case EInfo(info, expr): EInfo(info, eval(expr, cl)); - case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, true))); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, eval(decl.body, true), decl.isFinal)); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; case EUnop(op, isPrefix, expr): EUnop(op, isPrefix, eval(expr)); case EIf(cond, thenExpr, elseExpr): EIf(eval(cond), eval(thenExpr), elseExpr != null ? eval(elseExpr) : null); diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx index 240c747..2dd9a1d 100644 --- a/hscript/misc/classes/ClassHandler.hx +++ b/hscript/misc/classes/ClassHandler.hx @@ -67,6 +67,7 @@ class ClassHandler implements IHScriptCustomBehaviour { private inline function build() { classInterp.errorHandler = module.errorHandler; classInterp.publicVariables = module.publicVariables; + classInterp.variables.set(name, this); classInterp.execute(clsDecl.body); } @@ -81,6 +82,9 @@ class ClassHandler implements IHScriptCustomBehaviour { public function hget(field:String):Dynamic { if(field == 'new') return constructor; + + if(field == name) // MyClass.MyClass.something() isn't valid + return module.error(EUnknownVariable(field)); return classInterp.variables.get(field); } diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx index 74b6b48..4c9afdc 100644 --- a/hscript/misc/classes/InstanceInterp.hx +++ b/hscript/misc/classes/InstanceInterp.hx @@ -36,7 +36,7 @@ class InstanceInterp extends Interp { case EIdent(name): if (variablesDeclared[name]) variablesValues[name].r; else if(instanceVariablesDeclared[name]) instanceVariablesValues[name].r; - else resolveGlobal(name); + else resolveIdent(name); case EVar(name, init, _, isStatic): if(depth == 0 && isStatic) // Class variable, ignore it. return null; @@ -115,7 +115,7 @@ class InstanceInterp extends Interp { return assign(name, value); } - private override function resolveGlobal(ident:Ast.VariableType):Dynamic { + private function resolveIdent(ident:Ast.VariableType):Dynamic { var varName:String = variableNames[ident]; if(varName == 'this') return scriptParent; diff --git a/hscript/utils/ExprUtils.hx b/hscript/utils/ExprUtils.hx index 87466e0..3fc241a 100644 --- a/hscript/utils/ExprUtils.hx +++ b/hscript/utils/ExprUtils.hx @@ -116,7 +116,7 @@ import hscript.Ast.Expr; case EDoWhile(cond, body): EDoWhile(map(cond, iter), map(body, iter)); case EMeta(name, args, expr): EMeta(name, [for (arg in args) map(arg, iter)], map(expr, iter)); case EInfo(info, expr): EInfo(info, map(expr, iter)); - case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, map(decl.body, iter))); + case EClass(name, decl): EClass(name, new ClassDecl(decl.name, decl.extend, decl.implement, map(decl.body, iter), decl.isFinal)); case EBreak | EConst(_) | EContinue | EIdent(_) | EImport(_) | EEmpty: expr.expr; }, expr.line) else null; diff --git a/test/Main.hx b/test/Main.hx index 790d618..839bab9 100644 --- a/test/Main.hx +++ b/test/Main.hx @@ -20,7 +20,7 @@ class Main { public static var N:Int = 100; public static function getSum(a:Int, b:Int) { trace('static getSum'); - return a + b + N; + return a + b + MyClass.N; } public var a:Int = 50; From 5aa4438a6aff881947aff9c1e2b3834d93dd2266 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Thu, 29 Jan 2026 16:55:48 -0700 Subject: [PATCH 10/13] imports usage in class from module (WIP) --- hscript/misc/classes/ClassHandler.hx | 10 +++++- hscript/misc/classes/ClassInterp.hx | 14 ++++++++- hscript/misc/classes/InstanceInterp.hx | 11 +++++++ test/Main.hx | 42 +++++++++++--------------- 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx index 2dd9a1d..033f6e7 100644 --- a/hscript/misc/classes/ClassHandler.hx +++ b/hscript/misc/classes/ClassHandler.hx @@ -41,7 +41,7 @@ class ClassHandler implements IHScriptCustomBehaviour { this.module = module; this.name = clsDecl.name; this.isFinal = clsDecl.isFinal; - this.classInterp = new ClassInterp(this.name); + this.classInterp = new ClassInterp(this.name, this); this.clsDecl = clsDecl; this.constructor = Reflect.makeVarArgs(function(args) return this.create(args)); if(clsDecl.extend != null) @@ -79,6 +79,14 @@ class ClassHandler implements IHScriptCustomBehaviour { return classInterp.variables.exists(name); } + public function hasInModule(name:String):Bool { + return module.variables.exists(name); + } + + public function getFromModule(name:String):Dynamic { + return module.variables.get(name); + } + public function hget(field:String):Dynamic { if(field == 'new') return constructor; diff --git a/hscript/misc/classes/ClassInterp.hx b/hscript/misc/classes/ClassInterp.hx index d826cf6..6af5b85 100644 --- a/hscript/misc/classes/ClassInterp.hx +++ b/hscript/misc/classes/ClassInterp.hx @@ -1,8 +1,13 @@ package hscript.misc.classes; +import hscript.Ast.VariableType; + class ClassInterp extends Interp { - public function new(name:String) { + private var handler:ClassHandler; + + public function new(name:String, handler:ClassHandler) { + this.handler = handler; super(name); } @@ -26,4 +31,11 @@ class ClassInterp extends Interp { default: super.interpExpr(expr); } } + + override function resolveGlobal(ident:VariableType):Dynamic { + var varName:String = variableNames[ident]; + if(handler.hasInModule(varName)) + return handler.getFromModule(varName); + return super.resolveGlobal(ident); + } } diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx index 4c9afdc..077da83 100644 --- a/hscript/misc/classes/InstanceInterp.hx +++ b/hscript/misc/classes/InstanceInterp.hx @@ -121,6 +121,17 @@ class InstanceInterp extends Interp { return scriptParent; if(classHandler.hasField(varName)) return classHandler.classInterp.variables.get(varName); + return resolveGlobal(ident); + } + + // First resolves variables from the module + override function resolveGlobal(ident:VariableType):Dynamic { + var varName:String = variableNames[ident]; + // current issue: due to how the variable setting works, + // unless the imported class is referenced, it cannot be obtained + // directly since isn't actually set. + if(classHandler.hasInModule(varName)) + return classHandler.getFromModule(varName); return super.resolveGlobal(ident); } } diff --git a/test/Main.hx b/test/Main.hx index 839bab9..c5eaa19 100644 --- a/test/Main.hx +++ b/test/Main.hx @@ -16,40 +16,32 @@ class Main { public static function main() { var parser = new Parser(); var expr = parser.parseString(" - class MyClass { - public static var N:Int = 100; - public static function getSum(a:Int, b:Int) { - trace('static getSum'); - return a + b + MyClass.N; + import haxe.ds.StringMap; + + var outsideMap = new StringMap(); + + class MyMap { + var map:StringMap; + public function new() { + map = new StringMap(); } - public var a:Int = 50; - public var b:Int = 17; - public function new(a:Int, b:Int) { - trace('hello! :3'); - trace('---- locals check ----'); - trace('this.a: ${this.a}'); - trace('a: $a'); - trace('this.b: ${this.b}'); - trace('b: $b'); - this.a = a; - this.b = b; + public function get(s:String) { + return map.get(s); } - public function sum() { - var r = MyClass.getSum(a, b) * N; - if(r == 67) return -1; - return r; + public function set(s:String, v:Int) { + map.set(s, v); + return v; } } - trace(MyClass.getSum(9, 10) + 2); - var myInstance = new MyClass(6, 7); - var v = myInstance.sum(); - trace(v == -1 ? 'nope :>' : v); + var mm = new MyMap(); + trace(mm.set(':3', 9)); + trace(mm.get(':3')); "); - expr = Analyzer.optimize(expr); + //expr = Analyzer.optimize(expr); trace("------ HSCRIPT AST ------"); trace(hscript.utils.ExprUtils.print(expr)); trace("--------- OUTPUT --------"); From 52322a6d10b5cde3a984ab8e13e8b4f550eec32f Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Sun, 1 Feb 2026 00:56:28 -0700 Subject: [PATCH 11/13] imported REAL classes usage in custom classes - for now, it only works with real classes - added "customClassHandler" to handle inexistent import path, assuming they are paths to a custom class --- hscript/Ast.hx | 10 ++++++++++ hscript/Interp.hx | 16 +++++++++++++++- hscript/misc/classes/ClassHandler.hx | 8 ++++++++ hscript/misc/classes/ClassInterp.hx | 4 ++++ hscript/misc/classes/InstanceInterp.hx | 6 +++++- test/Main.hx | 2 +- 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/hscript/Ast.hx b/hscript/Ast.hx index d985964..db2f2aa 100644 --- a/hscript/Ast.hx +++ b/hscript/Ast.hx @@ -267,6 +267,16 @@ enum EImportMode { All; // import haxe.*; } +class ImportInfo { + public var path:String; + public var mode:EImportMode; + + public function new(path:String, mode:EImportMode) { + this.path = path; + this.mode = mode; + } +} + class ClassDecl { public var name:String; public var extend:Null; diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 27fa627..bf359db 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -1,5 +1,6 @@ package hscript; +import hscript.Ast.ImportInfo; import hscript.misc.classes.ClassHandler; import hscript.Ast.ClassDecl; import haxe.ds.ObjectMap; @@ -67,6 +68,9 @@ class ScriptRuntime { private var variableNames:Vector; private var variablesLookup:StringMap; + @:allow(hscript.misc.classes.ClassHandler) + private var importLookup:StringMap; + private var changes:Array = []; private var depth:Int = 0; @@ -79,6 +83,7 @@ class ScriptRuntime { public var variables:InterpLocals; public var publicVariables:StringMap; public var errorHandler:Error->Void; + public var customClassLookup:ImportInfo->Bool; public var hasScriptParent:Bool = false; public var scriptParent(default, set):Dynamic; @@ -125,6 +130,8 @@ class ScriptRuntime { this.variableNames = null; this.variablesLookup = null; + this.importLookup = null; + this.changes = []; this.variables.useDefaults = true; @@ -143,6 +150,8 @@ class ScriptRuntime { variableNames = Vector.fromArrayCopy(info); variablesLookup = new StringMap(); for (i => name in info) variablesLookup.set(name, i); + + importLookup = new StringMap(); } private function loadBaseVariables() { @@ -455,7 +464,8 @@ class Interp extends ScriptRuntime { throw ISReturn; case EImport(path, mode): var importValue:Dynamic = interpImport(path, mode); - if (importValue == null) error(EInvalidClass(path), expr.line); + // if is a boolean, there was an attempt to import a custom class + if (importValue == null || (importValue is Bool && !importValue)) error(EInvalidClass(path), expr.line); return importValue; case EClass(name, decl): interpClass(name, decl); @@ -534,9 +544,13 @@ class Interp extends ScriptRuntime { if (variablesLookup.exists(variableName)) declare(variablesLookup.get(variableName), value); + else if(!importLookup.exists(variableName)) // cache the import + importLookup.set(variableName, new ImportInfo(splitPathName.join("."), mode)); return value; } + else if(customClassLookup != null) // it's maybe a custom class + return customClassLookup(new ImportInfo(splitPathName.join("."), mode)); return null; } diff --git a/hscript/misc/classes/ClassHandler.hx b/hscript/misc/classes/ClassHandler.hx index 033f6e7..ac8d2a1 100644 --- a/hscript/misc/classes/ClassHandler.hx +++ b/hscript/misc/classes/ClassHandler.hx @@ -87,6 +87,14 @@ class ClassHandler implements IHScriptCustomBehaviour { return module.variables.get(name); } + public function hasImport(name:String):Bool { + return module.importLookup.exists(name); + } + + public function resolveImport(name:String):ImportInfo { + return module.importLookup.get(name); + } + public function hget(field:String):Dynamic { if(field == 'new') return constructor; diff --git a/hscript/misc/classes/ClassInterp.hx b/hscript/misc/classes/ClassInterp.hx index 6af5b85..de2fc16 100644 --- a/hscript/misc/classes/ClassInterp.hx +++ b/hscript/misc/classes/ClassInterp.hx @@ -36,6 +36,10 @@ class ClassInterp extends Interp { var varName:String = variableNames[ident]; if(handler.hasInModule(varName)) return handler.getFromModule(varName); + if(handler.hasImport(varName)) { + var i:Ast.ImportInfo = handler.resolveImport(varName); + return interpImport(i.path, i.mode); + } return super.resolveGlobal(ident); } } diff --git a/hscript/misc/classes/InstanceInterp.hx b/hscript/misc/classes/InstanceInterp.hx index 077da83..d72ccd8 100644 --- a/hscript/misc/classes/InstanceInterp.hx +++ b/hscript/misc/classes/InstanceInterp.hx @@ -129,9 +129,13 @@ class InstanceInterp extends Interp { var varName:String = variableNames[ident]; // current issue: due to how the variable setting works, // unless the imported class is referenced, it cannot be obtained - // directly since isn't actually set. + // directly since isn't actually set so it needs to be reimported. if(classHandler.hasInModule(varName)) return classHandler.getFromModule(varName); + if(classHandler.hasImport(varName)) { + var i:Ast.ImportInfo = classHandler.resolveImport(varName); + return interpImport(i.path, i.mode); + } return super.resolveGlobal(ident); } } diff --git a/test/Main.hx b/test/Main.hx index c5eaa19..de6c0f0 100644 --- a/test/Main.hx +++ b/test/Main.hx @@ -18,7 +18,7 @@ class Main { var expr = parser.parseString(" import haxe.ds.StringMap; - var outsideMap = new StringMap(); + //var outsideMap = new StringMap(); class MyMap { var map:StringMap; From 6223b3ab69ad782aa32714b3b9b1e1fb1a6af407 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Sun, 1 Feb 2026 22:58:52 -0700 Subject: [PATCH 12/13] removing this to find a better solution --- hscript/Interp.hx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hscript/Interp.hx b/hscript/Interp.hx index bf359db..9f53e32 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -83,7 +83,6 @@ class ScriptRuntime { public var variables:InterpLocals; public var publicVariables:StringMap; public var errorHandler:Error->Void; - public var customClassLookup:ImportInfo->Bool; public var hasScriptParent:Bool = false; public var scriptParent(default, set):Dynamic; @@ -464,8 +463,7 @@ class Interp extends ScriptRuntime { throw ISReturn; case EImport(path, mode): var importValue:Dynamic = interpImport(path, mode); - // if is a boolean, there was an attempt to import a custom class - if (importValue == null || (importValue is Bool && !importValue)) error(EInvalidClass(path), expr.line); + if (importValue == null) error(EInvalidClass(path), expr.line); return importValue; case EClass(name, decl): interpClass(name, decl); @@ -549,8 +547,6 @@ class Interp extends ScriptRuntime { return value; } - else if(customClassLookup != null) // it's maybe a custom class - return customClassLookup(new ImportInfo(splitPathName.join("."), mode)); return null; } From 014d81e794fd3027d2946c2d8a0a4f48e8810c85 Mon Sep 17 00:00:00 2001 From: Jaime Humberto Macias Bustamante Date: Mon, 2 Feb 2026 00:18:12 -0700 Subject: [PATCH 13/13] "pathResolver" implementation aka. "importFailedCallback" --- hscript/Interp.hx | 22 ++++++-- test/Main.hx | 132 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 117 insertions(+), 37 deletions(-) diff --git a/hscript/Interp.hx b/hscript/Interp.hx index 9f53e32..08deec4 100644 --- a/hscript/Interp.hx +++ b/hscript/Interp.hx @@ -525,7 +525,7 @@ class Interp extends ScriptRuntime { if (variablesDeclared[variableID]) return variablesValues[variableID].r; } - var testClass:Either, Enum> = StaticInterp.resolvePath(path); + var testClass:EitherOfThree, Dynamic, Enum> = StaticInterp.resolvePath(path); if (testClass == null) { var splitPathCopy:Array = splitPathName.copy(); splitPathCopy.splice(-2, 1); @@ -537,6 +537,7 @@ class Interp extends ScriptRuntime { if (testClass != null) { var value:Dynamic = switch (testClass) { case Left(resolvedClass): resolvedClass; + case Middle(resolvedValue): resolvedValue; case Right(rawEnum): StaticInterp.resolveEnum(rawEnum); } @@ -873,8 +874,16 @@ class Interp extends ScriptRuntime { } } +private enum EitherOfThree { + Left(v:L); + Middle(v:M); + Right(v:R); +} + class StaticInterp { - public static var staticVariables:StringMap = new StringMap(); + public static var staticVariables:StringMap = new StringMap(); + + public static var pathResolver:String -> Dynamic = null; public static inline function evaluateBinop(op:ExprBinop, val1:Dynamic, val2:Dynamic):Dynamic { switch (op) { @@ -931,7 +940,7 @@ class StaticInterp { } } - // https://github.com/HaxeFoundation/hscript/blob/master/hscript/Interp.hx#L646-L652 + // https://github.com/HaxeFoundation/hscript/blob/master/hscript/Interp.hx#L677-L683 public static inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic { return cast(map, IMap).get(key); } @@ -940,13 +949,18 @@ class StaticInterp { cast(map, IMap).set(key, value); } - public static function resolvePath(path:String):Either, Enum> { + public static function resolvePath(path:String):EitherOfThree, Dynamic, Enum> { var resolvedClass:Class = Type.resolveClass(path); if (resolvedClass != null) return Left(resolvedClass); var resolvedEnum:Enum = Type.resolveEnum(path); if (resolvedEnum != null) return Right(resolvedEnum); + if(pathResolver != null) { + var resolvedValue:Dynamic = pathResolver(path); + if(resolvedValue != null) return Middle(resolvedValue); + } + return null; } diff --git a/test/Main.hx b/test/Main.hx index de6c0f0..b285383 100644 --- a/test/Main.hx +++ b/test/Main.hx @@ -1,52 +1,118 @@ package; -import haxe.Timer; -import hscript.bytecode.ByteInstruction.ByteChunk; +import hscript.Ast.Expr; import hscript.anaylzers.Analyzer; -import hscript.bytecode.ByteCompiler; -import hscript.bytecode.ByteVM; -import hscript.utils.BytesPrinter; import hscript.Error; import hscript.Interp; import hscript.Parser; using hscript.utils.ExprUtils; +class Module { + // makes persisten class fields values :3 + private static var moduleCache:Map = []; + // fixed script lookup. It should fetch real script files to use + public static function importFailedCallback(path:String):Dynamic { + var module:Module = null; + var splitPath = path.split("."); + + if(moduleCache.exists(path)) + return moduleCache.get(path).interp.variables.get(splitPath[splitPath.length - 1]); + + switch(path) { + case "pack.MyOtherClass": + var code:String = " + import haxe.ds.StringMap; + + class HelperClass { + public static function sum(a:Int, b:Int):Int { + return a + b; + } + } + + class MyOtherClass { + public static var NUM:Int = 10; + var map:StringMap; + public function new(a:Int, b:Int) { + map = new StringMap(); + trace('Hello again :3'); + trace(map.get(':3') == null); + trace(HelperClass.sum(a, b)); + } + + public function iGotThis() { + return 'FAHHHH!!'; + } + } + "; + module = new Module(code, path); + module.load(); + moduleCache.set(path, module); + default: + } + return module != null ? module.interp.variables.get(splitPath[splitPath.length - 1]) : null; + } + + private var interp:Interp; + private var parser:Parser; + private var expr:Expr; + private var loaded:Bool = false; + + public function new(code:String, path:String) { + parser = new Parser(); + interp = new Interp(path); + interp.errorHandler = (error:Error) -> {Sys.println(error);} + + parse(code); + } + + private function parse(code:String) { + try { + if(code != null && StringTools.trim(code) != "") { + expr = parser.parseString(code); + expr = Analyzer.optimize(expr); + } + } + catch(e:hscript.Error) { + trace(e.toString()); + } + catch(e) { + trace(e.toString()); + } + } + + public function load() { + if(loaded) return; + + if(expr != null) + interp.execute(expr); + + loaded = true; + } +} + class Main { public static function main() { - var parser = new Parser(); - var expr = parser.parseString(" - import haxe.ds.StringMap; + StaticInterp.pathResolver = Module.importFailedCallback; - //var outsideMap = new StringMap(); + var code = " + import pack.MyOtherClass; - class MyMap { - var map:StringMap; - public function new() { - map = new StringMap(); - } + class MyClass { + var moc:MyOtherClass; - public function get(s:String) { - return map.get(s); - } - - public function set(s:String, v:Int) { - map.set(s, v); - return v; + function new() { + trace(MyOtherClass.NUM); + MyOtherClass.NUM += 20; + moc = new MyOtherClass(50, 50); + trace(moc.iGotThis()); + trace(MyOtherClass.NUM); } } - var mm = new MyMap(); - trace(mm.set(':3', 9)); - trace(mm.get(':3')); - "); - - //expr = Analyzer.optimize(expr); - trace("------ HSCRIPT AST ------"); - trace(hscript.utils.ExprUtils.print(expr)); - trace("--------- OUTPUT --------"); - var interp:Interp = new Interp("Main.hx"); - interp.errorHandler = (error:Error) -> {Sys.println(error);} - interp.execute(expr); + var mc = new MyClass(50, 50); + "; + var module1 = new Module(code, "Main.hx"); + module1.load(); } } \ No newline at end of file