Skip to content

Commit b1cccc6

Browse files
committed
Merge CodenameCrew/hscript-improved codename-dev into codename-dev
Brings in the 5 upstream commits since ca71db2 (CodenameCrew#23 operator/loop/field lookup optimizations, the Bytes encoder rewrite, desugared safe navigation, number-parsing fixes, CI). Adaptations to keep ShadowScript's features working: - Interp: keep final-variable checks, abstract array access and operator overloading on top of upstream's `applyAssignOp`/`cachedAssignValue` refactor; keep `blockDepth` reset in `execute`; add `isFinal: false` to the pooled param/loop DeclaredVars. - Interp.walkHasFunctionLiteral: handle SE's multi-catch `ETry`, switch guards, `EImport` star flag, `EInterface`/`ETypedef`/`EAbstract` and enum functions. - Bytes: port upstream's new format to SE's AST: star imports, private classes, interfaces, typedefs, abstracts, multi-catch try, switch guards, enum functions and key-value `for` loops. - TestHScript: keep both test sets. The full test suite passes (19/19) with and without -D hscriptPos under --interp. Before this merge, one test failed on SE's branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mw8An2fyRMC59d5vRCVcyW
2 parents 83a5b76 + 6fa563f commit b1cccc6

10 files changed

Lines changed: 1208 additions & 432 deletions

File tree

‎.github/workflows/main.yml‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- 'codename-dev' # this is our working branch
7+
- 'feature/**'
8+
- 'overhaul/**'
9+
- 'update/**'
10+
# exclude
11+
- '!master'
12+
- '!custom-classes'
13+
- '!testing'
14+
- '!overflow'
15+
- 'temp-fix'
16+
- '!archive/**'
17+
pull_request:
18+
19+
env:
20+
HAXE_VERSION: 4.3.7
21+
22+
jobs:
23+
build:
24+
runs-on: ubuntu-24.04
25+
steps:
26+
- name: Pulling the new commit
27+
uses: actions/checkout@v7
28+
- name: Setting up Haxe
29+
uses: krdlab/setup-haxe@v2
30+
with:
31+
haxe-version: ${{ env.HAXE_VERSION }}
32+
- name: Install haxelib dependencies
33+
run: |
34+
haxelib --global update haxelib
35+
haxelib install hx3compat
36+
haxelib dev hscript .
37+
- name: Test interp
38+
run: |
39+
haxe bin/build-interp.hxml
40+
haxe bin/build-interp.hxml -D hscriptPos
41+
- name: Test neko
42+
run: |
43+
haxe bin/build-neko.hxml && neko bin/Test.n
44+
haxe bin/build-neko.hxml -D hscriptPos && neko bin/Test.n
45+
- name: Test js
46+
run: |
47+
haxe bin/build-js.hxml && node bin/Test.js
48+
haxe bin/build-js.hxml -D hscriptPos && node bin/Test.js
49+
# - name: Build hashlink from source
50+
# run: |
51+
# git clone https://github.com/HaxeFoundation/hashlink.git
52+
# cd hashlink
53+
# make hl
54+
# cd ..
55+
# - name: Test hl
56+
# run: |
57+
# haxe bin/build-hl.hxml && ./hashlink/hl bin/Test.hl
58+
# haxe bin/build-hl.hxml -D hscriptPos && ./hashlink/hl bin/Test.hl

‎TestHScript.hx‎

Lines changed: 225 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,103 @@ import hscript.Parser;
99
import hscript.Interp;
1010
import haxe.unit.*;
1111

12+
// TODO: tests regex
1213
class TestHScript extends TestCase {
13-
function assertScript(x,v:Dynamic,?vars : Dynamic, allowTypes=false, ?pos:haxe.PosInfos) {
14+
/**
15+
* @param x Code to run
16+
* @param v Expected return value
17+
* @param vars Variables to use
18+
* @param allowTypes Allow types parsing on script (default: false)
19+
* @param pos `trace()` call information
20+
*/
21+
function assertScript(x:String, v:Dynamic, ?vars:Dynamic, allowTypes = false, ?pos:haxe.PosInfos) {
22+
pos ??= assertPos;
1423
var p = new hscript.Parser();
1524
p.allowTypes = allowTypes;
25+
p.allowMetadata = true;
1626
var program = p.parseString(x);
17-
var bytes = hscript.Bytes.encode(program);
18-
program = hscript.Bytes.decode(bytes);
27+
var bytes = hscript.Bytes.encode(program, allowTypes);
28+
program = hscript.Bytes.decode(bytes, allowTypes);
1929
var interp = new hscript.Interp();
30+
// #if js
31+
// if( optimize ) interp = new hscript.JsInterp();
32+
// #end
2033
if( vars != null )
2134
for( v in Reflect.fields(vars) )
2235
interp.variables.set(v,Reflect.field(vars,v));
2336
var ret : Dynamic = interp.execute(program);
2437
assertEquals(v, ret, pos);
38+
assertPos.lineNumber += 1;
2539
}
2640

41+
/**
42+
* @param code Code to run
43+
* @param vars Variables to use
44+
* @param allowTypes Allow types parsing on script (default: true)
45+
* @param pos `trace()` call information
46+
*/
47+
function assertScriptRun(code:String, ?vars:Dynamic, ?allowTypes:Bool = true, ?pos:haxe.PosInfos) {
48+
pos ??= assertPos;
49+
var p = new hscript.Parser();
50+
p.allowTypes = allowTypes;
51+
p.allowMetadata = true;
52+
var program = p.parseString(code);
53+
var bytes = hscript.Bytes.encode(program, allowTypes);
54+
program = hscript.Bytes.decode(bytes, allowTypes);
55+
var interp = new hscript.Interp();
56+
// #if js
57+
// if( optimize ) interp = new hscript.JsInterp();
58+
// #end
59+
if (vars != null)
60+
for (v in Reflect.fields(vars))
61+
interp.variables.set(v, Reflect.field(vars, v));
62+
var success:Bool = false;
63+
try {
64+
interp.execute(program);
65+
success = true;
66+
}
67+
catch(e:hscript.Expr.Error) {
68+
trace('Error: ${Printer.errorToString(e)}');
69+
}
70+
assertTrue(success, pos);
71+
assertPos.lineNumber += 1;
72+
}
73+
74+
private final assertPos:haxe.PosInfos = { // for debugging
75+
fileName: "TestHScript.hx",
76+
lineNumber: 0,
77+
className: "TestHScript",
78+
methodName: "test"
79+
};
80+
2781
function test():Void {
82+
assertPos.lineNumber = 81;
2883
assertScript("0",0);
84+
assertScript("var o = { x : 1 }; o.x = 5; o.x", 5);
85+
assertScript("var o = { x : 1 }; o.x += 2; o.x *= 3; o.x", 9);
86+
assertScript("var a = 0; var b = 0; if( a == 0 ) a = 1 else b = 2; a + b", 1);
87+
assertScript("var a = 3; if( a > 2 ) return 5; 3", 5);
88+
assertScript("for( i in 0...10 ) if( i == 4 ) return i; -1", 4);
89+
assertScript("var o = { a : 2, b : 3 }; o.a + o.b", 5);
90+
assertScript("var o = { n : 1 }; var a = o.n++; o.n-- + a + o.n", 4);
2991
assertScript("0xFF", 255);
3092
#if !(php || python)
93+
#if haxe3
3194
assertScript("0xBFFFFFFF", 0xBFFFFFFF);
3295
assertScript("0x7FFFFFFF", 0x7FFFFFFF);
96+
#elseif !neko
97+
assertScript("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
98+
assertScript("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
99+
#end
33100
#end
34101
assertScript("-123",-123);
35102
assertScript("- 123",-123);
36103
assertScript("1.546",1.546);
37104
assertScript(".545",.545);
105+
assertScript("1e5",100000);
106+
assertScript("1.2e2",120);
107+
assertScript("100e-2",1);
108+
assertScript("1.2e-1",0.12);
38109
assertScript("'bla'","bla");
39110
assertScript("null",null);
40111
assertScript("true",true);
@@ -55,11 +126,12 @@ class TestHScript extends TestCase {
55126
assertScript("3 * 2 // + 5 \n + 6",12);
56127
assertScript("3 /* 2\n */ + 5",8);
57128
assertScript("[55,66,77][1]",66);
129+
assertScript("[11,22,33,][2]", 33);
58130
assertScript("var a = [55]; a[0] *= 2; a[0]",110);
59131
assertScript("x",55,{ x : 55 });
60132
assertScript("var y = 33; y",33);
61133
assertScript("{ 1; 2; 3; }",3);
62-
assertScript("{ var x = 0; } x",55,{ x : 55 });
134+
//assertScript("{ var x = 0; } x",55,{ x : 55 }); // this fails because of how locals are handled
63135
assertScript("o.val",55,{ o : { val : 55 } });
64136
assertScript("o.val",null,{ o : {} });
65137
assertScript("var a = 1; a++",1);
@@ -74,7 +146,7 @@ class TestHScript extends TestCase {
74146
assertScript("var t = 0; for( x in [1,2,3] ) t += x; t",6);
75147
assertScript("var a = new Array(); for( x in 0...5 ) a[x] = x; a.join('-')","0-1-2-3-4");
76148
assertScript("(function(a,b) return a + b)(4,5)",9);
77-
assertScript("var y = 0; var add = function(a) y += a; add(5); add(3); y", 8);
149+
//assertScript("var y = 0; var add = function(a) y += a; add(5); add(3); y", 8);
78150
assertScript("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10);
79151
assertScript("var a = false; do { a = true; } while (!a); a;",true);
80152
assertScript("var t = 0; for( x in 1...10 ) t += x; t", 45);
@@ -86,7 +158,7 @@ class TestHScript extends TestCase {
86158
assertScript("var a = 5/2; a",2.5);
87159
assertScript("{ x = 3; x; }", 3);
88160
assertScript("{ x : 3, y : {} }.x", 3);
89-
assertScript("function bug(){ \n }\nbug().x", null);
161+
assertScript("function bug() return { \n }\nbug().x", null);
90162
assertScript("1 + 2 == 3", true);
91163
assertScript("-2 == 3 - 5", true);
92164
assertScript("var x=-3; x", -3);
@@ -102,13 +174,95 @@ class TestHScript extends TestCase {
102174
assertScript("var f = (x:Int,y:Int) -> x + y; f(3,1)", 4);
103175
assertScript("var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
104176
assertScript("var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
105-
assertScript("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
106-
assertScript("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
177+
// skipping those until issues with locals are addressed
178+
//assertScript("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
179+
//assertScript("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
107180
assertScript("var a = 10; var b = 5; a - -b", 15);
108181
assertScript("var a = 10; var b = 5; a - b / 2", 7.5);
182+
assertScript("var a; a", null);
183+
assertScript("var a = 1, b = 5; a + b;", 6);
184+
assertScript("var a, b = 5; if (a == null) a = 2; a + b;", 7);
185+
assertScript("var a:Int; a", null, null, true);
186+
assertScript("var a:Int = 1, b:Int = 5; a + b;", 6, null, true);
187+
assertScript("var a:Int, b:Int = 5; if (a == null) a = 2; a + b;", 7, null, true);
188+
assertScript("false && xxx", false);
189+
assertScript("true || xxx", true);
190+
assertScript("[for( x in arr ) switch( x ) { case 1: 55; case 3: 66; default: 0; }].join(':')",'55:0:66',{ arr : [1,2,3] });
191+
assertScript("switch( x ) { case 1: 55; case 3: 66; default: 0; }",66 ,{ x : 3 });
192+
assertScript("var a = 1; switch( b ) { default: a = 2; }; a", 2, { b : 2 });
193+
assertScript("var a = 1; switch( b ) { case 2: a = 100; default: a = 2; }; a", 100, { b : 2 });
194+
assertScript("var a = 3; switch( b ) { case 2: if (a == 1) { a = 100; } else { a = 99; }; default: a = 2; }; a", 99, { b : 2 });
195+
}
196+
197+
function testNullFieldAccess():Void {
198+
assertPos.methodName = "testNullFieldAccess";
199+
assertPos.lineNumber = 208;
200+
var pt = {
201+
x : 10,
202+
call : function() { return 11; }
203+
};
204+
var vars = {
205+
ptnull : null,
206+
pt: pt,
207+
pt2null : {pt : null},
208+
pt2: {pt : pt}
209+
}
210+
assertScript("ptnull?.x", null, vars);
211+
assertScript("ptnull?.pt.x", null, vars);
212+
assertScript("ptnull?.call()", null, vars);
213+
assertScript("ptnull?.pt.call()", null, vars);
214+
assertScript("pt?.x", 10, vars);
215+
assertScript("pt?.call()", 11, vars);
216+
assertScript("pt2null?.pt", null, vars);
217+
assertScript("pt2null?.pt?.x", null, vars);
218+
assertScript("pt2null?.pt?.call()", null, vars);
219+
assertScript("pt2?.pt", pt, vars);
220+
assertScript("pt2?.pt?.x", 10, vars);
221+
assertScript("pt2?.pt?.call()", 11, vars);
222+
assertScript("ptnull ?? 12", 12, vars);
223+
assertScript("pt2null.pt ?? 12", 12, vars);
224+
assertScript("ptnull?.x ?? 12", 12, vars);
225+
assertScript("pt?.x ?? 12", 10, vars);
226+
assertScript("pt.y ?? 12", 12, vars);
227+
}
228+
229+
function testIsOperator():Void {
230+
assertPos.methodName = "testIsOperator";
231+
assertPos.lineNumber = 237;
232+
var vars = {
233+
String: String,
234+
Bool: Bool,
235+
Int: Int,
236+
Float: Float,
237+
Dynamic: Dynamic
238+
}
239+
assertScript("10 is Int", true, vars);
240+
assertScript("10.0 is Int", true, vars);
241+
assertScript("10.1 is Int", false, vars);
242+
assertScript("10 is Float", true, vars);
243+
assertScript("10.0 is Float", true, vars);
244+
assertScript("10.1 is Float", true, vars);
245+
assertScript("10 is String", false, vars);
246+
assertScript('"hscript" is String', true, vars);
247+
assertScript('"" is String', true, vars);
248+
assertScript('true is Bool', true, vars);
249+
assertScript('false is Bool', true, vars);
250+
assertScript('0 is Bool', false, vars);
251+
assertScript('1 is Bool', false, vars);
252+
assertScript('1 is Bool', false, vars);
253+
assertScript("10 is Dynamic", true, vars);
254+
assertScript("10.1 is Dynamic", true, vars);
255+
assertScript('"hscript" is Dynamic', true, vars);
256+
assertScript('null is Int', false, vars);
257+
assertScript('null is Float', false, vars);
258+
assertScript('null is String', false, vars);
259+
assertScript('null is Bool', false, vars);
260+
assertScript('null is Dynamic', false, vars);
109261
}
110262

111263
function testMap():Void {
264+
assertPos.methodName = "testMap";
265+
assertPos.lineNumber = 276;
112266
var objKey = { ok:true };
113267
var vars = {
114268
stringMap: ["foo" => "Foo", "bar" => "Bar"],
@@ -145,6 +299,7 @@ class TestHScript extends TestCase {
145299
for (key in vars.stringMap.keys()) keys.push(key);
146300
keys.join("_");
147301
}, vars);
302+
assertPos.lineNumber += 9;
148303
assertScript('stringMap.remove("foo"); stringMap.exists("foo");', false, vars);
149304
assertScript('stringMap["foo"] = "a"; stringMap["foo"] += "b"', 'ab', vars);
150305
assertEquals('ab', vars.stringMap['foo']);
@@ -351,6 +506,54 @@ class TestHScript extends TestCase {
351506
assertEquals(null, Interp.getMeta(obj, "nonexistent"));
352507
}
353508

509+
function testPackage():Void {
510+
assertPos.methodName = "testPackage";
511+
assertPos.lineNumber = 316;
512+
assertScriptRun('package;');
513+
assertScriptRun('package my.pack;');
514+
assertScriptRun('package my.other.pack;');
515+
}
516+
517+
function testImport() {
518+
assertPos.methodName = "testImport";
519+
assertPos.lineNumber = 324;
520+
assertScript('import haxe.ds.StringMap; StringMap', haxe.ds.StringMap);
521+
assertScript('import Math as HaxeMath; HaxeMath', Math);
522+
assertScript('using StringTools; " hscript ".trim()', "hscript");
523+
assertScript('using Lambda; [1, 2, 3, 4].find((i) -> return i == 2)', 2);
524+
}
525+
526+
function testClass() {
527+
assertPos.methodName = "testClass";
528+
assertPos.lineNumber = 333;
529+
assertScript("class M {public var a:Int; function new(a:Int){this.a = a;}} var m = new M(10); m.a", 10, null, true);
530+
assertScript("class M {public static var N:Int = 10; public static function S(n:Int){return N += n;}} M.S(10)", 20, null, true);
531+
assertScript("class M {public var a:Int; function new(a:Int){this.a = a;}} class MA extends M {function new() {super(20);}} var ma = new MA(); ma.a", 20, null, true);
532+
assertScript("class M extends TestClass {function new() {super(16, 16);} override function getSum() {return super.getSum() * 2;}} var m = new M(); m.getSum()", 64, {"TestClass": TestClass}, true);
533+
}
534+
535+
function testProperty() {
536+
assertPos.methodName = "testProperty";
537+
assertPos.lineNumber = 342;
538+
assertScript("var n(default, set):Int = 16; function set_n(v):Int {return n = v;} n += 16; n", 32, null, true);
539+
assertScript("var p(get, set):Int; var _p:Int = 16; function get_p():Int {return _p;} function set_p(v):Int {return _p = v;} p += 16; p", 32, null, true);
540+
assertScript("@:isVar var m(get, set):String = 'hscript'; function get_m() return m; function set_m(v) return m = v; m = 'hscript-improved'; m", 'hscript-improved', null, true);
541+
}
542+
543+
function testEnum() {
544+
assertPos.methodName = "testEnum";
545+
assertPos.lineNumber = 350;
546+
assertScript("enum E {INTEGER(i:Int); DECIMAL(f:Float); NULL;} var r = switch(E.INTEGER(10)) {case E.INTEGER(i): i; default: null;}; r", 10, null, true);
547+
assertScript("enum abstract HttpStatus(Int) { var NOTFOUND = 404; var OK = 200; } var s = HttpStatus.OK; s", 200, null, true);
548+
}
549+
550+
function testCast() {
551+
assertPos.methodName = "testCast";
552+
assertPos.lineNumber = 357;
553+
assertScript('var a = cast b; a', 10, {b: 10});
554+
assertScript('cast(m, Map).get(1)', 64, {m: [1 => 64]});
555+
}
556+
354557
static function main() {
355558
#if ((haxe_ver < 4) && php)
356559
// uncaught exception: The each() function is deprecated. This message will be suppressed on further calls (errno: 8192)
@@ -360,6 +563,7 @@ class TestHScript extends TestCase {
360563

361564
var runner = new TestRunner();
362565
runner.add(new TestHScript());
566+
//runner.add(new TestHScript(true)); // TODO: JS Interp
363567
var succeed = runner.run();
364568

365569
#if sys
@@ -372,4 +576,17 @@ class TestHScript extends TestCase {
372576
#end
373577
}
374578

579+
}
580+
@:build(hscript.macros.ClassExtendMacro.build())
581+
class TestClass {
582+
private var a:Int;
583+
private var b:Int;
584+
public function new(a:Int, b:Int) {
585+
this.a = a;
586+
this.b = b;
587+
}
588+
589+
public function getSum() {
590+
return a + b;
591+
}
375592
}

‎bin/build-cpp.hxml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
bin/build-each.hxml
2-
-cpp bin
2+
-cpp bin/cpp

0 commit comments

Comments
 (0)