Compare commits

..
Author SHA1 Message Date
Jaime Humberto Macias Bustamante 9db1cb9ca1 Just found out i have to rewrite the entire thing from scratch for the sake of optimizations...
:'3
2025-05-29 15:25:26 -07:00
Jaime Humberto Macias Bustamante 1bcbce2d5b Property combination check
Also, it parses 'dynamic'.

Still working on getter/setter for variables
2025-05-16 14:10:20 -07:00
Jaime Humberto Macias Bustamante 22d6404799 getter/setter for variables 2025-05-15 01:17:11 -07:00
Jaime Humberto Macias Bustamante 932e643dc9 Better access context (IN PROGRESS)
Also, throws an error if a variable with "(get, set)" doesn't have a type or a default value.
2025-05-15 01:07:29 -07:00
Jaime Humberto Macias Bustamante 1cc678fbef Reverting getter/setter to remake
"clean this up, it sucks, i hate it"
- ne_eo
2025-05-14 11:16:02 -07:00
Jaime Humberto Macias Bustamante 122fd03a29 solved possible issues + field check improved 2025-05-13 20:47:14 -07:00
Jaime Humberto Macias Bustamante a3b153eb99 Update Interp.hx 2025-05-13 15:35:03 -07:00
Jaime Humberto Macias Bustamante b36d8b167f sorry, I forgot :'3 2025-05-13 15:24:25 -07:00
Jaime Humberto Macias Bustamante cae6d5072c Improved "null" handling 2025-05-13 12:04:49 -07:00
Jaime Humberto Macias Bustamante 8dce40354a "null" access handling + get/set for variables
- It handles "null" allowing access only inside of the class.

- "final" variables can be initialized in the constructor if they doesn't have a default value.

- "var a(get, set):Int" is parsed outside of Custom Class, but for now it doesn't call their accessors.

almost finished :3
2025-05-13 00:38:11 -07:00
Jaime Humberto Macias Bustamante 19b5b2b049 getter, setter and final for Custom Classes
full support for "var a(get, set):Int".
Also, it takes into account final vars

Soon for inline custom classes

[UNTESTED]
2025-05-12 11:27:43 -07:00
Jaime Humberto Macias Bustamante 64ee722124 Allow trailing array commas (https://github.com/HaxeFoundation/hscript/pull/139) 2025-05-11 12:36:55 -07:00
Jaime Humberto Macias Bustamante 77282e3b7b Update Parser.hx 2025-05-11 12:15:45 -07:00
Jaime Humberto Macias Bustamante 927b72c91a nvm, backward compatibility 2025-05-11 11:42:39 -07:00
Jaime Humberto Macias Bustamante 10b67def74 Moved "resolveField" to "hget" + fixes 2025-05-11 00:27:40 -07:00
Jaime Humberto Macias Bustamante 3f1f0e7234 Simplified Custom Class assignment 2025-05-08 16:25:16 -07:00
Jaime Humberto Macias Bustamante c7160d05fd super Custom Class check + null check optimization 2025-05-07 11:03:22 -07:00
Jaime Humberto Macias Bustamante 05577f7559 Cleanup 2025-05-06 11:58:38 -07:00
Jaime Humberto Macias Bustamante e26385f591 Fixed superClass calling + special operations
Fixed not being able to do "+=" on Custom Classes and getters and setters on constructor.

Also, fixed not being able to call functions from a CustomClass super class.
2025-05-05 21:42:02 -07:00
Jaime Humberto Macias Bustamante 33583e751f Small optimizations 2025-05-05 14:28:44 -07:00
Jaime Humberto Macias Bustamante 742b40c86c The Beginning of the End (Old Custom Class) 2025-05-04 23:20:56 -07:00
Jaime Humberto Macias Bustamante bf602f758b Typedefs for Custom Classes
Only works as an import method, for example: `typedef OtherShader = funkin.backend.shaders.CustomShader;`.
2025-05-04 20:13:01 -07:00
Jaime Humberto Macias Bustamante 34b07378fd Fix 1e parsing
Fix 1e5, 1e-5 evaluated as Infinity (https://github.com/HaxeFoundation/hscript/pull/132)
2025-04-10 14:51:36 -07:00
Jaime Humberto Macias Bustamante 6622220415 Array parse fix
Do not allow arrays without comma between values (https://github.com/HaxeFoundation/hscript/pull/138)
2025-04-09 11:57:04 -07:00
Jaime Humberto Macias Bustamante ca0599b180 capture variables for inline Custom Classes 2025-04-09 11:43:06 -07:00
Jaime Humberto Macias Bustamante d055203559 Public vars for Custom Classes in scripts
Uses public variables from the same scope as where the class was defined.
2025-04-08 11:48:31 -07:00
Jaime Humberto Macias Bustamante 65b43ebe78 Custom Classes inside of scripts 2025-04-07 11:52:49 -07:00
Jaime Humberto Macias Bustamante 9aa3481f62 superClass thing improvement
STILL IN PROGRESS
2025-04-05 19:48:59 -07:00
Jaime Humberto Macias Bustamante e087521a36 Update CustomClassDecl.hx
oops
2025-04-03 16:08:25 -07:00
Jaime Humberto Macias Bustamante f43221c3bb "Type" for Custom Classes - Part II
- Move the superClass instantiation to CustomClassDecl
- Imported classes available for Static Functions

Warning: untested code.
2025-03-27 16:02:06 -07:00
Jaime Humberto Macias Bustamante e71f5a0471 "Type" for Custom Classes 2025-03-24 20:28:29 -07:00
Jaime Humberto Macias Bustamante 73c17e51dc Enums, new import blocklist and redirects
warning: untested
2025-03-24 11:58:56 -07:00
Jaime Humberto Macias Bustamante e6b5d6af49 ".bind" support + "package;" 2025-03-04 20:26:32 -07:00
Jaime Humberto Macias Bustamante 9833c61f78 Using and Enums
Based on Enums and Using system from hscript-iris, mostly made by ne_eo.
2025-03-03 21:53:02 -07:00
Jaime Humberto Macias Bustamante a1ee770794 oops :3 2025-02-04 00:57:53 -07:00
Jaime Humberto Macias Bustamante 270087fc62 Update Parser.hx
forgot to put the "EIgnore" work
2025-02-03 20:35:10 -07:00
Jaime Humberto Macias Bustamante bd0ae3fbe1 Typedef support
Original Implementation by ne_eo for hscript-iris.

For now only works for normal scripts and making import redirects. Soon for custom classes.
2025-02-03 00:00:15 -07:00
Jaime Humberto Macias Bustamante 241d97aed0 Import aliases for Custom Classes
Import aliases support for Custom Classes. This allows you to import classes and other custom classes with an alias inside of a Custom Class.

This includes some improvements on "parseModuleDecl()"
2025-02-01 21:00:41 -07:00
Jaime Humberto Macias Bustamante b25ecb08bc Function Call reimplemented
The previous function call system has a serious issue. It can not differentiate between class fields and function argument fields.
2025-01-21 22:07:41 -07:00
Jaime Humberto Macias Bustamante c45373aa55 Custom Classes Rework 2025-01-21 00:27:11 -07:00
Ne_Eo fe673c21b2 Forgot to change this back to false 2025-01-19 21:08:40 +01:00
Ne_Eo 77420e9873 Bug fix with locals + Compatibility improvement 2025-01-19 21:00:20 +01:00
Ne_Eo e4789d9c40 Fix typo 2024-12-16 03:54:00 +01:00
Ne_Eo 9f0937b5c3 Merge branch 'master' into codename-dev 2024-11-15 15:25:22 +01:00
Ne_Eo bb46d45d56 Import Redirects 2024-11-15 15:25:04 +01:00
Ne_Eo ad18c12326 Small change to support more platforms 2024-11-15 14:51:04 +01:00
Redar13andGitHub 3a5c4f2164 Fix ending in with #end in the file (#5)
* Update Parser.hx

* Update Parser.hx
2024-11-06 16:39:09 +01:00
Ne_Eo b683c981b0 no more safe casts 2024-10-18 02:16:50 +02:00
Ne_Eo 9b614421b6 Remove haxe2 support 2024-10-18 01:57:19 +02:00
Ne_Eo 26659db833 Small fix to allow StringMap and such (testing required) 2024-10-04 23:15:50 +02:00
Ne_Eo 6b5a47b017 Fix isBypassAccessor on get resolve 2024-09-04 00:11:19 +02:00
Ne_Eo 3ec4fc9f0b Fixed bug with assign 2024-09-04 00:06:57 +02:00
Ne_Eo 5d2a99d40e More Optimizations 2024-09-03 15:28:33 +02:00
Ne_Eo 1d06905fc5 Optimizations 2024-09-03 14:12:11 +02:00
Ne_Eo 3f98441918 Fixed bugs and ram improvements 2024-09-03 13:42:25 +02:00
Ne_Eo f8e00859cd Inline parses correctly now 2024-09-03 13:10:13 +02:00
Ne_Eo 933c9cb5e3 final function parsing 2024-09-03 05:05:21 +02:00
Ne_Eo 11b3a52287 pain and suffering 2024-09-03 04:58:14 +02:00
Ne_Eo 07f7a75759 small todo 2024-09-03 02:04:40 +02:00
Ne_Eo 644c75094e Added private syntax sugar, custom class fix 2024-09-03 02:03:31 +02:00
Ne_Eo a565cb7b2a I think i fixed classes 2024-09-03 01:21:39 +02:00
Ne_Eo 6db0f7c60b Fix bug 2024-08-31 20:11:48 +02:00
Ne_Eo bfe0b0e99e Simplified code 2024-08-24 21:59:22 +02:00
65 changed files with 4966 additions and 6586 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
/hscript.swf /hscript.swf
/release.zip /release.zip
dump/*
tests/bin/* tests/bin/*
+1 -1
View File
@@ -3,7 +3,7 @@ hscript-improved
How to install How to install
``` ```
haxelib git hscript-improved https://github.com/FNF-CNE-Devs/hscript-improved.git haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
``` ```
To enable custom classes support you have to do this in project.xml To enable custom classes support you have to do this in project.xml
+5 -9
View File
@@ -26,18 +26,17 @@ class TestHScript extends TestCase {
assertScript("0",0); assertScript("0",0);
assertScript("0xFF", 255); assertScript("0xFF", 255);
#if !(php || python) #if !(php || python)
#if haxe3
assertScript("0xBFFFFFFF", 0xBFFFFFFF); assertScript("0xBFFFFFFF", 0xBFFFFFFF);
assertScript("0x7FFFFFFF", 0x7FFFFFFF); assertScript("0x7FFFFFFF", 0x7FFFFFFF);
#elseif !neko
assertScript("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
assertScript("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
#end
#end #end
assertScript("-123",-123); assertScript("-123",-123);
assertScript("- 123",-123); assertScript("- 123",-123);
assertScript("1.546",1.546); assertScript("1.546",1.546);
assertScript(".545",.545); assertScript(".545",.545);
assertScript("1e5",100000);
assertScript("1.2e2",120);
assertScript("100e-2",1);
assertScript("1.2e-1",0.12);
assertScript("'bla'","bla"); assertScript("'bla'","bla");
assertScript("null",null); assertScript("null",null);
assertScript("true",true); assertScript("true",true);
@@ -58,6 +57,7 @@ class TestHScript extends TestCase {
assertScript("3 * 2 // + 5 \n + 6",12); assertScript("3 * 2 // + 5 \n + 6",12);
assertScript("3 /* 2\n */ + 5",8); assertScript("3 /* 2\n */ + 5",8);
assertScript("[55,66,77][1]",66); assertScript("[55,66,77][1]",66);
assertScript("[11,22,33,][2]", 33);
assertScript("var a = [55]; a[0] *= 2; a[0]",110); assertScript("var a = [55]; a[0] *= 2; a[0]",110);
assertScript("x",55,{ x : 55 }); assertScript("x",55,{ x : 55 });
assertScript("var y = 33; y",33); assertScript("var y = 33; y",33);
@@ -81,11 +81,7 @@ class TestHScript extends TestCase {
assertScript("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10); assertScript("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10);
assertScript("var a = false; do { a = true; } while (!a); a;",true); assertScript("var a = false; do { a = true; } while (!a); a;",true);
assertScript("var t = 0; for( x in 1...10 ) t += x; t", 45); assertScript("var t = 0; for( x in 1...10 ) t += x; t", 45);
#if haxe3
assertScript("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45); assertScript("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45);
#else
assertScript("var t = 0; for( x in new IntIter(1,10) ) t +=x; t", 45);
#end
assertScript("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",790); assertScript("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",790);
assertScript("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",56); assertScript("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",56);
assertScript("var i=2; if( true ) --i; i",1); assertScript("var i=2; if( true ) --i; i",1);
+2 -2
View File
@@ -1,4 +1,4 @@
--macro keep('IntIterator') --macro keep('IntIterator')
--macro hscript.macros.UsingHandler.init() --macro hscript.macros.AbstractHandler.init()
--macro hscript.macros.UsingMacro.init()
--macro hscript.macros.ClassExtendMacro.init() --macro hscript.macros.ClassExtendMacro.init()
--define HSCRIPT_NEW
+7 -7
View File
@@ -153,11 +153,11 @@ class Async {
} }
inline function fun(arg:String, e, ?name) { inline function fun(arg:String, e, ?name) {
return mk(EFunction([{ name : arg, t : null }], e, name), e); return mk(EFunction([{ name : arg, t : null, opt: false, value: null }], e, name), e);
} }
inline function funs(arg:Array<String>, e, ?name) { inline function funs(arg:Array<String>, e, ?name) {
return mk(EFunction([for( a in arg ) { name : a, t : null }], e, name), e); return mk(EFunction([for( a in arg ) { name : a, t : null, opt: false, value: null }], e, name), e);
} }
inline function block(arr:Array<Expr>, e) { inline function block(arr:Array<Expr>, e) {
@@ -260,7 +260,7 @@ class Async {
defineVar(name, Defined); defineVar(name, Defined);
for( a in args ) for( a in args )
defineVar(a.name, Defined); defineVar(a.name, Defined);
args.unshift( { name : "_onEnd", t : null } ); args.unshift( { name : "_onEnd", t : null, opt: false, value: null } );
var frest = ident("_onEnd",e); var frest = ident("_onEnd",e);
var oldFun = currentFun; var oldFun = currentFun;
currentFun = name; currentFun = name;
@@ -417,10 +417,10 @@ class Async {
if( currentLoop == null ) throw "Continue outside loop"; if( currentLoop == null ) throw "Continue outside loop";
return block([retNull(currentLoop, e), mk(EReturn(),e)], e); return block([retNull(currentLoop, e), mk(EReturn(),e)], e);
case ESwitch(v, cases, def): case ESwitch(v, cases, def):
var cases = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ]; var cases:Array<SwitchCase> = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
return toCps(v, mk(EFunction([ { name : "_c", t : null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit ); return toCps(v, mk(EFunction([ { name : "_c", t : null, opt: false, value: null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
case EThrow(v): case EThrow(v):
return toCps(v, mk(EFunction([ { name : "_v", t : null } ], mk(EThrow(v),v)), v), exit); return toCps(v, mk(EFunction([ { name : "_v", t : null, opt: false, value: null } ], mk(EThrow(v),v)), v), exit);
case EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata case EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata
return toCps(e, rest, exit); return toCps(e, rest, exit);
//case EDoWhile(_), ETry(_), ECall(_): //case EDoWhile(_), ETry(_), ECall(_):
@@ -436,7 +436,7 @@ class AsyncInterp extends Interp {
public function setContext( api : Dynamic ) { public function setContext( api : Dynamic ) {
var funs = new Array(); var funs = [];
for( v in variables.keys() ) for( v in variables.keys() )
if( Reflect.isFunction(variables.get(v)) ) if( Reflect.isFunction(variables.get(v)) )
funs.push({ v : v, obj : null }); funs.push({ v : v, obj : null });
+17 -26
View File
@@ -27,7 +27,7 @@ class Bytes {
var bin : haxe.io.Bytes; var bin : haxe.io.Bytes;
var bout : haxe.io.BytesBuffer; var bout : haxe.io.BytesBuffer;
var pin : Int; var pin : Int;
var hstrings : #if haxe3 Map<String,Int> #else Hash<Int> #end; var hstrings : Map<String,Int>;
var strings : Array<String>; var strings : Array<String>;
var nstrings : Int; var nstrings : Int;
@@ -35,7 +35,7 @@ class Bytes {
this.bin = bin; this.bin = bin;
pin = 0; pin = 0;
bout = new haxe.io.BytesBuffer(); bout = new haxe.io.BytesBuffer();
hstrings = #if haxe3 new Map() #else new Hash() #end; hstrings = new Map();
strings = [null]; strings = [null];
nstrings = 1; nstrings = 1;
} }
@@ -44,7 +44,7 @@ class Bytes {
var vid = hstrings.get(v); var vid = hstrings.get(v);
if( vid == null ) { if( vid == null ) {
if( nstrings == 256 ) { if( nstrings == 256 ) {
hstrings = #if haxe3 new Map() #else new Hash() #end; hstrings = new Map();
nstrings = 1; nstrings = 1;
} }
hstrings.set(v,nstrings); hstrings.set(v,nstrings);
@@ -85,15 +85,6 @@ class Bytes {
bout.addByte(1); bout.addByte(1);
doEncodeInt(v); doEncodeInt(v);
} }
#if !haxe3
case CInt32(v):
bout.addByte(4);
var mid = haxe.Int32.toInt(haxe.Int32.and(v,haxe.Int32.ofInt(0xFFFFFF)));
bout.addByte(mid & 0xFF);
bout.addByte((mid >> 8) & 0xFF);
bout.addByte(mid >> 16);
bout.addByte(haxe.Int32.toInt(haxe.Int32.ushr(v, 24)));
#end
case CFloat(f): case CFloat(f):
bout.addByte(2); bout.addByte(2);
doEncodeString(Std.string(f)); doEncodeString(Std.string(f));
@@ -120,13 +111,6 @@ class Bytes {
CFloat( Std.parseFloat(doDecodeString()) ); CFloat( Std.parseFloat(doDecodeString()) );
case 3: case 3:
CString( doDecodeString() ); CString( doDecodeString() );
#if !haxe3
case 4:
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16);
var j = bin.get(pin+3);
pin += 4;
CInt32(haxe.Int32.or(haxe.Int32.ofInt(i), haxe.Int32.shl(haxe.Int32.ofInt(j), 24)));
#end
default: default:
throw "Invalid code "+bin.get(pin-1); throw "Invalid code "+bin.get(pin-1);
} }
@@ -140,10 +124,17 @@ class Bytes {
#end #end
bout.addByte(Type.enumIndex(e)); bout.addByte(Type.enumIndex(e));
switch( e ) { switch( e ) {
case EIgnore(_):
case EImport(c): case EImport(c):
// TODO // TODO
case EClass(_, _, _, _): case EClass(_, _, _, _):
// TODO // TODO
case ERedirect(_, _):
// TODO
case EUsing(name):
// TODO
case EEnum(name, fields):
// TODO
case EConst(c): case EConst(c):
doEncodeConst(c); doEncodeConst(c);
case EIdent(v): case EIdent(v):
@@ -278,7 +269,7 @@ class Bytes {
case 3: case 3:
EParent(doDecode()); EParent(doDecode());
case 4: case 4:
var a = new Array(); var a = [];
for( i in 0...bin.get(pin++) ) for( i in 0...bin.get(pin++) )
a.push(doDecode()); a.push(doDecode());
EBlock(a); EBlock(a);
@@ -295,7 +286,7 @@ class Bytes {
EUnop(op,prefix,doDecode()); EUnop(op,prefix,doDecode());
case 8: case 8:
var e = doDecode(); var e = doDecode();
var params = new Array(); var params = [];
for( i in 0...bin.get(pin++) ) for( i in 0...bin.get(pin++) )
params.push(doDecode()); params.push(doDecode());
ECall(e,params); ECall(e,params);
@@ -317,7 +308,7 @@ class Bytes {
case 14: case 14:
var params = new Array<Argument>(); var params = new Array<Argument>();
for( i in 0...bin.get(pin++) ) for( i in 0...bin.get(pin++) )
params.push({ name : doDecodeString() }); params.push({ name : doDecodeString(), opt: false, value: null, t: null });
var e = doDecode(); var e = doDecode();
var name = doDecodeString(); var name = doDecodeString();
EFunction(params,e,(name == "") ? null: name); EFunction(params,e,(name == "") ? null: name);
@@ -327,13 +318,13 @@ class Bytes {
var e = doDecode(); var e = doDecode();
EArray(e,doDecode()); EArray(e,doDecode());
case 17: case 17:
var el = new Array(); var el = [];
for( i in 0...bin.get(pin++) ) for( i in 0...bin.get(pin++) )
el.push(doDecode()); el.push(doDecode());
EArrayDecl(el); EArrayDecl(el);
case 18: case 18:
var cl = doDecodeString(); var cl = doDecodeString();
var el = new Array(); var el = [];
for( i in 0...bin.get(pin++) ) for( i in 0...bin.get(pin++) )
el.push(doDecode()); el.push(doDecode());
ENew(cl,el); ENew(cl,el);
@@ -344,7 +335,7 @@ class Bytes {
var v = doDecodeString(); var v = doDecodeString();
ETry(e,v,null,doDecode()); ETry(e,v,null,doDecode());
case 21: case 21:
var fl = new Array(); var fl:Array<ObjectField> = [];
for( i in 0...bin.get(pin++) ) { for( i in 0...bin.get(pin++) ) {
var name = doDecodeString(); var name = doDecodeString();
var e = doDecode(); var e = doDecode();
@@ -358,7 +349,7 @@ class Bytes {
ETernary(cond, e1, e2); ETernary(cond, e1, e2);
case 23: case 23:
var e = doDecode(); var e = doDecode();
var cases = []; var cases:Array<SwitchCase> = [];
while( true ) { while( true ) {
var v = doDecode(); var v = doDecode();
if( v == null ) break; if( v == null ) break;
+9
View File
@@ -12,6 +12,11 @@ class Config {
"openfl.display.BlendMode", "openfl.display.BlendMode",
]; ];
// Runs support for using in specific classes.
public static final ALLOWED_USING = [
];
// Incase any of your files fail // Incase any of your files fail
// These are the module names // These are the module names
public static final DISALLOW_CUSTOM_CLASSES = [ public static final DISALLOW_CUSTOM_CLASSES = [
@@ -21,4 +26,8 @@ class Config {
public static final DISALLOW_ABSTRACT_AND_ENUM = [ public static final DISALLOW_ABSTRACT_AND_ENUM = [
]; ];
public static final DISALLOW_USING = [
];
} }
+135 -45
View File
@@ -2,9 +2,9 @@ package hscript;
import hscript.Interp.DeclaredVar; import hscript.Interp.DeclaredVar;
import hscript.utils.UnsafeReflect; import hscript.utils.UnsafeReflect;
using StringTools; using StringTools;
@:deprecated('Switch to new Custom Class instead')
class CustomClassHandler implements IHScriptCustomConstructor { class CustomClassHandler implements IHScriptCustomConstructor {
public static var staticHandler = new StaticHandler(); public static var staticHandler = new StaticHandler();
@@ -23,104 +23,146 @@ class CustomClassHandler implements IHScriptCustomConstructor {
this.extend = extend; this.extend = extend;
this.interfaces = interfaces; this.interfaces = interfaces;
this.cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX'); //this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
if(cl == null) if(cl == null)
ogInterp.error(EInvalidClass(extend)); ogInterp.error(EInvalidClass(extend));
} }
public function hnew(args:Array<Dynamic>):Dynamic { public function hnew(args:Array<Dynamic>):Dynamic {
#if !HSCRIPT_NO_INT_VARS // TODO: clean this up, it sucks, i hate it
throw "todo: do all this lmao -lunar"; // TODO: make static vars work correctly
return null;
#else
var interp = new Interp(); var interp = new Interp();
interp.errorHandler = ogInterp.errorHandler; interp.errorHandler = ogInterp.errorHandler;
var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args); var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
var __capturedLocals = ogInterp.duplicate(ogInterp.locals); //var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
var capturedLocals:Map<String, DeclaredVar> = []; //var capturedLocals:Map<String, DeclaredVar> = [];
for(k=>e in __capturedLocals) //for(k=>e in __capturedLocals)
if (e != null && e.depth <= 0) // if (e != null && e.depth <= 0)
capturedLocals.set(k, e); // capturedLocals.set(k, e);
var disallowCopy = Type.getInstanceFields(cl); var disallowCopy = Type.getInstanceFields(cl);
for (key => value in capturedLocals) { _class.__real_fields = disallowCopy;
if(!disallowCopy.contains(key)) {
interp.locals.set(key, {r: value, depth: -1}); // todo: make it so you can use variables from the same scope as where the class was defined
}
} //for (key => value in capturedLocals) {
// if(!disallowCopy.contains(key)) {
// interp.locals.set(key, {r: value, depth: -1});
// }
//}
for (key => value in ogInterp.variables) { for (key => value in ogInterp.variables) {
if(!disallowCopy.contains(key)) { if(!disallowCopy.contains(key)) {
interp.variables.set(key, value); interp.variables.set(key, value);
} }
} }
for(key => value in ogInterp.customClasses) {
if(!disallowCopy.contains(key)) {
interp.customClasses.set(key, value);
}
}
// todo: clone static vars, but make it so setting it only sets it on the class
// todo: clone public vars
var comparisonMap = new Map(); //trace("Before: " + [for(key => value in interp.variables) key]);
interp.variables.set("super", staticHandler);
var comparisonMap:Map<String, Dynamic> = [];
for(key => value in interp.variables) { for(key => value in interp.variables) {
comparisonMap.set(key, value); comparisonMap.set(key, value);
} }
_class.__custom__variables = interp.variables; //_class.__custom__variables = interp.variables;
//trace(fields); //trace(fields);
for(expr in fields) { for(expr in fields) {
@:privateAccess @:privateAccess
interp.exprReturn(expr); interp.exprReturn(expr);
} }
interp.variables.set("super", staticHandler); //trace("After: " + [for(key => value in interp.variables) key]);
_class.__interp = interp; // TODO: Remove
interp.scriptObject = _class;
// get only variables that were not set before // get only variables that were not set before
var classVariables = [for(key => value in interp.variables) if(!comparisonMap.exists(key) || comparisonMap[key] != value) key => value]; var classVariables = [
for(variable => value in classVariables) { for(key => value in interp.variables)
if(variable == "this" || variable == "super") continue; if(!comparisonMap.exists(key) || comparisonMap[key] != value)
@:privateAccess key => value
if(!interp.__instanceFields.contains(variable)) { ];
interp.__instanceFields.push(variable); //for(variable => value in classVariables) {
} // if(variable == "this" || variable == "super" || variable == "new") continue;
} // @:privateAccess
// if(!interp.__instanceFields.contains(variable)) {
// interp.__instanceFields.push(variable);
// }
//}
//trace([for(key => value in classVariables) key]); //_class.__class__fields = [for(key => value in classVariables) key];
//trace(_class.__class__fields);
//@:privateAccess //@:privateAccess
//trace(interp.__instanceFields); //trace(interp.__instanceFields);
_class.__allowSetGet = false; //_class.__interp = interp;
//_class.__allowSetGet = false;
interp.scriptObject = _class;
for(variable => value in interp.variables) { for(variable => value in interp.variables) {
if(variable == "this") continue; if(variable == "this" || variable == "super" || variable == "new") continue;
if(variable.startsWith("set_") || variable.startsWith("get_")) { if(variable.startsWith("set_") || variable.startsWith("get_")) {
_class.__allowSetGet = true; //_class.__allowSetGet = true;
} }
} }
var newFunc = interp.variables.get("new"); var newFunc = interp.variables.get("new");
if(newFunc != null) { if(newFunc != null) {
var comparisonMap:Map<String, Dynamic> = [];
for(key => value in interp.variables) {
comparisonMap.set(key, value);
}
UnsafeReflect.callMethodUnsafe(null, newFunc, args); UnsafeReflect.callMethodUnsafe(null, newFunc, args);
// get only variables that were not set before
var classVariables = [
for(key => value in interp.variables)
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
key => value
];
for(variable => value in classVariables) {
if(variable == "this" || variable == "super" || variable == "new") continue;
@:privateAccess
if(!interp.__instanceFields.contains(variable)) {
interp.__instanceFields.push(variable);
}
/*
if(!_class.__class__fields.contains(variable)) {
_class.__class__fields.push(variable);
}
*/
}
} }
var newFunc = interp.variables.get("new");
if(newFunc != null) {
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
}
return _class; return _class;
#end
} }
public function toString():String { public function toString():String {
return name; return name;
} }
} }
/*
class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour { class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
public var __interp:Interp; // TODO: Remove public var __interp:Interp;
public var __custom__variables:Map<String, Dynamic>; public var __customClass:hscript.customclass.CustomClass;
public var __allowSetGet:Bool = true; public var __allowSetGet:Bool = true;
public var __custom__variables:Map<String, Dynamic>;
public var __real_fields:Array<String>;
public var __class__fields:Array<String>;
public function hset(name:String, val:Dynamic):Dynamic { public function hset(name:String, val:Dynamic):Dynamic {
if(__allowSetGet && __custom__variables.exists("set_" + name)) if(__allowSetGet && __custom__variables.exists("set_" + name))
@@ -129,14 +171,19 @@ class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptC
__custom__variables.set(name, val); __custom__variables.set(name, val);
return val; return val;
} }
UnsafeReflect.setProperty(this, name, val); if(__real_fields.contains(name)) {
return UnsafeReflect.field(this, name); UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
__custom__variables.set(name, val);
return val;
} }
public function hget(name:String):Dynamic { public function hget(name:String):Dynamic {
if(__allowSetGet && __custom__variables.exists("get_" + name)) if(__allowSetGet && __custom__variables.exists("get_" + name))
return __callGetter(name); return __callGetter(name);
if (__custom__variables.exists(name)) if (__custom__variables.exists(name))
return __custom__variables.get(name); return __custom__variables.get(name);
return UnsafeReflect.getProperty(this, name); return UnsafeReflect.getProperty(this, name);
} }
@@ -155,6 +202,49 @@ class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptC
} }
} }
*/
/**
* This is for backwards compatibility with old hscript-improved, since some scripts use it
*/
@:dox(hide)
@:keep
class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustomAccessBehaviour {
public var __interp:Interp;
public var __allowSetGet:Bool = true;
public var __allowPrivateAccess:Bool = true;
public function hset(name:String, val:Dynamic):Dynamic {
var variables = __interp.variables;
if(__allowSetGet && variables.exists("set_" + name))
return __callSetter(name, val);
variables.set(name, val);
return val;
}
public function hget(name:String):Dynamic {
var variables = __interp.variables;
if(__allowSetGet && variables.exists("get_" + name))
return __callGetter(name);
return variables.get(name);
}
public function __callGetter(name:String):Dynamic {
__allowSetGet = false;
var v = __interp.variables.get("get_" + name)();
__allowSetGet = true;
return v;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
__allowSetGet = false;
var v = __interp.variables.get("set_" + name)(val);
__allowSetGet = true;
return v;
}
}
/*
final class StaticHandler { final class StaticHandler {
public function new() {} public function new() {}
} }
*/
-83
View File
@@ -1,83 +0,0 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
#if hscriptPos
class Error_ {
public var e : ErrorDef;
public var pmin : Int;
public var pmax : Int;
public var origin : String;
public var line : Int;
public function new(e, pmin, pmax, origin, line) {
this.e = e;
this.pmin = pmin;
this.pmax = pmax;
this.origin = origin;
this.line = line;
}
public function toString(): String {
return Printer.errorToString(this);
}
}
enum ErrorDef
#else
enum Error_
#end
{
EInvalidChar( c : Int );
EUnexpected( s : String );
EUnterminatedString;
EUnterminatedComment;
EUnterminatedRegex;
EInvalidPreprocessor( msg : String );
EUnknownVariable( v : String );
EInvalidIterator( v : String );
EInvalidType( t : String );
EInvalidOp( op : String );
EInvalidAccess( f : String, ?on : String );
ECustom( msg : String );
EPreset( msg : ErrorMessage );
EInvalidClass( className : String);
EAlreadyExistingClass( className : String);
EInvalidEscape( s : String );
}
enum abstract ErrorMessage(Expr.UInt8) from Expr.UInt8 to Expr.UInt8 {
final INVALID_CHAR_CODE_MULTI;
final FROM_CHAR_CODE_NON_INT;
final EMPTY_INTERPOLATION;
final UNKNOWN_MAP_TYPE;
final UNKNOWN_MAP_TYPE_RUNTIME;
final EXPECT_KEY_VALUE_SYNTAX;
public function toString():String {
return switch(cast this) {
case INVALID_CHAR_CODE_MULTI: "'char'.code only works on single characters";
case FROM_CHAR_CODE_NON_INT: "String.fromCharCode only works on integers";
case EMPTY_INTERPOLATION: "Invalid interpolation: Expression cannot be empty";
case UNKNOWN_MAP_TYPE: "Unknown Map Type";
case UNKNOWN_MAP_TYPE_RUNTIME: "Unknown Map Type, while parsing at runtime";
case EXPECT_KEY_VALUE_SYNTAX: "Expected a => b";
}
}
}
+141 -293
View File
@@ -21,13 +21,13 @@
*/ */
package hscript; package hscript;
typedef Int8 = #if cpp cpp.Int8 #else Int #end; typedef Int8 = #if cpp cpp.Int8 #elseif java java.Int8 #elseif cs cs.Int8 #else Int #end;
typedef Int16 = #if cpp cpp.Int16 #else Int #end; typedef Int16 = #if cpp cpp.Int16 #elseif java java.Int16 #elseif cs cs.Int16 #else Int #end;
typedef Int32 = #if cpp cpp.Int32 #else Int #end; typedef Int32 = #if cpp cpp.Int32 #else Int #end;
typedef Int64 = #if cpp cpp.Int64 #else Int #end; typedef Int64 = #if cpp cpp.Int64 #elseif java java.Int64 #elseif cs cs.Int64 #else Int #end;
typedef UInt8 = #if cpp cpp.UInt8 #else Int #end; typedef UInt8 = #if cpp cpp.UInt8 #elseif cs cs.UInt8 #else Int #end;
typedef UInt16 = #if cpp cpp.UInt16 #else Int #end; typedef UInt16 = #if cpp cpp.UInt16 #elseif cs cs.UInt16 #else Int #end;
typedef UInt32 = #if cpp cpp.UInt32 #else Int #end; typedef UInt32 = #if cpp cpp.UInt32 #else Int #end;
typedef UInt64 = #if cpp cpp.UInt64 #else Int #end; typedef UInt64 = #if cpp cpp.UInt64 #else Int #end;
@@ -35,29 +35,102 @@ enum Const {
CInt( v : Int ); CInt( v : Int );
CFloat( f : Float ); CFloat( f : Float );
CString( s : String ); CString( s : String );
#if !haxe3
CInt32( v : haxe.Int32 );
#end
} }
enum abstract MapType(UInt8) {
var Null;
var IntMap;
var StringMap;
var EnumMap;
var ObjectMap;
var UnknownMap;
}
typedef Error = hscript.Error.Error_;
#if hscriptPos #if hscriptPos
class Expr { @:structInit
final class Expr {
public var e : ExprDef; public var e : ExprDef;
public var pmin : Int; public var pmin : Int;
public var pmax : Int; public var pmax : Int;
public var origin : String; public var origin : String;
public var line : Int; public var line : Int;
}
enum ExprDef {
#else
typedef ExprDef = Expr;
enum Expr {
#end
EIgnore(skip:Bool);
EConst( c : Const );
EIdent( v : String );
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess );
EParent( e : Expr );
EBlock( e : Array<Expr> );
EField( e : Expr, f : String , ?safe : Bool );
EBinop( op : String, e1 : Expr, e2 : Expr );
EUnop( op : String, prefix : Bool, e : Expr );
ECall( e : Expr, params : Array<Expr> );
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
EWhile( cond : Expr, e : Expr );
EFor( v : String, it : Expr, e : Expr, ?ithv: String);
EBreak;
EContinue;
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool );
EReturn( ?e : Expr );
EArray( e : Expr, index : Expr );
EArrayDecl( e : Array<Expr>, ?wantedType: CType );
ENew( cl : String, params : Array<Expr> );
EThrow( e : Expr );
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
EObject( fl : Array<ObjectField> );
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
ESwitch( e : Expr, cases : Array<SwitchCase>, ?defaultExpr : Expr );
EDoWhile( cond : Expr, e : Expr);
EMeta( name : String, args : Array<Expr>, e : Expr );
ECheckType( e : Expr, t : CType );
EImport( c : String, ?asname:String );
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
ERedirect( name:String, className:String, ?cl:Class<Dynamic> );
EUsing(name:String);
EEnum(name:String, fields:Array<EnumType>);
}
@:structInit
final class ObjectField {
public var name : String;
public var e : Expr;
}
@:structInit
final class SwitchCase {
public var values : Array<Expr>;
public var expr : Expr;
}
@:structInit
final class Argument {
public var name : String;
public var t : CType;
public var opt : Bool;
public var value : Expr;
}
@:structInit
final class MetadataEntry {
public var name : String;
public var params : Array<Expr>;
}
typedef Metadata = Array<MetadataEntry>;
enum CType {
CTPath( path : Array<String>, ?params : Array<CType> );
CTFun( args : Array<CType>, ret : CType );
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
CTParent( t : CType );
CTOpt( t : CType );
CTNamed( n : String, t : CType );
}
#if hscriptPos
class Error {
public var e : ErrorDef;
public var pmin : Int;
public var pmax : Int;
public var origin : String;
public var line : Int;
public function new(e, pmin, pmax, origin, line) { public function new(e, pmin, pmax, origin, line) {
this.e = e; this.e = e;
this.pmin = pmin; this.pmin = pmin;
@@ -65,277 +138,35 @@ class Expr {
this.origin = origin; this.origin = origin;
this.line = line; this.line = line;
} }
public function toString(): String {
return Printer.errorToString(this);
}
} }
enum ExprDef enum ErrorDef {
#else #else
typedef ExprDef = Expr; enum Error {
enum Expr
#end #end
{ EInvalidChar( c : Int );
EConst( c : Const ); EUnexpected( s : String );
EIdent( v : VarN ); EUnterminatedString;
EVar( n : VarN, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool ); EUnterminatedComment;
EParent( e : Expr, ?noOptimize : Bool ); EInvalidPreprocessor( msg : String );
EBlock( e : Array<Expr> ); EUnknownVariable( v : String );
EField( e : Expr, f : String , ?safe : Bool ); EInvalidIterator( v : String );
EBinop( op : Binop, e1 : Expr, e2 : Expr ); EInvalidOp( op : String );
EUnop( op : Unop, prefix : Bool, e : Expr ); EInvalidAccess( f : String );
ECall( e : Expr, params : Array<Expr> ); ECustom( msg : String );
EIf( cond : Expr, e1 : Expr, ?e2 : Expr ); EInvalidClass( className : String);
EWhile( cond : Expr, e : Expr ); EAlreadyExistingClass( className : String);
EFor( v : VarN, it : Expr, e : Expr);
EForKeyValue( v : VarN, it : Expr, e : Expr, ithv: VarN);
EBreak;
EContinue;
EFunction( args : Array<Argument>, e : Expr, ?name : VarN, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool );
EReturn( ?e : Expr );
EArray( e : Expr, index : Expr );
EMapDecl( type: MapType, keys: Array<Expr>, values: Array<Expr> );
EArrayDecl( e : Array<Expr> );
ENew( cl : VarN, params : Array<Expr> );
EThrow( e : Expr );
ETry( e : Expr, v : VarN, t : Null<CType>, ecatch : Expr );
EObject( fl : Array<ObjectField> );
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
ESwitch( e : Expr, cases : Array<SwitchCase>, ?defaultExpr : Expr );
EDoWhile( cond : Expr, e : Expr);
EMeta( name : String, args : Array<Expr>, e : Expr );
ECheckType( e : Expr, t : CType );
EImport( c : String, mode: KImportMode );
EClass( name:VarN, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
#if !HSCRIPT_NO_INT_VARS
EInfo( info: InfoClass, e: Expr );
#end
} }
enum Binop {
/**
`+`
**/
OpAdd;
/**
`*`
**/
OpMult;
/**
`/`
**/
OpDiv;
/**
`-`
**/
OpSub;
/**
`=`
**/
OpAssign;
/**
`==`
**/
OpEq;
/**
`!=`
**/
OpNotEq;
/**
`>`
**/
OpGt;
/**
`>=`
**/
OpGte;
/**
`<`
**/
OpLt;
/**
`<=`
**/
OpLte;
/**
`&`
**/
OpAnd;
/**
`|`
**/
OpOr;
/**
`^`
**/
OpXor;
/**
`&&`
**/
OpBoolAnd;
/**
`||`
**/
OpBoolOr;
/**
`<<`
**/
OpShl;
/**
`>>`
**/
OpShr;
/**
`>>>`
**/
OpUShr;
/**
`%`
**/
OpMod;
/**
`+=` `-=` `/=` `*=` `<<=` `>>=` `>>>=` `|=` `&=` `^=` `%=`
**/
OpAssignOp(op:Binop);
/**
`...`
**/
OpInterval;
/**
`=>`
**/
OpArrow;
/**
`is`
**/
OpIs; // used to be OpIn, but our system treats that differently
/**
`??`
**/
OpNullCoal;
}
enum abstract Unop(UInt8) {
/**
`++`
**/
var OpIncrement;
/**
`--`
**/
var OpDecrement;
/**
`!`
**/
var OpNot;
/**
`-`
**/
var OpNeg;
/**
`~`
**/
var OpNegBits;
/**
`...`
**/
var OpSpread;
}
#if !HSCRIPT_NO_INT_VARS
class InfoClass {
public var variables:Array<String>;
public function new(variables:Array<String>) {
this.variables = variables;
}
}
#end
enum KImportMode {
INormal;
IAs( name : String );
IAsReturn;
IAll;
}
class ObjectField {
public var name:String;
public var e:Expr;
public function new(name, e) {
this.name = name;
this.e = e;
}
}
@:structInit
final class SwitchCase {
public var values : Array<Expr>;
public var expr : Expr;
}
//typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
class Argument {
public var name : VarN;
public var t : Null<CType>;
public var opt : Bool;
public var value : Null<Expr>;
public function new(name, ?t, ?opt, ?value) {
this.name = name;
this.t = t;
this.opt = opt;
this.value = value;
}
public function toString() {
return (opt ? "?" : "") + name + (t != null ? ":" + Printer.convertTypeToString(t) : "") + (value != null ? "=" + Printer.convertExprToString(value) : "");
}
}
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
enum CType {
CTPath( path : Array<String>, ?params : Array<CType> );
CTFun( args : Array<CType>, ret : CType );
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
CTParent( t : CType );
CTOpt( t : CType );
CTNamed( n : String, t : CType );
}
enum ModuleDecl { enum ModuleDecl {
DPackage( path : Array<String> ); DPackage( path : Array<String> );
DImport( path : Array<String>, ?everything : Bool ); DImport( path : Array<String>, ?everything : Bool, ?asname:String );
DClass( c : ClassDecl ); DUsing( path : Array<String>);
DTypedef( c : TypeDecl ); DTypedef( c : TypeDecl );
DClass( c : ClassDecl );
} }
typedef ModuleType = { typedef ModuleType = {
@@ -377,21 +208,38 @@ enum FieldKind {
KVar( v : VarDecl ); KVar( v : VarDecl );
} }
typedef FunctionDecl = { @:structInit
var args : Array<Argument>; final class FunctionDecl {
var expr : Expr; public var args : Array<Argument>;
var ret : Null<CType>; public var body : Expr;
public var ret : Null<CType>;
} }
typedef VarDecl = { typedef VarDecl = {
var get : Null<String>; var get : FieldPropertyAccess;
var set : Null<String>; var set : FieldPropertyAccess;
var isFinal : Bool;
var expr : Null<Expr>; var expr : Null<Expr>;
var type : Null<CType>; var type : Null<CType>;
} }
#if !HSCRIPT_NO_INT_VARS typedef TypeParamDecl = {};
abstract VarN(Dynamic) from Int from String to Int to String {}
#else enum EnumType {
typedef VarN = String; ESimple(name: String);
#end EConstructor(name: String, args: Array<Argument>);
}
enum abstract FieldPropertyAccess(UInt8) {
var ADefault;
var ANull;
var AGet;
var ASet;
var ADynamic;
var ANever;
}
enum AccessContext {
CInner(cl:String);
COuter;
}
+12
View File
@@ -0,0 +1,12 @@
package hscript;
interface IHScriptCustomAccessBehaviour {
var __allowSetGet:Bool;
var __allowPrivateAccess:Bool;
public function hset(name:String, val:Dynamic):Dynamic;
public function hget(name:String):Dynamic;
public function __callGetter(name:String):Dynamic;
public function __callSetter(name:String, val:Dynamic):Dynamic;
}
+2 -12
View File
@@ -1,16 +1,6 @@
package hscript; package hscript;
import hscript.Interp;
interface IHScriptCustomClassBehaviour { interface IHScriptCustomClassBehaviour {
public var __interp:Interp; // TODO: Remove public var __customClass:hscript.customclass.CustomClass;
public var __custom__variables:Map<String, Dynamic>; public var __real_fields:Array<String>;
public var __allowSetGet:Bool;
public function hset(name:String, val:Dynamic):Dynamic;
public function hget(name:String):Dynamic;
public function __callGetter(name:String):Dynamic;
public function __callSetter(name:String, val:Dynamic):Dynamic;
} }
+1291 -843
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
package hscript;
class InterpConfig {
// Automatic import redirect for certain classes
public static final IMPORT_REDIRECTS = [
"Type" => "hscript.proxy.ProxyType"
];
// Incase an import fails
// These are the module names
@:unreflective
public static final DISALLOW_IMPORT = [
"Type"
];
}
+6 -56
View File
@@ -29,23 +29,13 @@ import haxe.macro.Expr;
class Macro { class Macro {
var p : Position; var p : Position;
#if haxe3
var binops : Map<String,Binop>; var binops : Map<String,Binop>;
var unops : Map<String,Unop>; var unops : Map<String,Unop>;
#else
var binops : Hash<Binop>;
var unops : Hash<Unop>;
#end
public function new(pos) { public function new(pos) {
p = pos; p = pos;
#if haxe3
binops = new Map(); binops = new Map();
unops = new Map(); unops = new Map();
#else
binops = new Hash();
unops = new Hash();
#end
for( c in Type.getEnumConstructs(Binop) ) { for( c in Type.getEnumConstructs(Binop) ) {
if( c == "OpAssignOp" ) continue; if( c == "OpAssignOp" ) continue;
var op = Type.createEnum(Binop, c); var op = Type.createEnum(Binop, c);
@@ -73,9 +63,7 @@ class Macro {
case OpMod: assign = true; "%"; case OpMod: assign = true; "%";
case OpAssignOp(_): ""; case OpAssignOp(_): "";
case OpInterval: "..."; case OpInterval: "...";
#if haxe3
case OpArrow: "=>"; case OpArrow: "=>";
#end
#if (haxe_ver >= 4) #if (haxe_ver >= 4)
case OpIn: "in"; case OpIn: "in";
#end #end
@@ -102,15 +90,8 @@ class Macro {
} }
} }
#if !haxe3
function isType(v:String) {
var c0 = v.charCodeAt(0);
return c0 >= 'A'.code && c0 <= 'Z'.code;
}
#end
function map < T, R > ( a : Array<T>, f : T -> R ) : Array<R> { function map < T, R > ( a : Array<T>, f : T -> R ) : Array<R> {
var b = new Array(); var b = [];
for( x in a ) for( x in a )
b.push(f(x)); b.push(f(x));
return b; return b;
@@ -156,17 +137,9 @@ class Macro {
case CInt(v): CInt(Std.string(v)); case CInt(v): CInt(Std.string(v));
case CFloat(f): CFloat(Std.string(f)); case CFloat(f): CFloat(Std.string(f));
case CString(s): CString(s); case CString(s): CString(s);
#if !haxe3
case CInt32(v): CInt(Std.string(v));
#end
}); });
case EIdent(v): case EIdent(v):
#if !haxe3 EConst(CIdent(v));
if( isType(v) )
EConst(CType(v));
else
#end
EConst(CIdent(v));
case EVar(n, t, e): case EVar(n, t, e):
EVars([ { name : n, expr : if( e == null ) null else convert(e), type : if( t == null ) null else convertType(t) } ]); EVars([ { name : n, expr : if( e == null ) null else convert(e), type : if( t == null ) null else convertType(t) } ]);
case EParent(e): case EParent(e):
@@ -174,12 +147,7 @@ class Macro {
case EBlock(el): case EBlock(el):
EBlock(map(el,convert)); EBlock(map(el,convert));
case EField(e, f): case EField(e, f):
#if !haxe3 EField(convert(e), f);
if( isType(f) )
EType(convert(e), f);
else
#end
EField(convert(e), f);
case EBinop(op, e1, e2): case EBinop(op, e1, e2):
var b = binops.get(op); var b = binops.get(op);
if( b == null ) throw EInvalidOp(op); if( b == null ) throw EInvalidOp(op);
@@ -198,23 +166,11 @@ class Macro {
EWhile(convert(c), convert(e), false); EWhile(convert(c), convert(e), false);
case EFor(v, it, efor): case EFor(v, it, efor):
#if (haxe_ver >= 4) #if (haxe_ver >= 4)
var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end; var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor)); EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
#elseif (haxe_211 || haxe3) #else
var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end; var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor)); EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
#else
EFor(v, convert(it), convert(efor));
#end
case EForKeyValue(v, it, efor, ithv):
#if (haxe_ver >= 4)
var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor), ithv);
#elseif (haxe_211 || haxe3)
var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor), ithv);
#else
EFor(v, convert(it), convert(efor), ithv);
#end #end
case EBreak: case EBreak:
EBreak; EBreak;
@@ -239,12 +195,6 @@ class Macro {
EReturn(e == null ? null : convert(e)); EReturn(e == null ? null : convert(e));
case EArray(e, index): case EArray(e, index):
EArray(convert(e), convert(index)); EArray(convert(e), convert(index));
case EMapDecl(type, keys, values): // port to array decl, haxe still uses EArrayDecl but with => as a binop
var el = [];
for( i in 0...keys.length ) {
el.push( hscript.Expr.EBinop("=>", keys[i], values[i]) );
}
EArrayDecl(map(el,convert));
case EArrayDecl(el): case EArrayDecl(el):
EArrayDecl(map(el,convert)); EArrayDecl(map(el,convert));
case ENew(cl, params): case ENew(cl, params):
-643
View File
@@ -1,643 +0,0 @@
package hscript;
import hscript.Expr;
import hscript.Tools;
import hscript.Parser;
@:access(hscript.Parser)
class Optimizer {
static inline function expr(e:Expr) return Tools.expr(e);
public static function optimize(s:Expr):Expr {
if(s == null)
return null;
var e = Tools.expr(s);
// TODO: Convert EBinOp(+, a, EUnop(-, b)) to EBinOp(-, a, b) aka (a - b)
// TODO: Make it so if EFor & EForKeyValue iterator variable isnt used its replaced with a $, which the code handles as doesnt exist
// TODO: Make it so <PURE_VAR|CONST> == ?; gets removed, IF it isnt stored
// TODO: Optimize ?. and ??
// TODO: Optimize Std.string(CONSTANT) to "CONSTANT"
// TODO: Remove Std.string if its the right side of a binop of a string
// TODO: Add EForComprehension, EWhileComprehension, EDoWhileComprehension for better optimization
// TODO: Optimize EFor(v, it, EBlock([e1])) to EFor(v, it, e1)
// TODO: Optimize EIf(cond, e1, e2) to EBlock([cond, e1]) if e1 == e2, if cond is constant or side effect free then convert to EParent(e1)
switch(e) {
// Parse all expressions and recreate the AST
case EBlock(exprs):
var exprs = exprs.map((v) -> optimize(v)).filter((v) -> v != null).filter((v) -> {
if(Tools.expr(v).match(EBlock([]))) return false; // Remove empty blocks
return true;
});
var newExprs = [];
for(e in exprs) {
switch(Tools.expr(e)) {
case EBlock(iex):
var declares = Lambda.exists(iex, ie -> hasDecl(ie));
if(!declares) {
for(ex in iex)
newExprs.push(ex);
} else {
newExprs.push(e);
}
case EReturn(_): // remove stuff that are after a return
newExprs.push(e);
break;
default:
newExprs.push(e);
}
}
return mk(EBlock(newExprs), s);
case EIf(econd, e1, e2):
var econd = optimize(econd);
var e1 = optimize(e1);
var e2 = optimize(e2);
if(isBool(econd)) {
var econd = getBool(econd);
if(econd == true) {
if(e1 == null)
return mk(EBlock([]), s);
return mk(EBlock([e1]), s);
} else {
if(e2 == null)
return mk(EBlock([]), s);
return mk(EBlock([e2]), s);
}
}
if(e2 != null && isBool(e1) && isBool(e2)) {
var c1 = getBool(e1);
var c2 = getBool(e2);
if(c1 == false && c2 == true) { // (VAR ? false : true)
return optimize(mk(EUnop(OpNot, true, econd), s));
}
if(c1 == true && c2 == false) { // (VAR ? true : false)
return optimize(mk(Tools.expr(econd), s));
}
if(isConstant(econd)) { // Side effect free
if(c1 == true && c2 == true) { // (CONST ? true : true)
return mk(convertConstant(true), s);
}
if(c1 == false && c2 == false) { // (CONST ? false : false)
return mk(convertConstant(false), s);
}
// TODO: Check if local variables are used in the condition
// Since they cant have side effects, they can be optimized
}
}
return mk(EIf(econd, e1, e2), s);
case ETernary(econd, e1, e2):
var econd = optimize(econd);
var e1 = optimize(e1);
var e2 = optimize(e2);
if(isConstant(econd)) {
var econd = getConstant(econd);
if(econd == true) {
if(e1 == null)
return mk(EBlock([]), s);
return mk(EParent(e1), s);
} else {
if(e2 == null)
return mk(EBlock([]), s);
return mk(EParent(e2), s);
}
}
if(isBool(e1) && isBool(e2)) {
var c1 = getBool(e1);
var c2 = getBool(e2);
if(c1 == false && c2 == true) { // (VAR ? false : true)
return optimize(mk(EUnop(OpNot, true, econd), s));
}
if(c1 == true && c2 == false) { // (VAR ? true : false)
return optimize(mk(Tools.expr(econd), s));
}
if(isConstant(econd)) { // Side effect free
if(c1 == true && c2 == true) { // (CONST ? true : true)
return mk(convertConstant(true), s);
}
if(c1 == false && c2 == false) { // (CONST ? false : false)
return mk(convertConstant(false), s);
}
// TODO: Check if local variables are used in the condition
// Since they cant have side effects, they can be optimized
}
}
return mk(ETernary(econd, e1, e2), s);
case EWhile(econd, e):
var econd = optimize(econd);
var e = optimize(e);
return mk(EWhile(econd, e), s);
case EDoWhile(econd, e):
var econd = optimize(econd);
var e = optimize(e);
return mk(EDoWhile(econd, e), s);
case EFor(v, it, e):
var it = optimize(it);
var e = optimize(e);
return mk(EFor(v, it, e), s);
case EForKeyValue(v, it, e, ithv):
var it = optimize(it);
var e = optimize(e);
return mk(EForKeyValue(v, it, e, ithv), s);
case EBreak:
return mk(EBreak, s);
case EContinue:
return mk(EContinue, s);
case EReturn(e):
var e = optimize(e);
return mk(EReturn(e), s);
case ETry(e, v, t, ecatch):
var e = optimize(e);
var ecatch = optimize(ecatch);
return mk(ETry(e, v, t, ecatch), s);
case EThrow(e):
var e = optimize(e);
return mk(EThrow(e), s);
case EVar(n, t, e, isPublic, isStatic):
e = optimize(e);
return mk(EVar(n, t, e, isPublic, isStatic), s);
case ECall(e, params):
e = optimize(e);
params = params.map((v) -> optimize(v));
function p(i:Int, t:ConstType, opt:Bool = false):Dynamic {
var p = params[i];
if(t != getConstType(p))
if(opt)
return null;
else
throw Parser.getBaseError(EInvalidType(getTypeName(p)));
return switch(t) {
case CTInt: getInt(p);
case CTFloat: getFloat(p);
case CTBool: getBool(p);
case CTString: getStringConstant(p);
case CTNull: null;
}
}
switch(Tools.expr(e)) {
case EField(expr(_) => EConst(CString(str)), field):
switch(field) {
case "toString":
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toString() takes no arguments"));
return mk(convertConstant(str.toString()), s);
case "toUpperCase":
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toUpperCase() takes no arguments"));
return mk(convertConstant(str.toUpperCase()), s);
case "toLowerCase":
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toLowerCase() takes no arguments"));
return mk(convertConstant(str.toLowerCase()), s);
case "charAt":
var index = p(0, CTInt);
return mk(convertConstant(str.charAt(index)), s);
case "charCodeAt":
var index = p(0, CTInt);
return mk(convertConstant(str.charCodeAt(index)), s);
case "indexOf":
var value = p(0, CTString);
var startIndex = p(1, CTInt, true);
return mk(convertConstant(str.indexOf(value, startIndex)), s);
case "lastIndexOf":
var value = p(0, CTString);
var startIndex = p(1, CTInt, true);
return mk(convertConstant(str.lastIndexOf(value, startIndex)), s);
case "split":
var delimiter = p(0, CTString);
var strArr = str == "" ? [] : str.split(delimiter); // fix platform dependent behavior
return mk(EArrayDecl([for(st in strArr) mk(EConst(CString(st)), s)]), s);
case "substr":
var pos = p(0, CTInt);
var len = p(1, CTInt, true);
return mk(convertConstant(str.substr(pos, len)), s);
case "substring":
var startIndex = p(0, CTInt);
var endIndex = p(1, CTInt, true);
return mk(convertConstant(str.substring(startIndex, endIndex)), s);
}
default:
}
switch(Tools.expr(e)) {
default:
}
return mk(ECall(e, params), s);
case EField(e, f, safe):
e = optimize(e);
switch(Tools.expr(e)) {
case EConst(CString(str)) if(f == "length"):
return mk(convertConstant(str.length), s);
default:
}
return mk(EField(e, f, safe), s);
case EIdent(_) | EConst(_):
return s;
case EParent(e, noOptimize):
e = optimize(e);
return mk(noOptimize ? EParent(e) : Tools.expr(e), s);
case ECheckType(e, t):
e = optimize(e);
return mk(ECheckType(e, t), s);
case EMeta(name, args, e):
e = optimize(e);
return mk(EMeta(name, args, e), s);
case ENew(cl, args):
args = args.map((v) -> optimize(v));
return mk(ENew(cl, args), s);
case EObject(fl):
fl.map((v) -> {
v.e = optimize(v.e);
});
return mk(EObject(fl), s);
case EImport(c, n):
return mk(EImport(c, n), s);
case EClass(name, fields, extend, interfaces): // Possible code not working
fields = fields.map((v) -> optimize(v));
return mk(EClass(name, fields, extend, interfaces), s);
case EFunction(args, e, name, ret, isPublic, isStatic, isOverride):
args.map((v) -> {
v.name = v.name;
v.value = optimize(v.value);
});
e = optimize(e);
return mk(EFunction(args, e, name, ret, isPublic, isStatic, isOverride), s);
case ESwitch(e, cases, def):
e = optimize(e);
cases.map((v) -> {
var values = v.values.map((v) -> optimize(v));
v.expr = optimize(v.expr);
});
def = optimize(def);
if(isConstant(e)) {
var econd = getConstant(e);
for(c in cases) {
for(v in c.values) {
if(isConstant(v)) {
var value = getConstant(v);
if(value == econd)
return mk(Tools.expr(c.expr), s);
}
}
}
// Maybe convert this to a Lambda.foreach?
var isAllCasesConstant = true;
for(c in cases) {
for(v in c.values) {
if(!isConstant(v)) {
isAllCasesConstant = false;
break;
}
}
}
if(isAllCasesConstant) {
#if debug
//trace("Didnt find any cases that match");
#end
if(def != null)
return mk(Tools.expr(def), s);
else
return mk(EBlock([]), s);
}
}
return mk(ESwitch(e, cases, def), s);
case EMapDecl(type, keys, values):
keys = keys.map((v) -> optimize(v));
values = values.map((v) -> optimize(v));
return mk(EMapDecl(type, keys, values), s);
case EArrayDecl(arr):
arr = arr.map((v) -> optimize(v));
return mk(EArrayDecl(arr), s);
case EArray(e, index):
e = optimize(e);
index = optimize(index);
if(isConstant(index)) {
if(Tools.expr(e).match(EArrayDecl(_))) {
var arr = switch(Tools.expr(e)) {
case EArrayDecl(arr): arr;
default: null;
};
var constant = Lambda.exists(arr, isConstant);
var index = getInt(index);
if(constant && index != null) {
return mk(Tools.expr(arr[index]), s);
}
}
if(Tools.expr(e).match(EMapDecl(_))) {
var map = switch(Tools.expr(e)) {
case EMapDecl(type, keys, vals): [for(i in 0...keys.length) [keys[i], vals[i]]];
default: null;
};
var constant = Lambda.exists(map, (v) -> isConstant(v[0]) && isConstant(v[1]));
if(constant) {
var idx = Lambda.findIndex(map, (v) -> Type.enumEq(Tools.expr(v[0]), Tools.expr(index)));
if(idx == -1)
return mk(EIdent("null"), s);
return mk(Tools.expr(map[idx][1]), s);
}
}
}
return mk(EArray(e, index), s);
case EBinop(op, e1, e2):
e1 = optimize(e1);
e2 = optimize(e2);
if(isConstant(e1) && isConstant(e2)) {
var optimized:Dynamic = optimizeOp(op, getConstant(e1), getConstant(e2));
if(optimized != null)
return mk(convertConstant(optimized), s);
}
// Possible bugs here
if(isNumber(e1) && !isConstant(e2)) {
var c1 = getNumber(e1);
if(compareNumber(c1, 0) && op == OpAdd)
return mk(Tools.expr(e2), s);
if(compareNumber(c1, 1) && op == OpMult)
return mk(Tools.expr(e2), s);
//if(compareNumber(c1, 0) && op == "*")
// return mk(convertConstant(0), s);
}
if(!isConstant(e1) && isNumber(e2)) {
var c2 = getNumber(e2);
if(op == OpAdd && compareNumber(c2, 0))
return mk(Tools.expr(e1), s);
if(op == OpDiv && compareNumber(c2, 1))
return mk(Tools.expr(e1), s);
if(op == OpMult && compareNumber(c2, 1))
return mk(Tools.expr(e1), s);
//if(op == "*" && compareNumber(c2, 0))
// return mk(convertConstant(0), s);
}
return mk(EBinop(op, e1, e2), s);
case EUnop(op, prefix, e):
e = optimize(e);
if(isConstant(e) && prefix) {
var constant:Dynamic = getConstant(e);
switch(op) {
case OpNeg: return mk(convertConstant(-constant), s);
case OpNot: return mk(convertConstant(!constant), s);
case OpNegBits:
var complement = #if (neko && !haxe3) haxe.Int32.complement(constant) #else ~constant #end;
return mk(convertConstant(complement), s);
default:
}
}
return mk(EUnop(op, prefix, e), s);
default:
Sys.println("Unknown expr: " + e);
}
return s;
}
static function compareNumber(a:Dynamic, b:Dynamic):Bool {
return switch(Type.typeof(a)) {
case TInt:
var a:Int = cast a;
switch(Type.typeof(b)) {
case TInt: a == cast(b, Int);
case TFloat: a == cast(b, Float);
default: false;
}
case TFloat:
var a:Float = cast a;
switch(Type.typeof(b)) {
case TInt: a == cast(b, Int);
case TFloat: a == cast(b, Float);
default: false;
}
default: false;
}
}
static function hasDecl(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EVar(_): true;// has a declaration
case EFunction(_): true;// has a declaration
default: false;
}
}
static function getTypeName(e:Expr):String {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CInt(_)): "Int";
case EConst(CFloat(_)): "Float";
case EConst(CString(_)): "String";
case EIdent("true") | EIdent("false"): "Bool";
case EIdent("null"): "Null";
case EIdent(_): "Dynamic";
case EParent(e): getTypeName(e);
default: Std.string(Tools.expr(e));
}
}
static function getConstType(e:Expr):ConstType {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CInt(_)): CTInt;
case EConst(CFloat(_)): CTFloat;
case EConst(CString(_)): CTString;
case EIdent("true") | EIdent("false"): CTBool;
case EIdent("null"): CTNull;
case EParent(e): getConstType(e);
default: throw "Unknown type " + Tools.expr(e);
}
}
static function isConstant(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EIdent("true") | EIdent("false") | EIdent("null"): true;
case EConst(_): true;
case EParent(e): isConstant(e);
default: false;
}
}
static function isBool(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EIdent("true") | EIdent("false"): true;
case EParent(e): isBool(e);
default: false;
}
}
static function isNumber(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EConst(CInt(_)): true;
case EConst(CFloat(_)): true;
case EParent(e): isNumber(e);
default: false;
}
}
static function getNumber(e:Expr):Dynamic {
return switch(Tools.expr(e)) {
case EConst(CInt(value)): value;
case EConst(CFloat(value)): value;
case EParent(e): getNumber(e);
default: throw "Unknown type " + Tools.expr(e);
}
}
static function getBool(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EIdent("true"): true;
case EIdent("false"): false;
case EParent(e): getBool(e);
default: throw "Unknown type " + Tools.expr(e);
}
}
static function getInt(e:Expr):Null<Int> {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CInt(value)): value;
case EParent(e): getInt(e);
default: null;
}
}
static function getFloat(e:Expr):Null<Float> {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CFloat(value)): value;
case EParent(e): getFloat(e);
default: null;
}
}
static function isString(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EConst(CString(_)): true;
case EParent(e): isString(e);
default: false;
}
}
static function getStringConstant(e:Expr):String {
return switch(Tools.expr(e)) {
case EConst(CString(value)): value;
case EParent(e): getStringConstant(e);
default: null;
}
}
static function getConstant(e:Expr):Dynamic {
return switch(Tools.expr(e)) {
case EParent(e): getConstant(e);
case EIdent("true"): true;
case EIdent("false"): false;
case EIdent("null"): null;
case EConst(CInt(value)): value;
case EConst(CFloat(value)): value;
case EConst(CString(value)): value;
default: throw "Unknown constant " + Tools.expr(e);
}
}
static function convertConstant(value:Dynamic):ExprDef {
return switch(Type.typeof(value)) {
case TInt: EConst(CInt(value));
case TFloat: EConst(CFloat(value));
case TBool: EIdent(value == true ? "true" : "false");
//case TString: EConst(CString(value));
case TNull: EIdent("null");
case TClass(String): EConst(CString(value));
default: throw "Unknown type " + Type.typeof(value);
}
}
static function mk(e:ExprDef, s:Expr):Expr {
#if hscriptPos
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
#else
return e;
#end
}
static function optimizeOp(op:Binop, f1:Dynamic, f2:Dynamic):Dynamic {
//trace("Optimizing " + f1 + " " + op + " " + f2);
return switch(op) {
case OpAdd: f1 + f2;
case OpSub: f1 - f2;
case OpMult: f1 * f2;
case OpDiv: f1 / f2;
case OpMod: f1 % f2;
case OpAnd: f1 & f2;
case OpOr: f1 | f2;
case OpXor: f1 ^ f2;
case OpShl: f1 << f2;
case OpShr: f1 >> f2;
case OpUShr: f1 >>> f2;
case OpEq: f1 == f2;
case OpNotEq: f1 != f2;
case OpGte: f1 >= f2;
case OpLte: f1 <= f2;
case OpGt: f1 > f2;
case OpLt: f1 < f2;
case OpBoolOr: f1 == true || f2 == true;
case OpBoolAnd: f1 == true && f2 == true;
case OpNullCoal: f1 == null ? f2 : f1;
default: null;
}
}
}
enum ConstType {
CTInt;
CTFloat;
CTBool;
CTString;
CTNull;
}
+685 -905
View File
File diff suppressed because it is too large Load Diff
-107
View File
@@ -1,107 +0,0 @@
package hscript;
import hscript.Expr;
import hscript.Error;
import hscript.Tools;
import hscript.Parser;
@:access(hscript.Parser)
class Postprocessor {
static inline function expr(e:Expr) return Tools.expr(e);
static function mk(e:ExprDef, s:Expr):Expr {
#if hscriptPos
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
#else
return e;
#end
}
#if !HSCRIPT_NO_INT_VARS
public static function processvars(e:Expr, ?vars:Array<String>) {
if(vars == null) vars = [];
for(v in getvars(e)) vars.push(v);
e = _processvars(e, vars);
return mk(EInfo(new InfoClass(vars), e), e);
}
private static function _processvars(e:Expr, vars:Array<String>):Expr {
if(e == null) return null;
var ge = e;
var doExtra = true;
var e = switch (expr(e)) {
case EIdent(v) if (v is String):
mk(EIdent(vars.indexOf(v)), e);
case EVar(n, t, _e, p, s) if (n is String):
mk(EVar(vars.indexOf(n), t, _e, p, s), e);
case EImport(c, IAs(n)) if (n is String):
mk(EVar(vars.indexOf(n), null, mk(EImport(c, IAsReturn), e), false, false), e);
case EFunction(args, _e, n, r, p, s, o) if (n is String):
var _args:Array<Argument> = [
for (arg in args)
new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value)
];
mk(EFunction(_args, _e, vars.indexOf(n), r, p, s, o), e);
case EFunction(args, _e, null, r, p, s, o):
var _args:Array<Argument> = [
for (arg in args)
new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value)
];
mk(EFunction(_args, _e, null, r, p, s, o), e);
case ENew(cl, p) if (cl is String):
var p = [for(arg in p) arg];
mk(ENew(vars.indexOf(cl), p), e);
case EClass(n, fls, extnd, i) if (n is String):
var fls:Array<Expr> = [for(f in fls) f];
mk(EClass(vars.indexOf(n), fls, extnd, i), e);
case ETry(_e, v, t, ec) if (v is String):
mk(ETry(_e, vars.indexOf(v), t, ec), e);
case EFor(v, it, _e) if (v is String):
mk(EFor(vars.indexOf(v), it, _e), e);
case EForKeyValue(v, it, _e, ithv) if (v is String && ithv is String):
mk(EForKeyValue(vars.indexOf(v), it, _e, vars.indexOf(ithv)), e);
default: {
doExtra = false;
e;
}
};
e = Tools.map(e, function(e) {
return _processvars(e, vars);
});
return e;
}
public static function getvars(se:Expr, ?vars:Array<String>):Array<String> {
inline function pushVar(v:VarN) {
if(v is String) {
var v:String = cast v;
if (!vars.contains(v))
vars.push(v);
}
};
if(vars == null) vars = [];
Tools.iterExprRecursive(se, (e) -> {
switch (expr(e)) {
case EVar(n, t, e, _,_): pushVar(n);
case EIdent(v): pushVar(v);
case EFunction(args,e,name,_,_,_,_):
pushVar(name);
for (a in args) {
pushVar(a.name);
}
case EFor(v,it,e): pushVar(v);
case EForKeyValue(v,it,e,ithv): pushVar(ithv); pushVar(v);
case ETry(e, v, _, ec): pushVar(v);
case ENew(cl, params): pushVar(cl);
case EClass(name, fls,_,_): pushVar(name);
default:
}
});
return vars;
}
#end
}
-147
View File
@@ -1,147 +0,0 @@
package hscript;
import hscript.Expr;
import hscript.Error;
import hscript.Tools;
import hscript.Parser;
@:access(hscript.Parser)
class Preprocessor {
static inline function expr(e:Expr) return Tools.expr(e);
private static var importStackName:Array<String> = [];
private static var importStackMode:Array<KImportMode> = [];
private static function addImport(e:String, mode:KImportMode = INormal) {
for(i in importStackName) if(i == e) return;
importStackName.push(e);
importStackMode.push(mode);
}
private static function popImport(e:Expr) {
return mk(EImport(importStackName.pop(), importStackMode.pop()), e);
}
/**
* Preprocesses the expression, like 'a'.code => 97
* Also for transforming any abstracts into their implementations (TODO)
* Also for transforming any static extensions into their real form (TODO)
* Also to automatically add imports for stuff that is not imported
**/
public static function process(e:Expr, top:Bool = true):Expr {
importStackName = [];
importStackMode = [];
var e = _process(e, top);
// Automatically add imports for stuff
switch(expr(e)) {
case EBlock(exprs):
while(importStackName.length > 0) {
exprs.unshift(popImport(e));
}
return mk(EBlock(exprs), e);
default:
if(importStackName.length > 0) {
var exprs = [];
while(importStackName.length > 0) {
exprs.unshift(popImport(e));
}
exprs.push(e);
return mk(EBlock(exprs), e);
}
}
return e;
}
private static function _process(e:Expr, top:Bool = true):Expr {
if(e == null)
return null;
// If stuff looks wrong, add this back
//e = Tools.map(e, function(e) {
// return _process(e, false);
//});
//trace(expr(e));
switch(expr(e)) {
case EField(expr(_) => EConst(CString(s)), "code", _): // Transform string.code into charCode
if(s.length != 1) {
throw Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI));
}
return mk(EConst(CInt(s.charCodeAt(0))), e);
case ECall(expr(_) => EField(expr(_) => EIdent("String"), "fromCharCode", _), [e]): // Seperate this later?
switch(expr(e)) { // should this be Optimizer?
case EConst(CInt(i)):
return mk(EConst(CString(String.fromCharCode(i))), e);
default:
}
if(!Preprocessor.isStringFromCharCodeFixed) {
// __StringWorkaround__fromCharCode(i);
#if !NO_FROM_CHAR_CODE_FIX
return mk(ECall(mk(EIdent("__StringWorkaround__fromCharCode"), e), [e]), e);
#else
throw Parser.getBaseError(EPreset(FROM_CHAR_CODE_NON_INT));
#end
}
// Automatically add imports for stuff
case ENew("String", _): addImport("String");
case EIdent("String"): addImport("String");
case ENew("StringBuf", _): addImport("StringBuf");
case EIdent("StringBuf"): addImport("StringBuf");
case EIdent("Bool"): addImport("Bool");
case EIdent("Float"): addImport("Float");
case EIdent("Int"): addImport("Int");
case ENew("IntIterator", _): addImport("IntIterator");
case EIdent("IntIterator"): addImport("IntIterator");
case EIdent("Array"): addImport("Array");
case EIdent("Sys"): addImport("Sys");
case EIdent("Std"): addImport("Std");
case EIdent("Type"): addImport("Type");
case EIdent("Reflect"): addImport("Reflect");
case EIdent("StringTools"): addImport("StringTools");
case EIdent("Math"): addImport("Math");
case ENew("Date", _): addImport("Date");
case EIdent("Date"): addImport("Date");
case EIdent("DateTools"): addImport("DateTools");
case EIdent("Lambda"): addImport("Lambda");
case ENew("Xml", _): addImport("Xml");
case EIdent("Xml"): addImport("Xml");
//case EIdent("List"): addImport("haxe.ds.List");
case ENew("EReg", _): addImport("EReg");
case EIdent("EReg"): addImport("EReg");
//case EField(expr(_) => EIdent("EReg"), "escape", _):
// addImport("EReg");
default:
}
e = Tools.map(e, function(e) {
return _process(e, false);
});
return e;
}
static function mk(e:ExprDef, s:Expr):Expr {
#if hscriptPos
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
#else
return e;
#end
}
public static var isStringFromCharCodeFixed(get, null):Null<Bool> = null;
static function get_isStringFromCharCodeFixed():Null<Bool> {
if(isStringFromCharCodeFixed == null) {
try {
Reflect.callMethod(null, Reflect.field(String, "fromCharCode"), [65]);
isStringFromCharCodeFixed = true;
} catch(e:Dynamic) {
isStringFromCharCodeFixed = false;
}
}
return isStringFromCharCodeFixed;
}
}
+262 -480
View File
@@ -30,33 +30,23 @@ class Printer {
public function new() { public function new() {
} }
public function exprToString( e : Expr ) { public function exprToString( e : Expr ):String {
buf = new StringBuf(); buf = new StringBuf();
tabs = ""; tabs = "";
expr(e); expr(e);
return buf.toString(); return buf.toString();
} }
public static function convertExprToString( e : Expr ) { public function typeToString( t : CType ):String {
var printer = new Printer();
return printer.exprToString(e);
}
public function typeToString( t : CType ) {
buf = new StringBuf(); buf = new StringBuf();
tabs = ""; tabs = "";
type(t); type(t);
return buf.toString(); return buf.toString();
} }
public static function convertTypeToString( t : CType ) { inline function add<T>(s:T):Void buf.add(s);
var printer = new Printer();
return printer.typeToString(t);
}
inline function add<T>(s:T) buf.add(s); function type( t : CType ):Void {
function type( t : CType ) {
switch( t ) { switch( t ) {
case CTOpt(t): case CTOpt(t):
add('?'); add('?');
@@ -78,14 +68,11 @@ class Printer {
type(t); type(t);
case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))): case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))):
add('('); add('(');
var first = true; for (a in args)
for (i=>a in args) {
switch a { switch a {
case CTNamed(_, _): type(a); case CTNamed(_, _): type(a);
default: type(CTNamed('_', a)); default: type(CTNamed('_', a));
} }
if( i != args.length - 1 ) add(", ");
}
add(')->'); add(')->');
type(ret); type(ret);
case CTFun(args, ret): case CTFun(args, ret):
@@ -114,512 +101,307 @@ class Printer {
} }
} }
function addType( t : CType ) { function addType( t : CType ):Void {
if( t != null ) { if( t != null ) {
add(" : "); add(" : ");
type(t); type(t);
} }
} }
function block(e : Expr, addSpaceIfBlock : Bool = true) { function expr( e : Expr ):Void {
var isBlock = Tools.expr(e).match(EBlock(_));
if(isBlock) {
if(addSpaceIfBlock) add(" ");
} else {
add("\n");
tabs += "\t";
add(tabs);
}
expr(e);
if(!isBlock) {
//add("\n");
tabs = tabs.substr(1);
//add(tabs);
}
return isBlock;
}
function isJson(s:String) {
var len = s.length;
var i = 0;
while (i < len) {
switch (StringTools.fastCodeAt(s, i++)) {
// [a-zA-Z0-9_]+
case "a".code | "b".code | "c".code | "d".code | "e".code | "f".code | "g".code | "h".code | "i".code | "j".code | "k".code | "l".code | "m".code
| "n".code | "o".code | "p".code | "q".code | "r".code | "s".code | "t".code | "u".code | "v".code | "w".code | "x".code | "y".code | "z".code
| "A".code | "B".code | "C".code | "D".code | "E".code | "F".code | "G".code | "H".code | "I".code | "J".code | "K".code | "L".code | "M".code
| "N".code | "O".code | "P".code | "Q".code | "R".code | "S".code | "T".code | "U".code | "V".code | "W".code | "X".code | "Y".code | "Z".code
| "0".code | "1".code | "2".code | "3".code | "4".code | "5".code | "6".code | "7".code | "8".code | "9".code | "_".code:
case _:
return true;
}
}
return false;
}
static inline function isPrintable( c : Int ) {
return c >= 32 && c <= 126;
}
static inline function hex( c : Int, ?len : Int = 2 ) {
return StringTools.hex(c, len).toLowerCase();
}
public static function getEscapedString( s : String ) {
var buf = new StringBuf();
#if target.unicode
var s = new UnicodeString(s);
#end
for( i in 0...s.length ) {
#if target.unicode
var c:Null<Int> = s.charCodeAt(i);
#else
var c:Null<Int> = StringTools.unsafeCodeAt(s, i);
#end
switch( c ) {
case '"'.code: buf.add('\\"');
case '\\'.code: buf.add('\\\\');
case '\n'.code: buf.add('\\n');
case '\r'.code: buf.add('\\r');
case '\t'.code: buf.add('\\t');
default:
if(c == null) continue;
if(isPrintable(c))
buf.addChar(c);
else {
if(c > 0xFF) {
buf.add("\\u{");
buf.add(hex(c, null));
buf.add("}");
} else {
buf.add("\\x");
buf.add(hex((c & 0xFF)));
}
}
}
}
return buf.toString();
}
function getVar(v:VarN) {
if( v == null ) {
return ("??NULL??");
}
if(v is Int) {
return ("$" + v);
} else {
return (v);
}
}
public static function getBinaryOp(op:Binop) {
return switch(op) {
case OpAdd: "+";
case OpSub: "-";
case OpMult: "*";
case OpDiv: "/";
case OpMod: "%";
case OpAnd: "&";
case OpOr: "|";
case OpXor: "^";
case OpShl: "<<";
case OpShr: ">>";
case OpUShr: ">>>";
case OpEq: "==";
case OpNotEq: "!=";
case OpGt: ">";
case OpGte: ">=";
case OpLt: "<";
case OpLte: "<=";
case OpBoolAnd: "&&";
case OpBoolOr: "||";
case OpIs: "is";
case OpNullCoal: "??";
case OpAssign: "=";
case OpArrow: "=>";
case OpInterval: "...";
case OpAssignOp(op): getBinaryOp(op) + "=";
}
}
public static function getUnaryOp(op:Unop) {
return switch(op) {
case OpIncrement: "++";
case OpDecrement: "--";
case OpNot: "!";
case OpNeg: "-";
case OpNegBits: "~";
case OpSpread: "...";
}
}
function expr( e : Expr ) {
if( e == null ) { if( e == null ) {
add("??NULL??"); add("??NULL??");
return; return;
} }
// TODO: make else if print correctly
switch( Tools.expr(e) ) { switch( Tools.expr(e) ) {
case EImport(c, mode): case EIgnore(_):
add("import " + c); case EImport(c, n):
switch(mode) { add("import " + c);
case IAs(name): add(' as $name'); if(n != null)
case IAll: add('.*'); add(' as $n');
default: case EClass(name, fields, extend, interfaces):
} add('class $name');
case EClass(name, fields, extend, interfaces): if (extend != null)
add('class ${getVar(name)}'); add(' extends $extend');
if (extend != null) for(_interface in interfaces) {
add(' extends $extend'); add(' implements $_interface');
for(_interface in interfaces) { }
add(' implements $_interface'); add(' {\n');
} tabs += "\t";
add(' {\n'); //for(field in fields) {
tabs += "\t"; // expr(field);
// TODO: Print fields //}
//for(field in fields) {
// expr(field);
//}
tabs = tabs.substr(1);
add("}");
case ERedirect(n, cl):
add('typedef $n = $cl');
case EConst(c):
switch( c ) {
case CInt(i): add(i);
case CFloat(f): add(f);
case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"');
}
case EIdent(v):
add(v);
case EVar(n, t, e): // TODO: static, public, override
add("var " + n);
addType(t);
if( e != null ) {
add(" = ");
expr(e);
}
case EParent(e):
add("("); expr(e); add(")");
case EBlock(el):
if( el.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( e in el ) {
add(tabs);
expr(e);
add(";\n");
}
tabs = tabs.substr(1); tabs = tabs.substr(1);
add("}"); add("}");
case EConst(c): }
switch( c ) { case EField(e, f, s):
case CInt(i): add(i); expr(e);
case CFloat(f): add(f); add((s == true ? "?." : ".") + f);
case CString(s): case EBinop(op, e1, e2):
add('"'); expr(e1);
add(getEscapedString(s)); add(" " + op + " ");
add('"'); expr(e2);
} case EUnop(op, pre, e):
case EIdent(v): if( pre ) {
add(getVar(v)); add(op);
case EVar(n, t, e): // TODO: static, public, override
add("var " + getVar(n));
if( t != null )
addType(t);
else
switch( Tools.expr(e) ) {
case EMapDecl(type, _, _):
add(" : Map<");
switch( type ) {
case ObjectMap: add("Dynamic");
case StringMap: add("String");
case EnumMap: add("EnumValue");
case IntMap: add("Int");
case UnknownMap: add("?");
case Null: add("Null");
}
add(", Dynamic>");
default:
}
if( e != null ) {
add(" = ");
expr(e);
}
case EParent(e):
add("("); expr(e); add(")");
case EBlock(el):
if( el.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( e in el ) {
add(tabs);
expr(e);
add(";\n");
}
tabs = tabs.substr(1);
add(tabs);
add("}");
}
case EField(e, f, s):
expr(e); expr(e);
add((s == true ? "?." : ".") + f); } else {
case EBinop(op, e1, e2): expr(e);
/*var op1 = switch(Tools.expr(e1)) { add(op);
case EBinop(op, _, _): op; }
case EConst(_): "_"; case ECall(e, args):
case EIdent(_): "_"; if( e == null )
default: null; expr(e);
else switch( #if hscriptPos e.e #else e #end ) {
case EField(_), EIdent(_), EConst(_):
expr(e);
default:
add("(");
expr(e);
add(")");
}
add("(");
var first = true;
for( a in args ) {
if( first ) first = false else add(", ");
expr(a);
}
add(")");
case EIf(cond,e1,e2):
add("if( ");
expr(cond);
add(" ) ");
expr(e1);
if( e2 != null ) {
add(" else ");
expr(e2);
}
case EWhile(cond,e):
add("while( ");
expr(cond);
add(" ) ");
expr(e);
case EDoWhile(cond,e):
add("do ");
expr(e);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e, ithv):
if(ithv != null)
add("for( "+ithv+" => "+v+" in ");
else
add("for( "+v+" in ");
expr(it);
add(" ) ");
expr(e);
case EBreak:
add("break");
case EContinue:
add("continue");
case EFunction(params, e, name, ret): // TODO: static, public, override
add("function");
if( name != null )
add(" " + name);
add("(");
var first = true;
for( a in params ) {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(a.name);
addType(a.t);
}
add(")");
addType(ret);
add(" ");
expr(e);
case EReturn(e):
add("return");
if( e != null ) {
add(" ");
expr(e);
}
case EArray(e,index):
expr(e);
add("[");
expr(index);
add("]");
case EArrayDecl(el, _):
add("[");
var first = true;
for( e in el ) {
if( first ) first = false else add(", ");
expr(e);
}
add("]");
case ENew(cl, args):
add("new " + cl + "(");
var first = true;
for( e in args ) {
if( first ) first = false else add(", ");
expr(e);
}
add(")");
case EThrow(e):
add("throw ");
expr(e);
case ETry(e, v, t, ecatch):
add("try ");
expr(e);
add(" catch( " + v);
addType(t);
add(") ");
expr(ecatch);
case EObject(fl):
if( fl.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( f in fl ) {
add(tabs);
add(f.name+" : ");
expr(f.e);
add(",\n");
} }
var op2 = switch(Tools.expr(e2)) { tabs = tabs.substr(1);
case EBinop(op, _, _): op; add("}");
case EConst(_): "_"; }
case EIdent(_): "_"; case ETernary(c,e1,e2):
default: null; expr(c);
} add(" ? ");
var paran = Tools.checkOpPrecedence(op, op1, op2);*/ expr(e1);
var paran = 2; add(" : ");
expr(e2);
var op = getBinaryOp(op); case ESwitch(e, cases, def):
add("switch( ");
if(paran == 0 || paran == 2) { expr(e);
add("("); add(") {");
expr(e1); for( c in cases ) {
add(")"); add("case ");
} else { var first = true;
expr(e1); for( v in c.values ) {
} if( first ) first = false else add(", ");
if(op == "...") expr(v);
add(op);
else
add(" " + op + " ");
if(paran == 1 || paran == 2) {
add("(");
expr(e2);
add(")");
} else {
expr(e2);
}
case EUnop(op, pre, e):
var op = getUnaryOp(op);
if( pre ) {
add(op);
expr(e);
} else {
expr(e);
add(op);
}
case ECall(e, args):
if( e == null )
expr(e);
else switch( Tools.expr(e) ) {
case EField(_), EIdent(_), EConst(_):
expr(e);
default:
add("(");
expr(e);
add(")");
} }
add(": ");
expr(c.expr);
add(";\n");
}
if( def != null ) {
add("default: ");
expr(def);
add(";\n");
}
add("}");
case EMeta(name, args, e):
add("@");
add(name);
if( args != null && args.length > 0 ) {
add("("); add("(");
var first = true; var first = true;
for( a in args ) { for( a in args ) {
if( first ) first = false else add(", ");
expr(a);
}
add(")");
case EIf(cond,e1,e2):
add("if( ");
expr(cond);
add(" )");
var wasBlock = block(e1, true);
if( e2 != null ) {
if(!wasBlock) add("\n" + tabs);
add(" else ");
block(e2);
}
case EWhile(cond,e):
add("while( ");
expr(cond);
add(" )");
block(e, true);
case EDoWhile(cond,e):
add("do");
block(e, true);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e):
add("for( "+getVar(v)+" in ");
expr(it);
add(" )");
block(e, true);
case EForKeyValue(v, it, e, ithv):
add("for( "+getVar(ithv)+" => "+getVar(v)+" in ");
expr(it);
add(" )");
block(e, true);
case EBreak:
add("break");
case EContinue:
add("continue");
case EFunction(params, e, name, ret): // TODO: static, public, override
add("function");
if( name != null )
add(" " + getVar(name));
add("(");
var first = true;
for( a in params ) {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(getVar(a.name));
addType(a.t);
}
add(")");
addType(ret);
add(" ");
expr(e);
case EReturn(e):
add("return");
if( e != null ) {
add(" ");
expr(e);
}
case EArray(e,index):
expr(e);
add("[");
expr(index);
add("]");
case EMapDecl(type, keys, values):
add("[");
var first = true;
for( i in 0...keys.length ) {
if( first ) first = false else add(", ");
expr(keys[i]);
add(" => ");
expr(values[i]);
}
add("]");
case EArrayDecl(el):
add("[");
var first = true;
for( e in el ) {
if( first ) first = false else add(", ");
expr(e);
}
add("]");
case ENew(cl, args):
add("new " + getVar(cl) + "(");
var first = true;
for( e in args ) {
if( first ) first = false else add(", "); if( first ) first = false else add(", ");
expr(e); expr(e);
} }
add(")"); add(")");
case EThrow(e): }
add("throw "); add(" ");
expr(e); expr(e);
case ETry(e, v, t, ecatch): case ECheckType(e, t):
add("try"); add("(");
var wasBlock = block(e); expr(e);
if( !wasBlock ) { add(" : ");
add("\n" + tabs); addType(t);
} else add(" "); add(")");
add("catch( " + v); case EUsing(name):
addType(t); add("using ");
add(" )"); add(name);
block(ecatch); case EEnum(name, params):
case EObject(fl): if (params.length == 0) {
if( fl.length == 0 ) { add("enum " + name + " {}");
add("{}"); return;
} else { }
tabs += "\t"; add("enum " + name + " {\n");
add("{\n"); tabs += "\t";
for( i=>f in fl ) { for (p in params) {
add(tabs);
var name = isJson(f.name) ? "\"" + f.name + "\"" : f.name;
add(name+" : ");
expr(f.e);
if( i != fl.length - 1 ) add(",");
add("\n");
}
tabs = tabs.substr(1);
add(tabs);
add("}");
}
case ETernary(c,e1,e2):
expr(c);
add(" ? ");
expr(e1);
add(" : ");
expr(e2);
case ESwitch(e, cases, def):
add("switch( ");
expr(e);
add(" ) {\n");
tabs += "\t";
for( c in cases ) {
add(tabs);
add("case ");
var first = true;
for( v in c.values ) {
if( first ) first = false else add(", ");
expr(v);
}
add(":");
block(c.expr, true);
add(";\n");
}
if( def != null ) {
add(tabs);
add("default: ");
block(def, true);
add(";\n");
}
tabs = tabs.substr(1);
add(tabs); add(tabs);
add("}"); switch p {
case EMeta(name, args, e): case EConstructor(name, args):
add("@"); add(name);
add(name); add("(");
if( args != null && args.length > 0 ) { for (a in args)
add("("); addArgument(a);
var first = true; add(")");
for( a in args ) { case ESimple(name):
if( first ) first = false else add(", "); add(name);
expr(e);
}
add(")");
} }
add(" "); add(";\n");
expr(e); }
case ECheckType(e, t): tabs = tabs.substr(1);
add("("); add(tabs);
expr(e); add("}");
add(" : ");
addType(t);
add(")");
#if !HSCRIPT_NO_INT_VARS
case EInfo(_, e):
expr(e);
#end
} }
} }
public inline static function toString( e : Expr ) { function addArgument(a: Argument) {
if (a.opt)
add("?");
add(a.name);
addType(a.t);
}
public static function toString( e : Expr ):String {
return new Printer().exprToString(e); return new Printer().exprToString(e);
} }
public inline static function compareErrors( e1 : Error, e2 : Error ) { public static function errorToString( e : Expr.Error ):String {
return Type.enumEq(Tools.cleanError(e1), Tools.cleanError(e2)); var message = switch( #if hscriptPos e.e #else e #end ) {
}
public inline static function getPrintableError( e : Error ) {
return Tools.cleanError(switch( Tools.cleanError(e) ) {
case EPreset(msg): Parser.getBaseError(ECustom(msg.toString()));
default: e;
});
}
public static function errorToString( e : Error ) {
var message = switch( Tools.cleanError(e) ) {
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")"; case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
case EUnexpected(s): "Unexpected token: \""+s+"\""; case EUnexpected(s): "Unexpected token: \""+s+"\"";
case EUnterminatedString: "Unterminated string"; case EUnterminatedString: "Unterminated string";
case EUnterminatedComment: "Unterminated comment"; case EUnterminatedComment: "Unterminated comment";
case EUnterminatedRegex: "Unterminated regular expression";
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")"; case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
case EUnknownVariable(v): "Unknown variable: "+v; case EUnknownVariable(v): "Unknown variable: "+v;
case EInvalidIterator(v): "Invalid iterator: "+v; case EInvalidIterator(v): "Invalid iterator: "+v;
case EInvalidType(t): "Invalid type: "+t;
case EInvalidOp(op): "Invalid operator: "+op; case EInvalidOp(op): "Invalid operator: "+op;
case EInvalidAccess(f, on) if (on != null): "Invalid access to field " + f + " on " + on;
case EInvalidAccess(f): "Invalid access to field " + f; case EInvalidAccess(f): "Invalid access to field " + f;
case ECustom(msg): msg; case ECustom(msg): msg;
case EPreset(msg): msg.toString();
case EInvalidClass(cla): "Invalid class: " + cla + " was not found."; case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.'; case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
case EInvalidEscape(s): "Invalid escape sequence: " + s;
}; };
#if hscriptPos #if hscriptPos
return e.origin + ":" + e.line + ": " + message; return e.origin + ":" + e.line + ": " + message;
+190 -252
View File
@@ -21,131 +21,79 @@
*/ */
package hscript; package hscript;
import hscript.Expr; import hscript.Expr;
import hscript.utils.UnsafeReflect;
using StringTools;
class Tools { class Tools {
public static function iter( e : Expr, f : Expr -> Void ) { public static function iter( e : Expr, f : Expr -> Void ):Void {
switch( expr(e) ) { switch( expr(e) ) {
case EConst(_), EIdent(_): f(e); case EConst(_), EIdent(_):
case EImport(c, _): f(e); case EImport(c): f(e);
case EClass(_, e, _, _): for( a in e ) f(a); case EClass(_, e, _, _): for( a in e ) f(a);
case EVar(_, _, e): if( e != null ) f(e); case EVar(_, _, e): if( e != null ) f(e);
case EParent(e, _): f(e); case EParent(e): f(e);
case EBlock(el): for( e in el ) f(e); case EBlock(el): for( e in el ) f(e);
case EField(e, _): f(e); case EField(e, _): f(e);
case EBinop(_, e1, e2): f(e1); f(e2); case EBinop(_, e1, e2): f(e1); f(e2);
case EUnop(_, _, e): f(e); case EUnop(_, _, e): f(e);
case ECall(e, args): f(e); for( a in args ) f(a); case ECall(e, args): f(e); for( a in args ) f(a);
case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2); case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2);
case EWhile(c, e): f(c); f(e); case EWhile(c, e): f(c); f(e);
case EDoWhile(c, e): f(c); f(e); case EDoWhile(c, e): f(c); f(e);
case EFor(_, it, e): f(it); f(e); case EFor(_, it, e): f(it); f(e);
case EForKeyValue(_, it, e, _): f(it); f(e); case EBreak,EContinue:
case EBreak,EContinue: case EFunction(_, e, _, _): f(e);
case EFunction(_, e, _, _): f(e); case EReturn(e): if( e != null ) f(e);
case EReturn(e): if( e != null ) f(e); case EArray(e, i): f(e); f(i);
case EArray(e, i): f(e); f(i); case EArrayDecl(el): for( e in el ) f(e);
case EMapDecl(type, keys, values): for( e in keys ) f(e); for( e in values ) f(e); case ENew(_,el): for( e in el ) f(e);
case EArrayDecl(el): for( e in el ) f(e); case EThrow(e): f(e);
case ENew(_,el): for( e in el ) f(e); case ETry(e, _, _, c): f(e); f(c);
case EThrow(e): f(e); case EObject(fl): for( fi in fl ) f(fi.e);
case ETry(e, _, _, c): f(e); f(c); case ETernary(c, e1, e2): f(c); f(e1); f(e2);
case EObject(fl): for( fi in fl ) f(fi.e); case ESwitch(e, cases, def):
case ETernary(c, e1, e2): f(c); f(e1); f(e2); f(e);
case ESwitch(e, cases, def): for( c in cases ) {
f(e); for( v in c.values ) f(v);
for( c in cases ) { f(c.expr);
for( v in c.values ) f(v); }
f(c.expr); if( def != null ) f(def);
} case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
if( def != null ) f(def); case ECheckType(e,_): f(e);
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e); default:
case ECheckType(e,_): f(e);
#if !HSCRIPT_NO_INT_VARS
case EInfo(info, e): f(e);
#end
} }
} }
public static function iterExprRecursive( ve : Expr, f : Expr -> Void ) { public static function map( e : Expr, f : Expr -> Expr ):Expr {
var rec = (e:Expr) -> iterExprRecursive(e, f);
f(ve);
switch( expr(ve) ) {
case EConst(_), EIdent(_): {}
case EImport(c, _): {}
case EClass(_, e, _, _): for( a in e ) rec(a);
case EVar(_, _, e): if( e != null ) rec(e);
case EParent(e, _): rec(e);
case EBlock(el): for( e in el ) rec(e);
case EField(e, _): rec(e);
case EBinop(_, e1, e2): rec(e1); rec(e2);
case EUnop(_, _, e): rec(e);
case ECall(e, args): rec(e); for( a in args ) rec(a);
case EIf(c, e1, e2): rec(c); rec(e1); if( e2 != null ) rec(e2);
case EWhile(c, e): rec(c); rec(e);
case EDoWhile(c, e): rec(c); rec(e);
case EFor(_, it, e): rec(it); rec(e);
case EForKeyValue(_, it, e, _): rec(it); rec(e);
case EBreak,EContinue: {}
case EFunction(_, e, _, _): rec(e);
case EReturn(e): if( e != null ) rec(e);
case EArray(e, i): rec(e); rec(i);
case EMapDecl(type, keys, values): for( e in keys ) rec(e); for( e in values ) rec(e);
case EArrayDecl(el): for( e in el ) rec(e);
case ENew(_,el): for( e in el ) rec(e);
case EThrow(e): rec(e);
case ETry(e, _, _, c): rec(e); rec(c);
case EObject(fl): for( fi in fl ) rec(fi.e);
case ETernary(c, e1, e2): rec(c); rec(e1); rec(e2);
case ESwitch(e, cases, def):
rec(e);
for( c in cases ) {
for( v in c.values ) rec(v);
rec(c.expr);
}
if( def != null ) rec(def);
case EMeta(name, args, e): if( args != null ) for( a in args ) rec(a); rec(e);
case ECheckType(e,_): rec(e);
#if !HSCRIPT_NO_INT_VARS
case EInfo(info, e): rec(e);
#end
}
}
public static function map( e : Expr, f : Expr -> Expr ) {
var edef = switch( expr(e) ) { var edef = switch( expr(e) ) {
case EConst(_), EIdent(_), EBreak, EContinue: expr(e); case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
case EVar(n, t, e, p, s): EVar(n, t, if( e != null ) f(e) else null, p, s); case EVar(n, t, e, isPublic, isStatic, isPrivate): EVar(n, t, if( e != null ) f(e) else null, isPublic, isStatic, isPrivate);
case EParent(e, no): EParent(f(e), no); case EParent(e): EParent(f(e));
case EBlock(el): EBlock([for( e in el ) f(e)]); case EBlock(el): EBlock([for( e in el ) f(e)]);
case EField(e, fi, s): EField(f(e),fi,s); case EField(e, fi): EField(f(e),fi);
case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2)); case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
case EUnop(op, pre, e): EUnop(op, pre, f(e)); case EUnop(op, pre, e): EUnop(op, pre, f(e));
case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]); case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]);
case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null); case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null);
case EWhile(c, e): EWhile(f(c),f(e)); case EWhile(c, e): EWhile(f(c),f(e));
case EDoWhile(c, e): EDoWhile(f(c),f(e)); case EDoWhile(c, e): EDoWhile(f(c),f(e));
case EFor(v, it, e): EFor(v, f(it), f(e)); case EFor(v, it, e): EFor(v, f(it), f(e));
case EForKeyValue(v, it, e, ithv): EForKeyValue(v, f(it), f(e), ithv); case EFunction(args, e, name, t, isPublic, isStatic, isOverride, isPrivate): EFunction(args, f(e), name, t, isPublic, isStatic, isOverride, isPrivate);
case EFunction(args, e, name, t, p, s, o): EFunction(args, f(e), name, t, p, s, o); case EReturn(e): EReturn(if( e != null ) f(e) else null);
case EReturn(e): EReturn(if( e != null ) f(e) else null); case EArray(e, i): EArray(f(e),f(i));
case EArray(e, i): EArray(f(e),f(i)); case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
case EMapDecl(type, keys, values): EMapDecl(type, [for( e in keys ) f(e)], [for( e in values ) f(e)]); case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]); case EThrow(e): EThrow(f(e));
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]); case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
case EThrow(e): EThrow(f(e)); case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
case ETry(e, v, t, c): ETry(f(e), v, t, f(c)); case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
case EObject(fl): EObject([for( fi in fl ) new ObjectField(fi.name, f(fi.e))]); case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) { values : [for( v in c.values ) f(v)], expr : f(c.expr) } ], def == null ? null : f(def));
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2)); case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) { values : [for( v in c.values ) f(v)], expr : f(c.expr) } ], def == null ? null : f(def)); case ECheckType(e,t): ECheckType(f(e), t);
case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e)); case EImport(c): EImport(c);
case ECheckType(e,t): ECheckType(f(e), t); case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
case EImport(c, m): EImport(c, m); default: #if hscriptPos e.e #else e #end;
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
#if !HSCRIPT_NO_INT_VARS
case EInfo(info, e): EInfo(info, f(e));
#end
} }
return mk(edef, e); return mk(edef, e);
} }
@@ -158,165 +106,155 @@ class Tools {
#end #end
} }
public static inline function cleanError( e : Error ) {
#if hscriptPos
return e.e;
#else
return e;
#end
}
public static inline function mk( e : ExprDef, p : Expr ):Expr { public static inline function mk( e : ExprDef, p : Expr ):Expr {
#if hscriptPos #if hscriptPos
return new Expr(e, p.pmin, p.pmax, p.origin, p.line); return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
#else #else
return e; return e;
#end #end
} }
public static function isValidBinOp(op:String):Bool { @:access(hscript.Interp)
if(op == ("??"+"=")) return true; public static function clearClasses() {
return switch(op) { #if CUSTOM_CLASSES
case "+" | "-" | "*" | "/" | "%" | "&" | "|" | "^" | "<<" | ">>" | ">>>" | "==" | "!=" | ">=" | "<=" | ">" | "<" | "||" | "&&" | "is" | "=" | "??" | "..." | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | ">>>=": true; Interp._customClasses.clear();
case "=>": true; Interp._customClassAliases.clear();
default: false; #end
}
} }
public static function getOpEnum(op:String):Binop { public static function removeInnerClass(name: String): String {
return switch(op) { var ll = name.lastIndexOf(".");
case "+": OpAdd; if (name.indexOf(".") != ll) { // checks if there are 2 or more dots
case "-": OpSub; // wtf did i make -neo
case "*": OpMult; // this is awesome -crow
case "/": OpDiv; return name.substr(0, name.lastIndexOf(".", ll - 1)) + "." + name.substr(ll + 1);
case "%": OpMod;
case "&": OpAnd;
case "|": OpOr;
case "^": OpXor;
case "<<": OpShl;
case ">>": OpShr;
case ">>>": OpUShr;
case "==": OpEq;
case "!=": OpNotEq;
case ">=": OpGte;
case "<=": OpLte;
case ">": OpGt;
case "<": OpLt;
case "||": OpBoolOr;
case "&&": OpBoolAnd;
case "is": OpIs;
case "=": OpAssign;
case "=>": OpArrow;
case "??": OpNullCoal;
case "...": OpInterval;
default: {
var op2 = op.substr(0, op.length - 1);
if(isValidBinOp(op2)) {
return OpAssignOp(getOpEnum(op2));
}
throw "Unknown binary operator: " + op;
}
} }
// only one dot or no dots (will work since -1 + 1 = 0)
return name.substr(ll + 1);
} }
public static function getUnopEnum(op:String):Unop { // TODO: maybe use this function for import since also check for innerclasses
return switch(op) { public static function getClass(name: String): Dynamic {
case "++": OpIncrement; var splitClassName = [for (e in name.split(".")) e.trim()];
case "--": OpDecrement; var realClassName = splitClassName.join(".");
case "!": OpNot;
case "-": OpNeg; var c: Dynamic = Type.resolveClass(realClassName);
case "~": OpNegBits; if (c == null) // try importing as enum
case "...": OpSpread; try
default: throw "Unknown unary operator: " + op; c = Type.resolveEnum(name);
if (c == null) {
// lastly try removing any inner class from it
// this allows you to import stuff like
// flixel.text.FlxText.FlxTextBorderStyle
// without the script crashing immediately
var className = removeInnerClass(realClassName);
if (className != name) {
c = Type.resolveClass(className);
if (c == null)
c = Type.resolveEnum(className);
}
} }
return c;
} }
static var priorities = [ public static function isIterable(v: Dynamic): Bool {
["%"], // TODO: test for php and lua, they might have issues with this check
["*", "/"], return v != null && v.iterator != null;
["+", "-"], }
["<<", ">>", ">>>"],
["|", "&", "^"],
["==", "!=", ">", "<", ">=", "<="],
["..."],
["&&"],
["||"],
["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^=","=>","??"+"="],
["->", "??"],
["is"]
];
public static function checkOpPrecedence(mainOp:String, leftOp:String, rightOp:String):Int {
var mainOpGroup = getOpGroup(mainOp);
var leftOpGroup = getOpGroup(leftOp);
var rightOpGroup = getOpGroup(rightOp);
var leftParam = false; /**
var rightParam = false; * DO NOT USE INLINE ON THIS FUNCTION
**/
if(leftOpGroup > mainOpGroup && leftOpGroup != -1) leftParam = true; public static function argCount(func: haxe.Constraints.Function): Int {
if(rightOpGroup > mainOpGroup && rightOpGroup != -1) rightParam = true; #if cpp
return untyped __cpp__("{0}->__ArgCount()", func);
if(!leftParam && !rightParam) { #elseif js
var mainOpIndex = getOpIndex(mainOp); return untyped js.Syntax.code("{0}.length", func);
var leftOpIndex = getOpIndex(leftOp); #else
var rightOpIndex = getOpIndex(rightOp);
if(mainOpGroup == rightOpGroup) {
if(rightOpIndex < mainOpIndex) rightParam = true;
}
if(leftOpGroup == mainOpGroup) {
if(leftOpIndex < mainOpIndex) leftParam = true;
}
}
// Convert to index
if(!leftParam && !rightParam) return -1;
if(leftParam && rightParam) return 2;
if(leftParam) return 0;
if(rightParam) return 1;
return -1; return -1;
#end
} }
public static function getOpIndex(op:String):Int { public static function getClassDeclFields(classDecl:hscript.Expr.ClassDecl, onlyStatic:Bool = false):Array<FieldDecl> {
if(op == "_") return -1; var fields = classDecl.fields.filter(function(field:FieldDecl) {
var i = 0; var isStatic = field.access.contains(AStatic);
for(p in priorities) { return onlyStatic ? isStatic : !isStatic;
for(pp in p) { });
if(op == pp) return fields;
return i;
i++;
}
}
return -1;
} }
public static function getOpGroup(op:String):Int { }
if(op == "_") return -1;
var i = 0; /**
for(p in priorities) { * @see https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L217
for(pp in p) { */
if(op == pp) class EnumValue {
return i; public var enumName: String;
} public var name: String;
i++; public var index: Int;
} public var args: Array<Dynamic>;
return -1; public var enumParent:Dynamic;
public function new(enumName: String, name: String, index: Int, enumParent:Dynamic, ?args: Array<Dynamic>) {
this.enumName = enumName;
this.name = name;
this.index = index;
this.enumParent = enumParent;
this.args = args;
} }
public function toString(): String {
if (args == null)
return enumName + "." + name;
return enumName + "." + name + "(" + [for (arg in args) arg].join(", ") + ")";
}
public static function getEnum(cl:Enum<Dynamic>):Dynamic { public inline function getEnumName(): String
var enumThingy:Dynamic = {}; return this.enumName;
for (c in cl.getConstructors()) {
try { public inline function getConstructorArgs(): Array<Dynamic>
UnsafeReflect.setField(enumThingy, c, cl.createByName(c)); return this.args != null ? this.args : [];
} catch(e) {
try { public function compare(other: EnumValue): Bool {
UnsafeReflect.setField(enumThingy, c, Reflect.makeVarArgs((args:Array<Dynamic>) -> cl.createByName(c, args))); if (enumName != other.enumName || name != other.name)
} catch(ex) { return false;
throw e; if (args == null && other.args == null)
} return true;
} if (args == null || other.args == null)
return false;
if (args.length != other.args.length)
return false;
for (i in 0...args.length) {
// TODO: allow deep comparison, like arrays
if (args[i] != other.args[i])
return false;
} }
return enumThingy;
return true;
}
}
class HScriptEnum implements IHScriptCustomBehaviour {
public var enumValues(default, null) = {};
public function new() {}
public function setEnum(name:String, enumValue:Dynamic) {
Reflect.setField(enumValues, name, enumValue);
}
public function getEnumValue(name:String):Null<Dynamic> {
if(Reflect.hasField(enumValues, name)) return Reflect.field(enumValues, name);
return null;
}
public function hget(name:String):Dynamic {
return getEnumValue(name);
}
public function hset(name:String, val:Dynamic):Dynamic {
return null;
} }
} }
+512
View File
@@ -0,0 +1,512 @@
package hscript.customclass;
import hscript.utils.UnsafeReflect;
import haxe.Constraints.Function;
import hscript.Expr;
import hscript.Expr.FieldPropertyAccess;
import hscript.Expr.VarDecl;
import hscript.Expr.FunctionDecl;
import hscript.Expr.FieldDecl;
using Lambda;
using StringTools;
/**
* The Custom Class core.
*
* Provides handlers for custom classes.
*
* Rest in peace, CustomClassHandler.hx
*
* Based on Polymod Hscript class system
* @see https://github.com/larsiusprime/polymod/tree/master/polymod/hscript/_internal
*/
@:access(hscript.customclass.CustomClassDecl)
class CustomClass implements IHScriptCustomAccessBehaviour {
public var interp:Interp;
public var superClass:Dynamic;
public var superConstructor(default, null):Dynamic;
public var superIsCustomClass(get, never):Bool;
private function get_superIsCustomClass():Bool
return (superClass != null && superClass is CustomClass);
public var className(get, never):String;
private function get_className():String {
return __class.toString();
}
private var __class:CustomClassDecl;
private var __cachedSuperFields:Null<Map<String, Dynamic>> = null;
private var __cachedFieldDecls:Map<String, FieldDecl> = [];
private var __cachedFunctionDecls:Map<String, FunctionDecl> = [];
private var __cachedVarDecls:Map<String, VarDecl> = [];
public var accessContext:AccessContext = COuter;
public var __allowSetGet:Bool = false;
public var __allowPrivateAccess:Bool = false;
private var isInline(default, null):Bool = false;
private var ogVariables(default, null):Map<String, Dynamic>;
private var initializing(default, null):Bool = false; // Allows final variables to be initialized
public function new(__class:CustomClassDecl, args:Array<Dynamic>, ?extendFieldDecl:Map<String, Dynamic>, ?ogInterp:Interp, ?callNew:Bool = true) {
this.__class = __class;
this.interp = new Interp(this);
if (ogInterp != null) {
interp.importFailedCallback = ogInterp.importFailedCallback;
interp.errorHandler = ogInterp.errorHandler;
interp.allowStaticVariables = ogInterp.allowStaticVariables;
interp.staticVariables = ogInterp.staticVariables;
// todo: make it so you can use variables from the same scope as where the class was defined
if(__class.isInline != null && __class.isInline) {
isInline = __class.isInline;
ogVariables = ogInterp.variables;
interp.allowPublicVariables = __class.staticInterp.allowPublicVariables;
interp.publicVariables = __class.staticInterp.publicVariables;
}
}
buildImports();
buildUsings();
if (extendFieldDecl != null)
__cachedSuperFields = extendFieldDecl;
buildClass();
if (hasFunction('new') && callNew) {
buildSuperConstructor();
initializing = true;
callFunction('new', args);
initializing = false;
if (this.superClass == null && this.__class.classDecl.extend != null)
this.interp.error(ECustom("super() not called"));
} else if (__class.classDecl.extend != null) {
createSuperClass(args);
}
}
function buildClass() {
if (__cachedSuperFields == null)
__cachedSuperFields = [];
for (f in __class.classDecl.fields) {
if (f.access.contains(AStatic))
continue; // Skip static field. It's handled by CustomClassDecl.hx
__cachedFieldDecls.set(f.name, f);
switch (f.kind) {
case KFunction(fn):
__cachedFunctionDecls.set(f.name, fn);
#if hscriptPos
var fexpr:Expr = {
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
pmin: fn.body.pmin,
pmax: fn.body.pmax,
line: fn.body.line,
origin: fn.body.origin
};
#else
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
#end
var func:Function = this.interp.expr(fexpr);
this.interp.variables.set(f.name, func);
case KVar(v):
if(v.get != ADefault || v.set != ADefault)
__allowSetGet = true;
__cachedVarDecls.set(f.name, v);
if (v.expr != null) {
var varValue = this.interp.expr(v.expr);
this.interp.variables.set(f.name, varValue);
}
}
}
if (!__cachedSuperFields.empty()) {
for (f => v in __cachedSuperFields) {
this.hset(f, v);
}
__cachedSuperFields.clear();
}
}
function buildSuperConstructor() {
superConstructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
createSuperClass(args);
});
}
private function createSuperClass(?args:Array<Dynamic>) {
if (args == null)
args = [];
if(__class.superClassDecl is CustomClassDecl)
superClass = new CustomClass(__class.superClassDecl, args, __cachedSuperFields, this.interp);
else {
if (__cachedSuperFields != null) {
Reflect.setField(__class.superClassDecl, "__cachedFields", __cachedSuperFields); // Static field
}
var disallowCopy = Type.getInstanceFields(__class.superClassDecl);
superClass = Type.createInstance(__class.superClassDecl, args);
superClass.__customClass = this;
superClass.__real_fields = disallowCopy;
if(isInline) {
for(s => v in ogVariables)
if(!disallowCopy.contains(s))
interp.variables.set(s, v);
}
}
}
function buildImports() {
var i:Int = 0;
for (_import in __class.imports) {
var importedClass = _import.fullPath;
var importAlias = _import.as;
if (Interp.customClassExist(importedClass) && this.interp.importFailedCallback != null) {
this.interp.importFailedCallback(importedClass.split("."), importAlias);
continue;
}
#if hscriptPos
var e:Expr = {
e: ExprDef.EImport(importedClass, importAlias),
pmin: 0,
pmax: 0,
origin: this.className,
line: i
};
#else
var e = Expr.EImport(importedClass, importAlias);
#end
this.interp.expr(e);
i++;
}
}
inline function buildUsings() {
for (us in __class.usings) {
@:privateAccess this.interp.useUsing(us);
}
}
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
var r:Dynamic = null;
if (hasField(name)) {
var fn = getFunction(name);
try {
if (fn == null)
interp.error(ECustom('${name} is not a function'));
r = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args);
} catch (e:hscript.Expr.Error) {
// A script error occurred while executing the custom class function.
// Purge the function from the cache so it is not called again.
purgeFunction(name);
}
}
else {
var fixedArgs = [];
for (a in args) {
if ((a is CustomClass)) {
var customClass:CustomClass = cast(a, CustomClass);
fixedArgs.push(customClass.superClass != null ? customClass.getSuperclass() : customClass);
} else {
fixedArgs.push(a);
}
}
var superFn:Function = null;
if(superClass is CustomClass) {
superFn = cast(superClass, CustomClass).hget(name);
}
else {
var fixedName = '_HX_SUPER__${name}';
superFn = Reflect.field(superClass, fixedName);
}
if (superFn == null || !Reflect.isFunction(superFn)) {
this.interp.error(ECustom('Error while calling function super.${name}(): EInvalidAccess'
+ '\n'
+ 'InvalidAccess error: Super function "${name}" does not exist! Define it or call the correct superclass function.'));
}
r = Reflect.callMethod((superClass is CustomClass) ? null : superClass, superFn, fixedArgs);
}
return r;
}
// Field check
private function hasField(name:String):Bool {
return __cachedFieldDecls.exists(name);
}
private function getField(name:String):FieldDecl {
return __cachedFieldDecls != null ? __cachedFieldDecls.get(name) : null;
}
private function hasVar(name:String):Bool {
return __cachedVarDecls.exists(name);
}
private function getVar(name:String):VarDecl {
return __cachedVarDecls.get(name);
}
private function hasFunction(name:String):Bool {
return __cachedFunctionDecls.exists(name);
}
private function getFunction(name:String):Function {
var fn = this.interp.variables.get(name);
return Reflect.isFunction(fn) ? fn : null;
}
// SuperClass field check
private function cacheSuperField(name:String, value:Dynamic) {
if (__cachedSuperFields != null) {
__cachedSuperFields.set(name, value);
}
}
var __superClassFieldList:Array<String> = null;
public function superHasField(name:String):Bool {
if (superClass == null)
return false;
// Reflect.hasField(this, name) is REALLY expensive so we use a cache.
if (__superClassFieldList == null) {
__superClassFieldList = [];
if(superClass is CustomClass) {
var cls:Null<Dynamic> = superClass;
while(cls != null && cls is CustomClass) {
var currentClass = cast(cls, CustomClass);
var fields = [for(f in currentClass.__cachedFieldDecls.keys()) f];
__superClassFieldList.concat(fields);
var next = currentClass.superClass;
if(next == null)
break;
cls = next;
}
// The last fetched class is a real class
if(!(cls is CustomClass))
__superClassFieldList.concat(Reflect.fields(cls).concat(Type.getInstanceFields(Type.getClass(cls))));
}
else {
var realFields = Reflect.fields(superClass).concat(Type.getInstanceFields(Type.getClass(superClass)));
__superClassFieldList.concat(realFields);
}
}
return __superClassFieldList.indexOf(name) != -1;
}
/**
* Remove a function from the cache.
* This is useful when a function is broken and needs to be skipped.
* @param name The name of the function to remove from the cache.
*/
private function purgeFunction(name:String):Void {
if (__cachedFunctionDecls != null) {
__cachedFunctionDecls.remove(name);
}
}
// Access fields
public function hget(name:String):Dynamic {
switch (name) {
case "superClass": return this.superClass;
case "createSuperClass": return this.createSuperClass;
case "hasFunction": return this.hasFunction;
case "callFunction": return this.callFunction;
default:
if (hasFunction(name)) {
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
return this.callFunction(name, args);
});
return fn;
}
if (hasVar(name)) {
var v = getVar(name);
var getter = v.get;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.className;
default: false;
}
var value:Dynamic = null;
if(getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
throw 'field $name cannot be accessed for reading';
if (__allowSetGet && getter == AGet) {
if (hasFunction('get_$name'))
value = __callGetter(name);
else
throw 'Method get_$name required by property $name is missing';
}
else if (this.interp.variables.exists(name))
value = this.interp.variables.get(name);
else {
if (v.expr != null) {
value = this.interp.expr(v.expr);
this.interp.variables.set(name, value);
}
}
return value;
}
if (this.superClass != null) {
if (superHasField(name)) {
// Anonymous structure as a super class???
if (Type.getClass(this.superClass) == null) {
// Anonymous structure
if (Reflect.hasField(this.superClass, name))
return Reflect.field(this.superClass, name);
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
}
if (this.superClass is CustomClass) {
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
superCustomClass.__allowSetGet = this.__allowSetGet;
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
superCustomClass.accessContext = switch (accessContext) {
case CInner(_): CInner(superCustomClass.className);
default: COuter;
}
return superCustomClass.hget(name);
}
// Real Class
if (__allowSetGet)
Reflect.getProperty(this.superClass, name);
else
Reflect.field(this.superClass, name);
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
}
return null;
}
public function hset(name:String, val:Dynamic):Dynamic {
switch (name) {
default:
if (hasVar(name)) {
var v = getVar(name);
var setter = v.set;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.className;
default: false;
}
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || (v.isFinal && !initializing))
throw 'field $name cannot be accessed for writing';
if (__allowSetGet && setter == ASet) {
if (hasFunction('set_$name'))
return __callSetter(name, val);
else
throw 'Method set_$name required by property $name is missing';
}
this.interp.variables.set(name, val);
}
else if (this.superClass != null) {
if (superHasField(name)) {
if (Type.getClass(this.superClass) == null) {
// Anonymous structure
if (Reflect.hasField(this.superClass, name)) {
Reflect.setField(this.superClass, name, val);
return val;
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
}
if (this.superClass is CustomClass) {
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
superCustomClass.__allowSetGet = this.__allowSetGet;
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
superCustomClass.accessContext = switch (accessContext) {
case CInner(_): CInner(superCustomClass.className);
default: COuter;
}
return superCustomClass.hset(name, val);
}
// Real Class
if (__allowSetGet)
Reflect.setProperty(this.superClass, name, val);
else
Reflect.setField(this.superClass, name, val);
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
}
return val;
}
public function __callGetter(name:String):Dynamic {
__allowSetGet = false;
var r = callFunction('get_$name');
__allowSetGet = true;
return r;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
__allowSetGet = false;
var r = callFunction('set_$name', [val]);
__allowSetGet = true;
return r;
}
/**
* Returns the real superClass if the Custom Class
* extends another Custom Class, and so on until
* it reaches a real class, otherwise it will
* return the last fetched Custom Class
* @return Null<Dynamic>
*/
public function getSuperclass():Null<Dynamic> {
var cls:Null<Dynamic> = this.superClass;
// Check if the superClass is another custom class,
// so it will find for a real class, otherwise
// returns the last super CustomClass parent.
while(cls != null && cls is CustomClass) {
var next = cast(cls, CustomClass).superClass;
if(next == null)
break; // Return the Custom Class itself
cls = next;
}
return cls;
}
}
+290
View File
@@ -0,0 +1,290 @@
package hscript.customclass;
import hscript.Expr.AccessContext;
import hscript.proxy.ProxyType;
import hscript.customclass.utils.FunctionUtils;
import haxe.Constraints.Function;
import hscript.Expr.FieldDecl;
import hscript.Expr.VarDecl;
import hscript.Expr.FunctionDecl;
/**
* The ACTUAL "StaticHandler"
* @author Jamextreme140
*/
@:access(hscript.Interp)
@:structInit
class CustomClassDecl implements IHScriptCustomAccessBehaviour {
public var classDecl:Expr.ClassDecl; //This holds the class instantiation info
public var imports:Map<String, CustomClassImport>;
public var usings:Array<String>;
public var pkg:Null<Array<String>> = null;
public var ogInterp:Null<Interp> = null;
public var isInline:Null<Bool> = null;
public var staticInterp:Interp = new Interp();
public var superClassDecl(default, null):Dynamic = null; //This holds the super class reference.
var _cachedStaticFields:Map<String, FieldDecl> = [];
var _cachedStaticFunctions:Map<String, FunctionDecl> = [];
var _cachedStaticVariables:Map<String, VarDecl> = [];
public var __allowSetGet:Bool = true;
public var __allowPrivateAccess:Bool = false;
public var accessContext:AccessContext = COuter;
public final name:String;
public function new(classDecl:Expr.ClassDecl, imports:Map<String, CustomClassImport>, usings:Array<String>, ?pkg:Array<String>, ?ogInterp:Interp, ?isInline:Bool) {
this.classDecl = classDecl;
this.imports = imports;
this.usings = usings;
this.pkg = pkg;
this.ogInterp = ogInterp;
this.isInline = isInline;
// Cache the classname
var pkg = pkg != null ? '${pkg.join(".")}.' : "";
var className = classDecl.name;
this.name = '$pkg$className';
if(ogInterp != null) {
staticInterp.importFailedCallback = ogInterp.importFailedCallback;
staticInterp.errorHandler = ogInterp.errorHandler;
staticInterp.allowStaticVariables = ogInterp.allowStaticVariables;
staticInterp.staticVariables = ogInterp.staticVariables;
if(isInline != null && isInline) {
// uses public variables from the same scope as where the class was defined
staticInterp.variables = ogInterp.variables;
staticInterp.allowPublicVariables = ogInterp.allowPublicVariables;
staticInterp.publicVariables = ogInterp.publicVariables;
}
}
cacheImports();
processUsings();
cacheFields();
if(classDecl.extend != null)
buildSuperClass();
}
function cacheImports() {
// This will make imported classes available for Static Functions
var i:Int = 0;
for(s => imp in imports) {
var importedClass = imp.fullPath;
var importAlias = imp.as;
if(this.staticInterp.variables.exists(imp.name)) continue; // class is already imported
if (Interp.customClassExist(importedClass) && this.staticInterp.importFailedCallback != null) {
this.staticInterp.importFailedCallback(importedClass.split("."), importAlias);
continue;
}
#if hscriptPos
var e:Expr = {
e: ExprDef.EImport(importedClass, importAlias),
pmin: 0,
pmax: 0,
origin: this.classDecl.name,
line: i
};
i++;
#else
var e = Expr.EImport(importedClass, importAlias);
#end
this.staticInterp.expr(e);
}
}
function cacheFields() {
for (f in classDecl.fields) {
if (f.access.contains(AStatic)) {
_cachedStaticFields.set(f.name, f);
switch (f.kind) {
case KFunction(fn):
_cachedStaticFunctions.set(f.name, fn);
#if hscriptPos
var fexpr:Expr = {
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
pmin: fn.body.pmin,
pmax: fn.body.pmax,
line: fn.body.line,
origin: fn.body.origin
};
#else
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
#end
var func:Function = this.staticInterp.expr(fexpr);
this.staticInterp.variables.set(f.name, func);
case KVar(v):
if(v.get != ADefault || v.set != ADefault)
__allowSetGet = true;
_cachedStaticVariables.set(f.name, v);
if (v.expr != null) {
var varValue = this.staticInterp.expr(v.expr);
this.staticInterp.variables.set(f.name, varValue);
}
}
}
}
}
function processUsings() {
for(us in usings) {
this.staticInterp.useUsing(us);
}
}
function buildSuperClass() {
var extendString = new Printer().typeToString(classDecl.extend);
if (this.pkg != null && extendString.indexOf(".") == -1) {
extendString = this.pkg.join(".") + "." + extendString;
}
var cls:Dynamic = Type.resolveClass('${extendString}_HSX');
if(cls == null)
cls = ProxyType.resolveClass(extendString);
superClassDecl = cls;
if(superClassDecl == null)
staticInterp.error(ECustom("could not resolve super class: " + extendString));
}
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
var func:Function = getFunction(name);
return FunctionUtils.callStaticFunction(name, this, staticInterp, func, args != null ? args : []);
}
public function hasField(name:String):Bool {
return _cachedStaticFields.exists(name);
}
private function hasFunction(name:String) {
return _cachedStaticFunctions.exists(name);
}
private function getFunction(name:String):Function {
var fn = this.staticInterp.variables.get(name);
return Reflect.isFunction(fn) ? fn : null;
}
private function hasVar(name:String):Bool {
return _cachedStaticVariables.exists(name);
}
private function getVar(name:String):VarDecl {
return _cachedStaticVariables.get(name);
}
/**
* Remove a function from the cache.
* This is useful when a function is broken and needs to be skipped.
* @param name The name of the function to remove from the cache.
*/
private function purgeFunction(name:String):Void {
if (_cachedStaticFunctions != null) {
_cachedStaticFunctions.remove(name);
}
}
public function hget(name:String):Dynamic {
if(hasVar(name)) {
var v = getVar(name);
var getter = v.get;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.toString();
default: false;
}
var r:Dynamic = null;
if (getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
throw 'field $name cannot be accessed for reading';
if(__allowSetGet && getter == AGet){
if(hasFunction('get_$name'))
r = __callGetter(name);
else
throw 'Method get_$name required by property $name is missing';
}
else if (this.staticInterp.variables.exists(name))
r = this.staticInterp.variables.get(name);
else {
if(v.expr != null) {
r = this.staticInterp.expr(v.expr);
this.staticInterp.variables.set(name, r);
}
}
return r;
}
if(hasFunction(name)) {
// TODO: optimize this
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
return this.callFunction(name, args);
});
return fn;
}
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
}
public function hset(name:String, val:Dynamic):Dynamic {
if (hasVar(name)) {
var v = getVar(name);
var setter = v.set;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.toString();
default: false;
}
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || v.isFinal)
throw 'field $name cannot be accessed for writing';
if (__allowSetGet && setter == ASet) {
if (hasFunction('set_$name'))
return __callSetter(name, val);
else
throw 'Method set_$name required by property $name is missing';
}
else {
this.staticInterp.variables.set(name, val);
return val;
}
}
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
}
public function __callGetter(name:String):Dynamic {
__allowSetGet = false;
var r = callFunction('get_${name}');
__allowSetGet = true;
return r;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
__allowSetGet = false;
var r = callFunction('set_${name}', [val]);
__allowSetGet = true;
return r;
}
public function toString():String {
return name;
}
}
typedef CustomClassImport = {
var ?name:String;
var ?pkg:Array<String>;
var ?fullPath:String; // pkg.pkg.pkg.name
var ?as:Null<String>; // import pkg.Name as OtherName
}
@@ -0,0 +1,25 @@
package hscript.customclass.utils;
import hscript.utils.UnsafeReflect;
import haxe.Constraints.Function;
@:access(hscript.customclass.CustomClassDecl)
class FunctionUtils {
public static inline function callStaticFunction(name:String, classDecl:CustomClassDecl, interp:Interp, fn:Function, args:Array<Dynamic> = null) {
var r:Dynamic = null;
try {
if (fn == null)
interp.error(ECustom('${name} is not a function'));
r = UnsafeReflect.callMethodUnsafe(null, fn, args);
} catch (e:hscript.Expr.Error) {
// A script error occurred while executing the custom class function.
// Purge the function from the cache so it is not called again.
classDecl.purgeFunction(name);
interp.error(#if hscriptPos e.e #else e #end);
}
return r;
}
}
@@ -10,12 +10,12 @@ import haxe.macro.Compiler;
using StringTools; using StringTools;
class UsingHandler { class AbstractHandler {
public static function init() { public static function init() {
#if !display #if !display
if(Context.defined("display")) return; if(Context.defined("display")) return;
for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) { for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) {
Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.UsingHandler.build())'); Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.AbstractHandler.build())');
} }
#end #end
} }
+215 -67
View File
@@ -60,20 +60,19 @@ class ClassExtendMacro {
if(key == "away3d.tools.commands.SphereMaker") return fields; // Error: Unknown identifier if(key == "away3d.tools.commands.SphereMaker") return fields; // Error: Unknown identifier
if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier
if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined
if(fkey == "hscript.CustomClassHandler.CustomTemplateClass") return fields; // Error: Redefined
if(key == "sys.thread.EventLoop") return fields; // Error: cant override force inlined if(key == "sys.thread.EventLoop") return fields; // Error: cant override force inlined
if(Config.DISALLOW_CUSTOM_CLASSES.contains(cl.module) || Config.DISALLOW_CUSTOM_CLASSES.contains(fkey)) return fields; if(Config.DISALLOW_CUSTOM_CLASSES.contains(cl.module) || Config.DISALLOW_CUSTOM_CLASSES.contains(fkey)) return fields;
if(cl.module.contains("_")) return fields; // Weird issue, sorry if(cl.module.contains("_")) return fields; // Weird issue, sorry
var superFields = []; var superFields = [];
if(cl.superClass != null) { if(false && cl.superClass != null) {
var _superFields = cl.superClass.t.get().fields.get(); var _superFields = cl.superClass.t.get().fields.get();
_superFields = []; // Comment to enable super support, (broken) _superFields = []; // Comment to enable super support, (broken)
for(field in _superFields) {
if(!field.kind.match(FMethod(_))) // only catch methods
continue;
function convertField(field:ClassField) {
try { try {
var nfield = @:privateAccess TypeTools.toField(field); var nfield = FixedTypeTools.toSimpleField(field);
switch ([field.kind, field.type]) { switch ([field.kind, field.type]) {
case [FMethod(kind), TFun(args, ret)]: case [FMethod(kind), TFun(args, ret)]:
if(kind == MethInline) if(kind == MethInline)
@@ -86,9 +85,9 @@ class ClassExtendMacro {
switch(nfield.kind) { switch(nfield.kind) {
case FFun(fun): case FFun(fun):
if (fun.params != null && fun.params.length > 0) if (fun.params != null && fun.params.length > 0)
continue; return null;
fun.ret = Utils.fixStdTypes(fun.ret); //sfun.ret = Utils.fixStdTypes(fun.ret);
var metas = nfield.meta; var metas = nfield.meta;
var defaultValues:Map<String, Dynamic> = []; var defaultValues:Map<String, Dynamic> = [];
@@ -107,7 +106,7 @@ class ClassExtendMacro {
if(m.name == ":generic") if(m.name == ":generic")
isGeneric = true; isGeneric = true;
} }
if(isGeneric) continue; if(isGeneric) return null;
if(defaultEntry != null) if(defaultEntry != null)
metas.remove(defaultEntry); metas.remove(defaultEntry);
@@ -119,20 +118,80 @@ class ClassExtendMacro {
arg.opt = false; arg.opt = false;
} }
arg.type = Utils.fixStdTypes(arg.type); arg.type = null;//Utils.fixStdTypes(arg.type);
if(arg.opt) { //if(arg.opt) {
if(arg.type.getParameters()[0].name != "Null") // if(arg.type.getParameters()[0].name != "Null")
arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null<Dynamic>}; // arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null<Dynamic>};
} //}
} }
trace(nfield.name);
default: default:
} }
superFields.push(nfield); return nfield;
} catch(e) { } catch(e) {
trace(field.name, e);
return null;
} }
} }
var didPrint = false;
var fieldNames = [for(f in fields) f.name];
/*for(field in _superFields) {
if(fieldNames.contains(field.name))
continue;
if(!field.kind.match(FMethod(_))) // only catch methods
continue;
if(field.name.startsWith("get_")) {
var access = FixedTypeTools.getAccess(field);
if(access.contains(AInline) || access.contains(AFinal) || field.isFinal)
continue;
var name = field.name;
superFields.push({
name: field.name,
pos: field.pos,
kind: FFun({
ret: null,
params: [],
expr: macro {
return super.$name();
},
args: []
}),
access: access,
meta: field.meta.get(),
});
//var f = convertField(field);
//if(f != null)
// superFields.push(f);
if(field.name == "get_bgColor") {
if(!didPrint) {
trace(cl.name);
didPrint = true;
}
trace("> " + field.name + " : " + access, field);
}
}
}*/
// want to get this working
/*for(field in _superFields) {
if(fieldNames.contains(field.name))
continue;
if(!field.kind.match(FMethod(_))) // only catch methods
continue;
var f = convertField(field);
if(f != null)
superFields.push(f);
}*/
//superFields = []; //superFields = [];
} }
@@ -152,11 +211,20 @@ class ClassExtendMacro {
continue; continue;
if (f.name == "new") { if (f.name == "new") {
hasNew = true; hasNew = true;
switch (f.kind) {
case FFun(fn):
var constructor:Field = buildConstructor(fn.args);
shadowClass.fields.push(constructor);
definedFields.push(f.name);
default:
continue;
}
continue; continue;
} }
if (f.name.startsWith(FUNC_PREFIX)) if (f.name.startsWith(FUNC_PREFIX))
continue; continue;
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline)) if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline) || f.access.contains(AFinal))
continue; continue;
if(f.name == "hget" || f.name == "hset") continue; // sorry, no overwriting the hget and hset in custom classes, yet if(f.name == "hget" || f.name == "hset") continue; // sorry, no overwriting the hget and hset in custom classes, yet
@@ -186,7 +254,7 @@ class ClassExtendMacro {
if (returns) { if (returns) {
overrideExpr = macro { overrideExpr = macro {
var name:String = $v{name}; var name:String = $v{name};
/*
if (__custom__variables != null) { if (__custom__variables != null) {
if(__custom__variables.exists(name)) { if(__custom__variables.exists(name)) {
var v:Dynamic = null; var v:Dynamic = null;
@@ -195,12 +263,17 @@ class ClassExtendMacro {
} }
} }
} }
*/
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
return __customClass.callFunction(name, [$a{arguments}]);
}
return super.$name($a{arguments}); return super.$name($a{arguments});
}; };
} else { } else {
overrideExpr = macro { overrideExpr = macro {
var name:String = $v{name}; var name:String = $v{name};
/*
if (__custom__variables != null) { if (__custom__variables != null) {
if(__custom__variables.exists(name)) { if(__custom__variables.exists(name)) {
var v:Dynamic = null; var v:Dynamic = null;
@@ -210,6 +283,12 @@ class ClassExtendMacro {
} }
} }
} }
*/
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
__customClass.callFunction(name, [$a{arguments}]);
return;
}
super.$name($a{arguments}); super.$name($a{arguments});
}; };
} }
@@ -280,45 +359,64 @@ class ClassExtendMacro {
Utils.setupMetas(shadowClass, imports); Utils.setupMetas(shadowClass, imports);
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect"); Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
// Adding hscript getters and setters shadowClass.fields.push({
name: "__cachedFields",
pos: Context.currentPos(),
kind: FVar(macro: Map<String, Dynamic>),
access: [APublic, AStatic]
});
// TODO: Remove // Adding hscript getters and setters
/*
shadowClass.fields.push({ shadowClass.fields.push({
name: "__interp", name: "__interp",
pos: Context.currentPos(), pos: Context.currentPos(),
kind: FVar(TPath({ kind: FVar(macro: hscript.Interp),
pack: ['hscript'],
name: 'Interp'
})),
access: [APublic] access: [APublic]
}); });
*/
shadowClass.fields.push({
name: "__customClass",
pos: Context.currentPos(),
kind: FVar(macro: hscript.customclass.CustomClass),
access: [APublic]
});
/*
shadowClass.fields.push({ shadowClass.fields.push({
name: "__custom__variables", name: "__custom__variables",
pos: Context.currentPos(), pos: Context.currentPos(),
kind: FVar(TPath({ kind: FVar(macro: Map<String, Dynamic>),
pack: [],
name: 'Map',
params: [TPType(TPath({name: "String", pack: []})), TPType(TPath({name: "Dynamic", pack: []}))]
})),
access: [APublic] access: [APublic]
}); });
shadowClass.fields.push({ shadowClass.fields.push({
name: "__allowSetGet", name: "__allowSetGet",
pos: Context.currentPos(), pos: Context.currentPos(),
kind: FVar(TPath({ kind: FVar(macro: Bool, macro true),
pack: [],
name: 'Bool',
}), macro true),
access: [APublic] access: [APublic]
}); });
*/
shadowClass.fields.push({
name: "__real_fields",
pos: Context.currentPos(),
kind: FVar(macro: Array<String>),
access: [APublic]
});
/*
shadowClass.fields.push({
name: "__class__fields",
pos: Context.currentPos(),
kind: FVar(macro: Array<String>),
access: [APublic]
});
*/
// Unneccessary since the get/set call is already handled.
/*
shadowClass.fields.push({ shadowClass.fields.push({
name: "__callGetter", name: "__callGetter",
pos: Context.currentPos(), pos: Context.currentPos(),
kind: FFun({ kind: FFun({
ret: TPath({name: 'Dynamic', pack: []}), ret: macro: Dynamic,
params: [], params: [],
expr: macro { expr: macro {
__allowSetGet = false; __allowSetGet = false;
@@ -331,7 +429,7 @@ class ClassExtendMacro {
name: "name", name: "name",
opt: false, opt: false,
meta: [], meta: [],
type: TPath({name: "String", pack: []}) type: macro: String
} }
] ]
}), }),
@@ -342,7 +440,7 @@ class ClassExtendMacro {
name: "__callSetter", name: "__callSetter",
pos: Context.currentPos(), pos: Context.currentPos(),
kind: FFun({ kind: FFun({
ret: TPath({name: 'Dynamic', pack: []}), ret: macro: Dynamic,
params: [], params: [],
expr: macro { expr: macro {
__allowSetGet = false; __allowSetGet = false;
@@ -355,19 +453,19 @@ class ClassExtendMacro {
name: "name", name: "name",
opt: false, opt: false,
meta: [], meta: [],
type: TPath({name: "String", pack: []}) type: macro: String
}, },
{ {
name: "val", name: "val",
opt: false, opt: false,
meta: [], meta: [],
type: TPath({name: "Dynamic", pack: []}) type: macro: Dynamic
} }
] ]
}), }),
access: [APublic] access: [APublic]
}); });
*/
// Todo: make it possible to override // Todo: make it possible to override
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") { if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
Context.defineModule(cl.module, [shadowClass], imports); Context.defineModule(cl.module, [shadowClass], imports);
@@ -410,42 +508,66 @@ class ClassExtendMacro {
var hgetField = if(hasHgetInSuper) { var hgetField = if(hasHgetInSuper) {
macro { macro {
if(__allowSetGet && __custom__variables.exists("get_" + name)) /*
return __callGetter(name); if(__custom__variables != null) {
if (__custom__variables.exists(name)) if(__allowSetGet && __custom__variables.exists("get_" + name))
return __custom__variables.get(name); return __callGetter(name);
if (__custom__variables.exists(name))
return __custom__variables.get(name);
}
*/
return super.hget(name); return super.hget(name);
} }
} else { } else {
macro { macro {
if(__allowSetGet && __custom__variables.exists("get_" + name)) /*
return __callGetter(name); if(__custom__variables != null) {
if (__custom__variables.exists(name)) if(__allowSetGet && __custom__variables.exists("get_" + name))
return __custom__variables.get(name); return __callGetter(name);
if (__custom__variables.exists(name))
return __custom__variables.get(name);
}
*/
return UnsafeReflect.getProperty(this, name); return UnsafeReflect.getProperty(this, name);
} }
} }
var hsetField = if(hasHsetInSuper) { var hsetField = if(hasHsetInSuper) {
macro { macro {
if(__allowSetGet && __custom__variables.exists("set_" + name)) /*
return __callSetter(name, val); if(__custom__variables != null) {
if (__custom__variables.exists(name)) { if(__allowSetGet && __custom__variables.exists("set_" + name))
__custom__variables.set(name, val); return __callSetter(name, val);
return val; if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
return val;
}
} }
return super.hset(this, name); */
if(__real_fields != null && __real_fields.contains(name)) {
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
return super.hset(name, val);
} }
} else { } else {
macro { macro {
if(__allowSetGet && __custom__variables.exists("set_" + name)) /*
return __callSetter(name, val); if(__custom__variables != null) {
if (__custom__variables.exists(name)) { if(__allowSetGet && __custom__variables.exists("set_" + name))
__custom__variables.set(name, val); return __callSetter(name, val);
return val; if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
return val;
}
} }
UnsafeReflect.setProperty(this, name, val); */
return UnsafeReflect.field(this, name); if(__real_fields != null && __real_fields.contains(name)) {
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
//if(__custom__variables != null) __custom__variables.set(name, val);
return val;
} }
} }
@@ -458,7 +580,7 @@ class ClassExtendMacro {
pos: Context.currentPos(), pos: Context.currentPos(),
access: hasHsetInSuper ? [AOverride, APublic] : [APublic], access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
kind: FFun({ kind: FFun({
ret: TPath({name: 'Dynamic', pack: []}), ret: macro: Dynamic,
params: [], params: [],
expr: hsetField, expr: hsetField,
args: [ args: [
@@ -466,13 +588,13 @@ class ClassExtendMacro {
name: "name", name: "name",
opt: false, opt: false,
meta: [], meta: [],
type: TPath({name: "String", pack: []}) type: macro: String
}, },
{ {
name: "val", name: "val",
opt: false, opt: false,
meta: [], meta: [],
type: TPath({name: "Dynamic", pack: []}) type: macro: Dynamic
} }
] ]
}) })
@@ -483,7 +605,7 @@ class ClassExtendMacro {
pos: Context.currentPos(), pos: Context.currentPos(),
access: hasHgetInSuper ? [AOverride, APublic] : [APublic], access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
kind: FFun({ kind: FFun({
ret: TPath({name: 'Dynamic', pack: []}), ret: macro: Dynamic,
params: [], params: [],
expr: hgetField, expr: hgetField,
args: [ args: [
@@ -491,7 +613,7 @@ class ClassExtendMacro {
name: "name", name: "name",
opt: false, opt: false,
meta: [], meta: [],
type: TPath({name: "String", pack: []}) type: macro: String
} }
] ]
}) })
@@ -509,6 +631,32 @@ class ClassExtendMacro {
return fields; return fields;
} }
static function buildConstructor(constArgs:Array<FunctionArg>):Field {
var superCallArgs:Array<Expr> = [for (arg in constArgs) macro $i{arg.name}];
return {
name: 'new',
access: [APublic],
pos: Context.currentPos(),
kind: FFun({
args: constArgs,
expr: macro
{
// Call the super constructor with appropriate args
super($a{superCallArgs});
if(__cachedFields != null) {
for(k => v in __cachedFields) {
Reflect.setProperty(this, k, v);
trace(k);
}
__cachedFields = null;
}
},
}),
};
}
} }
#else #else
class ClassExtendMacro { class ClassExtendMacro {
+529
View File
@@ -0,0 +1,529 @@
package hscript.macros;
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
import Type as StdType;
using Lambda;
/**
This class provides some utility methods to work with types. It is
best used through 'using haxe.macro.TypeTools' syntax and then provides
additional methods on haxe.macro.Type instances.
**/
#if hl
@:hlNative("macro")
#end
class FixedTypeTools {
static function nullable(complexType:ComplexType):ComplexType
return macro:Null<$complexType>;
public static function toField(cf:ClassField):Field {
function varAccessToString(va:VarAccess, getOrSet:String):String {
return {
switch (va) {
case AccNormal | AccCtor: "default";
case AccNo: "null";
case AccNever: "never";
case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString";
case AccCall: getOrSet;
case AccInline: "default";
case AccRequire(_, _): "default";
}
}
}
var access = cf.isPublic ? [APublic] : [APrivate];
if (cf.meta.has(":final")) {
access.push(AFinal);
}
if (cf.params.length != 0)
throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params";
return {
name: cf.name,
doc: cf.doc,
access: access,
kind: switch ([cf.kind, cf.type]) {
case [FVar(read, write), ret]:
FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null);
case [FMethod(_), TFun(args, ret)]:
Sys.println("Converting " + cf.name);
FFun({
args: [
for (a in args)
{
name: a.name,
opt: a.opt,
type: toComplexType(a.t),
}
],
ret: toComplexType(ret),
expr: null,
});
case [FMethod(_), TLazy(f)]:
Sys.println("Converting lazy " + cf.name + " in " + cf.pos);
switch(f()) {
case TFun(args, ret):
FFun({
args: [
for (a in args)
{
name: a.name,
opt: a.opt,
type: toComplexType(a.t),
}
],
ret: toComplexType(ret),
expr: null,
});
default:
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
}
default:
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
},
pos: cf.pos,
meta: cf.meta.get(),
}
}
public static function getAccess(cf:ClassField):Array<Access> {
var access = cf.isPublic ? [APublic] : [APrivate];
if (cf.meta.has(":final") || cf.isFinal) {
access.push(AFinal);
}
switch ([cf.kind, cf.type]) {
case [FMethod(kind), TFun(_, _)] | [FMethod(kind), TLazy(_)]:
if(kind == MethInline)
access.push(AInline);
if(kind == MethDynamic)
access.push(ADynamic);
default:
}
return access;
}
public static function toSimpleField(cf:ClassField):Field {
function varAccessToString(va:VarAccess, getOrSet:String):String {
return {
switch (va) {
case AccNormal | AccCtor: "default";
case AccNo: "null";
case AccNever: "never";
case AccResolve: throw "Invalid " + StdType.enumConstructor(cf.type) + " in varAccessToString";
case AccCall: getOrSet;
case AccInline: "default";
case AccRequire(_, _): "default";
}
}
}
var access = cf.isPublic ? [APublic] : [APrivate];
if (cf.meta.has(":final")) {
access.push(AFinal);
}
if (cf.params.length != 0)
throw "Invalid " + StdType.enumConstructor(cf.type) + " has more than 0 params";
return {
name: cf.name,
doc: cf.doc,
access: access,
kind: switch ([cf.kind, cf.type]) {
case [FVar(read, write), ret]:
FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null);
case [FMethod(_), TFun(args, ret)]:
Sys.println("Converting " + cf.name);
FFun({
args: [
for (a in args)
{
name: a.name,
opt: a.opt,
type: null,//toComplexType(a.t),
}
],
ret: null,//toComplexType(ret),
expr: null,
});
case [FMethod(_), TLazy(f)]:
Sys.println("Converting lazy " + cf.name + " in " + cf.pos);
switch(f()) {
case TFun(args, ret):
FFun({
args: [
for (a in args)
{
name: a.name,
opt: a.opt,
type: null,//toComplexType(a.t),
}
],
ret: null,//toComplexType(ret),
expr: null,
});
default:
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
}
default:
throw "Invalid " + StdType.enumConstructor(cf.type) + " when converting to Field , " + cf.kind + ", " + cf.type;
},
pos: cf.pos,
meta: cf.meta.get(),
}
}
/**
Returns a syntax-level type corresponding to Type `t`.
This function is mostly inverse to `ComplexTypeTools.toType`, but may
lose some information on types that do not have a corresponding syntax
version, such as monomorphs. In these cases, the result is null.
If `t` is null, an internal exception is thrown.
**/
public static function toComplexType(type:Null<Type>):Null<ComplexType>
return {
#if macro
Context.toComplexType(type);
#else
switch (type) {
case null:
null;
case TMono(_.get() => t):
t == null ? null : toComplexType(t);
case TEnum(_.get() => baseType, params):
TPath(toTypePath(baseType, params));
case TInst(_.get() => classType, params):
switch (classType.kind) {
case KTypeParameter(_):
TPath({
name: classType.name,
pack: [],
});
default:
TPath(toTypePath(classType, params));
}
case TType(_.get() => baseType, params):
TPath(toTypePath(baseType, params));
case TFun(args, ret):
TFunction([for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t)], toComplexType(ret));
case TAnonymous(_.get() => {fields: fields}):
TAnonymous([for (cf in fields) toField(cf)]);
case TDynamic(t):
if (t == null) {
macro:Dynamic;
} else {
var ct = toComplexType(t);
macro:Dynamic<$ct>;
}
case TLazy(f):
toComplexType(f());
case TAbstract(_.get() => baseType, params):
TPath(toTypePath(baseType, params));
default:
throw "Invalid type";
}
#end
}
static function toTypeParam(type:Type):TypeParam
return {
switch (type) {
case TInst(_.get() => {kind: KExpr(e)}, _): TPExpr(e);
case _: TPType(toComplexType(type));
}
}
static function toTypePath(baseType:BaseType, params:Array<Type>):TypePath
return {
var module = baseType.module;
{
pack: baseType.pack,
name: module.substring(module.lastIndexOf(".") + 1),
sub: baseType.name,
params: [for (t in params) toTypeParam(t)],
}
}
#if macro
/**
Follows all typedefs of `t` to reach the actual type.
If `once` is true, this function does not call itself recursively,
otherwise it does. This can be useful in cases where intermediate
typedefs might be of interest.
Affected types are monomorphs `TMono` and typedefs `TType(t,pl)`.
If `t` is null, an internal exception is thrown.
Usage example with monomorphs:
var t = Context.typeof(macro null); // TMono(<mono>)
var ts = Context.typeof(macro "foo"); //TInst(String,[])
Context.unify(t, ts);
trace(t); // TMono(<mono>)
trace(t.follow()); //TInst(String,[])
Usage example with typedefs:
var t = Context.typeof(macro ("foo" :MyString)); // typedef MyString = String
trace(t); // TType(MyString,[])
trace(t.follow()); //TInst(String,[])
**/
static public inline function follow(t:Type, ?once:Bool):Type
return Context.follow(t, once);
/**
Like `follow`, follows all typedefs of `t` to reach the actual type.
Will however follow also abstracts to their underlying implementation,
if they are not a @:coreType abstract
If `t` is null, an internal exception is thrown.
Usage example:
var t = Context.typeof(macro new Map<String, String>());
trace(t); // TAbstract(Map,[TInst(String,[]),TInst(String,[])])
trace(t.followWithAbstracts()); // TInst(haxe.ds.StringMap, [TInst(String,[])])
**/
static public inline function followWithAbstracts(t:Type, once:Bool = false):Type
return Context.followWithAbstracts(t, once);
/**
Returns true if `t1` and `t2` unify, false otherwise.
**/
static public inline function unify(t1:Type, t2:Type):Bool
return Context.unify(t1, t2);
/**
Tries to extract the class instance stored inside `t`.
If `t` is a class instance `TInst(c,pl)`, c is returned.
If `t` is of a different type, an exception of type String is thrown.
If `t` is null, the result is null.
**/
static public function getClass(t:Type)
return t == null ? null : switch (follow(t)) {
case TInst(c, _): c.get();
case _: throw "Class instance expected";
}
/**
Tries to extract the enum instance stored inside `t`.
If `t` is an enum instance `TEnum(e,pl)`, e is returned.
If `t` is of a different type, an exception of type String is thrown.
If `t` is null, the result is null.
**/
static public function getEnum(t:Type)
return t == null ? null : switch (follow(t)) {
case TEnum(e, _): e.get();
case _: throw "Enum instance expected";
}
/**
Applies the type parameters `typeParameters` to type `t` with the given
types `concreteTypes`.
This function replaces occurrences of type parameters in `t` if they are
part of `typeParameters`. The array index of such a type parameter is
then used to lookup the concrete type in `concreteTypes`.
If `typeParameters.length` is not equal to `concreteTypes.length`, an
exception of type `String` is thrown.
If `typeParameters.length` is 0, `t` is returned unchanged.
If either argument is `null`, the result is unspecified.
**/
static public function applyTypeParameters(t:Type, typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>):Type {
if (typeParameters.length != concreteTypes.length)
throw 'Incompatible arguments: ${typeParameters.length} type parameters and ${concreteTypes.length} concrete types';
else if (typeParameters.length == 0)
return t;
#if (neko || eval)
return @:privateAccess Context.load("apply_params", 3)(typeParameters, concreteTypes, t);
#else
return applyParams(typeParameters, concreteTypes, t);
#end
}
#if !neko
private static function applyParams(typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>, t:Type):Type {
return null;
}
#end
/**
Transforms `t` by calling `f` on each of its subtypes.
If `t` is a compound type, `f` is called on each of its components.
Otherwise `t` is returned unchanged.
The following types are considered compound:
- TInst, TEnum, TType and TAbstract with type parameters
- TFun
- TAnonymous
If `t` or `f` are null, the result is unspecified.
**/
static public function map(t:Type, f:Type->Type):Type {
return switch (t) {
case TMono(tm):
switch (tm.get()) {
case null: t;
case var t: f(t);
}
case TEnum(_, []) | TInst(_, []) | TType(_, []):
t;
case TEnum(en, tl):
TEnum(en, tl.map(f));
case TInst(cl, tl):
TInst(cl, tl.map(f));
case TType(t2, tl):
TType(t2, tl.map(f));
case TAbstract(a, tl):
TAbstract(a, tl.map(f));
case TFun(args, ret):
TFun(args.map(function(arg) return {
name: arg.name,
opt: arg.opt,
t: f(arg.t)
}), f(ret));
case TAnonymous(an):
TAnonymous(@:privateAccess Context.load("map_anon_ref", 2)(an, f));
case TDynamic(t2):
t == t2 ? t : TDynamic(f(t2));
case TLazy(ft):
var ft = ft();
var ft2 = f(ft);
ft == ft2 ? t : ft2;
}
}
/**
Calls function `f` on each component of type `t`.
If `t` is not a compound type, this operation has no effect.
The following types are considered compound:
- TInst, TEnum, TType and TAbstract with type parameters
- TFun
- TAnonymous
If `t` or `f` are null, the result is unspecified.
**/
static public function iter(t:Type, f:Type->Void):Void {
switch (t) {
case TMono(tm):
var t = tm.get();
if (t != null)
f(t);
case TEnum(_, tl) | TInst(_, tl) | TType(_, tl) | TAbstract(_, tl):
for (t in tl)
f(t);
case TDynamic(t2):
if (t != t2)
f(t2);
case TLazy(ft):
f(ft());
case TAnonymous(an):
for (field in an.get().fields)
f(field.type);
case TFun(args, ret):
for (arg in args)
f(arg.t);
f(ret);
}
}
/**
Converts type `t` to a human-readable String representation.
**/
static public function toString(t:Type):String {
#if (neko || eval)
return @:privateAccess Context.load("s_type", 1)(t);
#else
return null;
#end
}
/**
Changes the name of the variable in the typed expression.
**/
static public function setVarName(t:TVar, name:String) {
@:privateAccess Context.load("set_var_name", 2)(t, name);
}
/**
Converts type `t` to `ModuleType`.
**/
static public function toModuleType(t:Type):ModuleType {
#if (neko || eval)
return @:privateAccess Context.load("type_to_module_type", 1)(t);
#else
return null;
#end
}
/**
Creates a type from the `ModuleType` argument.
**/
static public function fromModuleType(mt:ModuleType):Type {
#if (neko || eval)
return @:privateAccess Context.load("module_type_to_type", 1)(mt);
#else
return null;
#end
}
#end
/**
Resolves the field named `name` on class `c`.
If `isStatic` is true, the classes' static fields are checked. Otherwise
the classes' member fields are checked.
If the field is found, it is returned. Otherwise if `c` has a super
class, `findField` recursively checks that super class. Otherwise null
is returned.
If any argument is null, the result is unspecified.
**/
static public function findField(c:ClassType, name:String, isStatic:Bool = false):Null<ClassField> {
var field = (isStatic ? c.statics : c.fields).get().find(function(field) return field.name == name);
return if (field != null) field; else if (c.superClass != null) findField(c.superClass.t.get(), name, isStatic); else null;
}
}
+173
View File
@@ -0,0 +1,173 @@
package hscript.macros;
#if macro
import Type.ValueType;
import haxe.macro.Compiler;
import haxe.macro.Context;
import haxe.macro.Expr;
using Lambda;
using StringTools;
using haxe.macro.Tools;
/**
* Macro used for the `using Class;` keyword
*
* you can make classes be able to be used specifing the classes/packages on Config.hx!
* or implementing the interface `hscript.utils.UsingClass`
* ```haxe
* public static final ALLOWED_USING = ["my.pack.VeryNiceTools"];
* ```
*
* Usage:
*
* ```haxe
* package my.pack;
* // @:usableEntry() // optional
* // @:usableEntry(forceAny) // optional // forces the class to be called with any type
* // @:usableEntry(onlyBasic) // optional // only basic types will be allowed
* // @:usableEntry(onlyBasic, forceAny) // optional // only basic types will be allowed, and the class will be called with any type
* class VeryNiceTools implements hscript.utils.UsingClass {}
* ```
*
* @author NeeEoo
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/macro/UsingMacro.macro.hx
**/
class UsingMacro {
public static inline final USING_PREFIX = "_HX_USING__";
public static var unallowedMetas:Array<String> = [":noUsing", ":noUse"];
public static function init() {
#if !display
if(Context.defined("display")) return;
for(apply in Config.ALLOWED_USING) {
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.UsingMacro.build())");
}
#end
}
public static function build() {
var cls: haxe.macro.Type.ClassType = Context.getLocalClass().get();
var fields = Context.getBuildFields();
var key = cls.module;
var fkey = cls.module + "." + cls.name;
var packName = (cls.pack.length > 0 ? cls.pack.join(".") + "." : "") + cls.name;
if(cls.meta.get().find(function(m) return m.name == ':usingProcessed') != null) return fields;
if (Config.DISALLOW_USING.contains(key) || Config.DISALLOW_USING.contains(fkey) || Config.DISALLOW_USING.contains(packName))
return fields;
var entryField = cls.meta.get().find(function(m) return m.name == ':usableEntry');
var hasParams = entryField != null && entryField.params != null;
var forceAny = false;
var onlyBasic = false;
if (hasParams) {
for (i in 0...entryField.params.length) {
if (entryField.params[i].expr.match(EConst(CIdent("onlyBasic"))))
onlyBasic = true;
if (entryField.params[i].expr.match(EConst(CIdent("forceAny"))))
forceAny = true;
}
}
var data: Array<Array<String>> = [];
for (field in fields) {
// functions marked with @:noUsing won't be able to be used by variables
// also if you want it to be usable in source, but not in the script, use @:noUse
for(m in field.meta)
if(unallowedMetas.contains(m.name))
continue;
// It's called "static extensions" for some reason
if(!field.access.contains(AStatic))
continue;
switch (field.kind) {
default:
case FFun(f):
if (f.args.length == 0)
continue;
var arg = f.args[0];
if (arg.type == null)
continue;
var type = arg.type;
var valueType: String = switch (type) {
case TPath({name: "Int", pack: []}):
"TInt";
case TPath({name: "Float", pack: []}):
"TFloat";
case TPath({name: "Single", pack: []}):
"TFloat";
case TPath({name: "String", pack: []}):
"TClass(String)";
case TPath({name: "Bool", pack: []}):
"TBool";
case TPath({name: "Array", pack: []}):
"TClass(Array)";
case TPath({name: "Map", pack: []}):
"TClass(haxe.Constraints.IMap)";
case TPath({name: "Dynamic", pack: []}):
null;
case TPath({name: "Class", pack: []}):
"TClass(null)"; // this feels wrong
case TPath({name: "Enum", pack: []}):
"TEnum(null)";
// case TPath({name: "Void", pack: []}): "ValueType.TVoid";
default:
null; // null acts as a wildcard
}
// MIGHT CRASH COMPILATION? YEAH, IT CRASH ON TYPE PARAMETERS
if (!onlyBasic && valueType == null) {
var rtype:haxe.macro.Type = null;
try {rtype = type.toType();} catch(e) {}
if(rtype != null) {
switch (rtype) {
case TInst(t, []):
valueType = "TClass(" + t.toString() + ")";
default:
}
}
}
if (forceAny) {
valueType = null;
}
data.push([field.name, valueType]);
}
}
if(data.length == 0)
return fields;
fields.push({
name: '$USING_PREFIX${packName.replace(".", "_")}',
access: [APrivate, AStatic],
kind: FVar(macro : Map<String, Type.ValueType>, {
var arr: Array<Expr> = [];
for (i in data)
if (i[1] != null)
arr.push(macro $v{i[0]} => ${Context.parse("Type.ValueType." + i[1], Context.currentPos())});
else
arr.push(macro $v{i[0]} => null);
macro $a{arr};
}),
pos: cls.pos,
});
//var printer = new haxe.macro.Printer();
//trace(printer.printField(fields[fields.length - 1]));
cls.meta.add(':usingProcessed', [], cls.pos);
return fields;
}
}
#end
+364
View File
@@ -0,0 +1,364 @@
package hscript.proxy;
import hscript.customclass.CustomClass;
import hscript.customclass.CustomClassDecl;
import hscript.Tools.HScriptEnum;
import hscript.Tools.EnumValue as HScriptEnumValue;
abstract OneOfTwo<T1, T2>(Dynamic) from T1 from T2 to T1 to T2 {}
@:allow(hscript.customclass.CustomClass)
@:allow(hscript.customclass.CustomClassDecl)
class ProxyType {
/**
Returns the class of `o`, if `o` is a class instance.
If `o` is null or of a different type, null is returned.
In general, type parameter information cannot be obtained at runtime.
**/
inline static function getClass<T>(o:OneOfTwo<T, CustomClass>):Null<Dynamic> {
if(o is CustomClass)
@:privateAccess return cast(o, CustomClass).__class;
return Type.getClass(cast o);
}
/**
Returns the enum of enum instance `o`.
An enum instance is the result of using an enum constructor. Given an
`enum Color { Red; }`, `getEnum(Red)` returns `Enum<Color>`.
If `o` is null, null is returned.
In general, type parameter information cannot be obtained at runtime.
**/
inline static function getEnum(o:OneOfTwo<EnumValue, HScriptEnumValue>):Dynamic {
// TODO: SUPPORT HSCRIPT ENUMS
var isScripted:Bool = Std.isOfType(o, HScriptEnumValue);
if(isScripted) {
return cast(o, HScriptEnumValue).enumParent;
}
return Type.getEnum(cast o);
}
/**
Returns the super-class of class `c`.
If `c` has no super class, null is returned.
If `c` is null, the result is unspecified.
In general, type parameter information cannot be obtained at runtime.
**/
inline static function getSuperClass(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Dynamic {
if(c is CustomClassDecl) {
return cast(c, CustomClassDecl).superClassDecl;
}
return Type.getSuperClass(cast c);
}
/**
Returns the name of class `c`, including its path.
If `c` is inside a package, the package structure is returned dot-
separated, with another dot separating the class name:
`pack1.pack2.(...).packN.ClassName`
If `c` is a sub-type of a Haxe module, that module is not part of the
package structure.
If `c` has no package, the class name is returned.
If `c` is null, the result is unspecified.
The class name does not include any type parameters.
**/
inline static function getClassName(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):String {
if(c is CustomClassDecl) {
var cls:CustomClassDecl = cast c;
var name = cls.classDecl.name;
var pkg = cls.pkg != null ? '${cls.pkg.join(".")}.' : "";
return '$pkg$name';
}
return Type.getClassName(cast c);
}
/**
Returns the name of enum `e`, including its path.
If `e` is inside a package, the package structure is returned dot-
separated, with another dot separating the enum name:
`pack1.pack2.(...).packN.EnumName`
If `e` is a sub-type of a Haxe module, that module is not part of the
package structure.
If `e` has no package, the enum name is returned.
If `e` is null, the result is unspecified.
The enum name does not include any type parameters.
**/
inline static function getEnumName(e:Enum<Dynamic>):String {
var isScripted:Bool = Std.isOfType(e, HScriptEnumValue);
if (isScripted)
return cast(e, HScriptEnumValue).getEnumName();
return Type.getEnumName(e);
}
/**
Resolves a class by name.
If `name` is the path of an existing class, that class is returned.
Otherwise null is returned.
If `name` is null or the path to a different type, the result is
unspecified.
The class name must not include any type parameters.
**/
inline static function resolveClass(name:String):Dynamic {
if(Interp.customClassExist(name)) {
return Interp.getCustomClass(name);
}
return Type.resolveClass(name);
}
/**
Resolves an enum by name.
If `name` is the path of an existing enum, that enum is returned.
Otherwise null is returned.
If `name` is null the result is unspecified.
If `name` is the path to a different type, null is returned.
The enum name must not include any type parameters.
**/
inline static function resolveEnum(name:String):Enum<Dynamic> {
return Type.resolveEnum(name);
}
/**
Creates an instance of class `cl`, using `args` as arguments to the
class constructor.
This function guarantees that the class constructor is called.
Default values of constructors arguments are not guaranteed to be
taken into account.
If `cl` or `args` are null, or if the number of elements in `args` does
not match the expected number of constructor arguments, or if any
argument has an invalid type, or if `cl` has no own constructor, the
result is unspecified.
In particular, default values of constructor arguments are not
guaranteed to be taken into account.
**/
inline static function createInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>, args:Array<Dynamic>):T {
// TODO: somehow pass the ogInterp argument without breaking the original functionality
if(cl is CustomClassDecl) {
return cast new CustomClass(cast cl, args);
}
return Type.createInstance(cast cl, args);
}
/**
Creates an instance of class `cl`.
This function guarantees that the class constructor is not called.
If `cl` is null, the result is unspecified.
**/
inline static function createEmptyInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>):T {
// TODO: same as above
if(cl is CustomClassDecl)
return cast new CustomClass(cast cl, [], null, null, false);
return Type.createEmptyInstance(cast cl);
}
/**
Creates an instance of enum `e` by calling its constructor `constr` with
arguments `params`.
If `e` or `constr` is null, or if enum `e` has no constructor named
`constr`, or if the number of elements in `params` does not match the
expected number of constructor arguments, or if any argument has an
invalid type, the result is unspecified.
**/
inline static function createEnum<T>(e:OneOfTwo<Enum<T>, HScriptEnum>, constr:String, ?params:Array<Dynamic>):T {
// TODO: SUPPORT HSCRIPT ENUMS
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
if(isScripted) {
var scriptEnum:HScriptEnum = cast e;
var enumToCreate = scriptEnum.getEnumValue(constr);
if(Reflect.isFunction(enumToCreate)) {
return Reflect.callMethod(null, enumToCreate, params);
}
return enumToCreate;
}
return Type.createEnum(cast e, constr, params);
}
/**
Creates an instance of enum `e` by calling its constructor number
`index` with arguments `params`.
The constructor indices are preserved from Haxe syntax, so the first
declared is index 0, the next index 1 etc.
If `e` or `constr` is null, or if enum `e` has no constructor named
`constr`, or if the number of elements in `params` does not match the
expected number of constructor arguments, or if any argument has an
invalid type, the result is unspecified.
**/
inline static function createEnumIndex<T>(e:Enum<T>, index:Int, ?params:Array<Dynamic>):T {
// TODO: SUPPORT HSCRIPT ENUMS
return Type.createEnumIndex(e, index, params);
}
/**
Returns a list of the instance fields of class `c`, including
inherited fields.
This only includes fields which are known at compile-time. In
particular, using `getInstanceFields(getClass(obj))` will not include
any fields which were added to `obj` at runtime.
The order of the fields in the returned Array is unspecified.
If `c` is null, the result is unspecified.
**/
inline static function getInstanceFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
if(c is CustomClassDecl) {
var cls:CustomClassDecl = cast c;
return [for(f in Tools.getClassDeclFields(cls.classDecl)) f.name];
}
return Type.getInstanceFields(cast c);
}
/**
Returns a list of static fields of class `c`.
This does not include static fields of parent classes.
The order of the fields in the returned Array is unspecified.
If `c` is null, the result is unspecified.
**/
inline static function getClassFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
if(c is CustomClassDecl) {
var cls:CustomClassDecl = cast c;
return [for(f in Tools.getClassDeclFields(cls.classDecl, true)) f.name];
}
return Type.getClassFields(cast c);
}
/**
Returns a list of the names of all constructors of enum `e`.
The order of the constructor names in the returned Array is preserved
from the original syntax.
If `e` is null, the result is unspecified.
**/
inline static function getEnumConstructs(e:OneOfTwo<Enum<Dynamic>, HScriptEnum>):Array<String> {
// TODO: SUPPORT HSCRIPT ENUMS
return Type.getEnumConstructs(cast e);
}
/**
Returns the runtime type of value `v`.
The result corresponds to the type `v` has at runtime, which may vary
per platform. Assumptions regarding this should be minimized to avoid
surprises.
**/
inline static function typeof(v:Dynamic):Type.ValueType {
return Type.typeof(v);
}
/**
Recursively compares two enum instances `a` and `b` by value.
Unlike `a == b`, this function performs a deep equality check on the
arguments of the constructors, if exists.
If `a` or `b` are null, the result is unspecified.
**/
inline static function enumEq(a:Dynamic, b:Dynamic):Bool {
var isScripted:Bool = Std.isOfType(a, HScriptEnumValue) && Std.isOfType(b, HScriptEnumValue);
if (isScripted)
return cast(a, HScriptEnumValue).compare(cast(b, HScriptEnumValue));
return Type.enumEq(a, b);
}
/**
Returns the constructor name of enum instance `e`.
The result String does not contain any constructor arguments.
If `e` is null, the result is unspecified.
**/
inline static function enumConstructor(e:Dynamic):String {
if (Std.isOfType(e, HScriptEnumValue))
return cast(e, HScriptEnumValue).name;
return Type.enumConstructor(e);
}
/**
Returns a list of the constructor arguments of enum instance `e`.
If `e` has no arguments, the result is [].
Otherwise the result are the values that were used as arguments to `e`,
in the order of their declaration.
If `e` is null, the result is unspecified.
**/
inline static function enumParameters(e:Dynamic):Array<Dynamic> {
if (Std.isOfType(e, HScriptEnumValue))
return cast(e, HScriptEnumValue).args;
return Type.enumParameters(e);
}
/**
Returns the index of enum instance `e`.
This corresponds to the original syntactic position of `e`. The index of
the first declared constructor is 0, the next one is 1 etc.
If `e` is null, the result is unspecified.
**/
inline static function enumIndex(e:Dynamic):Int {
if (Std.isOfType(e, HScriptEnumValue))
return cast(e, HScriptEnumValue).index;
return Type.enumIndex(e);
}
/**
Returns a list of all constructors of enum `e` that require no
arguments.
This may return the empty Array `[]` if all constructors of `e` require
arguments.
Otherwise an instance of `e` constructed through each of its non-
argument constructors is returned, in the order of the constructor
declaration.
If `e` is null, the result is unspecified.
**/
inline static function allEnums<T>(e:OneOfTwo<Enum<T>, HScriptEnum>):Array<T> {
// TODO: SUPPORT HSCRIPT ENUMS
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
if(isScripted) {
var hEnum:HScriptEnum = cast e;
return [for(en in Reflect.fields(hEnum.enumValues)) Reflect.field(hEnum, en)];
}
return Type.allEnums(cast e);
}
}
+2 -2
View File
@@ -52,7 +52,7 @@ class UnsafeReflect {
o.__SetField(field, value, untyped __cpp__("::hx::paccAlways")); o.__SetField(field, value, untyped __cpp__("::hx::paccAlways"));
} }
#else #else
Reflect.setProperty(o, field); Reflect.setProperty(o, field, value);
#end #end
} }
@@ -64,7 +64,7 @@ class UnsafeReflect {
return untyped func.__Run(args); return untyped func.__Run(args);
} }
#else #else
return Reflect.callMethod(o, func, args); return Reflect.callMethod(o, Reflect.field(o, field), args);
#end #end
} }
+9
View File
@@ -0,0 +1,9 @@
package hscript.utils;
/**
* This is used to mark classes that can be used with the `using` keyword.
* You can also add @:usableEntry to your class.
* If you wanna force the class to be called with any type, you can add @:usableEntry(forceAny)
**/
@:autoBuild(hscript.macros.UsingMacro.build())
interface UsingClass {}
+17
View File
@@ -0,0 +1,17 @@
package hscript.utils;
typedef UsingCall = (o: Dynamic, f: String, args: Array<Dynamic>) -> Dynamic;
/**
* Code based on Using system from "hscript-iris"
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/utils/UsingEntry.hx
*/
class UsingEntry {
public var name: String;
public var call: UsingCall;
public function new(name: String, call: UsingCall) {
this.name = name;
this.call = call;
}
}
+45
View File
@@ -0,0 +1,45 @@
package hscript.utils;
import hscript.utils.UsingEntry.UsingCall;
import StringTools;
import Lambda;
class UsingHandler {
public static var usingEntries:Array<UsingEntry> = [
new UsingEntry("StringTools", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
if (f == "isEof") // has @:noUsing
return null;
switch (Type.typeof(o)) {
case TInt if (f == "hex"):
return StringTools.hex(o, args[0]);
case TClass(String):
if (Reflect.hasField(StringTools, f)) {
var field = Reflect.field(StringTools, f);
if (Reflect.isFunction(field)) {
return Reflect.callMethod(StringTools, field, [o].concat(args));
}
}
default:
}
return null;
}),
new UsingEntry("Lambda", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
if (Tools.isIterable(o)) {
// TODO: Check if the values are Iterable<T>
if (Reflect.hasField(Lambda, f)) {
var field = Reflect.field(Lambda, f);
if (Reflect.isFunction(field)) {
return Reflect.callMethod(Lambda, field, [o].concat(args));
}
}
}
return null;
}),
];
public static function registerUsingGlobal(name: String, call:UsingCall):UsingEntry {
var entry = new UsingEntry(name, call);
usingEntries.push(entry);
return entry;
}
}
-1
View File
@@ -1 +0,0 @@
bin/*
-9
View File
@@ -1,9 +0,0 @@
# Haxe project
This is an example Haxe project scaffolded by Visual Studio Code.
Without further changes the structure is following:
* `src/Main.hx`: Entry point Haxe source file
* `build.hxml`: Haxe command line file used to build the project
* `README.md`: This file
-25
View File
@@ -1,25 +0,0 @@
# Generic Haxe stuffs to make the project compile
-cp src
-cp ../
-D analyzer-optimize
-main Main
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
# --library hxcpp-debug-server
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
--define hscriptPos
--define HXCPP_CHECK_POINTER
--define HXCPP_STACK_LINE
--define HXCPP_DEBUG_LINK
--dce no
# File Specific Defines
--define linux
# Copying the assets from source code to bin folder
# --cmd rm -rf bin/cpp/assets
# --cmd cp -r assets bin/cpp/assets
--cpp bin/cpp
--cmd cd bin/cpp
--cmd ./Main
-34
View File
@@ -1,34 +0,0 @@
# Generic Haxe stuffs to make the project compile
-cp src
-cp ../
-D analyzer-optimize
-main Main
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
# --library hscript-improved
# --library hxcpp-debug-server
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
--define hscriptPos
--define HXCPP_CHECK_POINTER
--define HXCPP_STACK_LINE
--define HXCPP_DEBUG_LINK
# -D HXCPP_M64
# -D HXCPP_ARM64
--dce no
# File Specific Defines
--define macos
--define mac
--macro keep('IntIterator')
# --macro hscript.macros.UsingHandler.init()
# --macro hscript.macros.ClassExtendMacro.init()
# Copying the assets from source code to bin folder
# --cmd rm -rf bin/cpp/assets
# --cmd cp -r assets bin/cpp/assets
--cpp bin/cpp
--cmd cd bin/cpp
--cmd ./Main
-74
View File
@@ -1,74 +0,0 @@
package;
import hscript.Interp;
import hscript.Expr;
@:access(hscript.Interp)
@:access(hscript.Parser)
class HScriptRunner {
public var interp:Interp;
public var headerCode:String = "";
public var tailCode:String = "";
public var clearPrevious:Bool = true;
public function new() {}
public function getNewInterp() {
var interp = Util.getInterp();
interp.errorHandler = Util.errorHandler;
return interp;
}
public function setup() {
interp = getNewInterp();
}
public function run() {}
public function teardown() {}
private var lastExpr:Expr;
public function execute(script:String):Dynamic {
var interp = clearPrevious ? getNewInterp() : this.interp;
lastExpr = Util.parse(headerCode + script + tailCode);
if(lastExpr == null)
return "ERROR";
if (clearPrevious)
return interp.execute(lastExpr);
else
return interp.exprReturn(lastExpr);
}
public function executeWithVars(script:String, vars:Dynamic):Dynamic {
var interp = clearPrevious ? getNewInterp() : this.interp;
lastExpr = Util.parse(headerCode + script + tailCode);
if(lastExpr == null)
return "ERROR";
for(v in Reflect.fields(vars))
interp.variables.set(v, Reflect.field(vars, v));
if (clearPrevious)
return interp.execute(lastExpr);
else
return interp.exprReturn(lastExpr);
}
public function executeUnsafe(script:String):Dynamic {
var interp = clearPrevious ? getNewInterp() : this.interp;
lastExpr = Util.parseUnsafe(headerCode + script + tailCode);
if (clearPrevious)
return interp.execute(lastExpr);
else
return interp.exprReturn(lastExpr);
}
public function executeWithVarsUnsafe(script:String, vars:Dynamic):Dynamic {
var interp = clearPrevious ? getNewInterp() : this.interp;
lastExpr = Util.parseUnsafe(headerCode + script + tailCode);
for(v in Reflect.fields(vars))
interp.variables.set(v, Reflect.field(vars, v));
if (clearPrevious)
return interp.execute(lastExpr);
else
return interp.exprReturn(lastExpr);
}
}
-84
View File
@@ -1,84 +0,0 @@
#if BENCHMARK
import benchmark.*;
#else
import tests.*;
#end
class Main {
public static var SHOW_KNOWN_BUGS:Bool = false;
static function main() {
#if BENCHMARK
Sys.println("Running benchmark");
//new Benchmark("Test", 1000);
var haxeTotalTime:Float = 0;
var hscriptTotalTime:Float = 0;
var haxeTimes:Array<Float> = [];
var hscriptTimes:Array<Float> = [];
var iterations:Int = 10;
for (i in 0...iterations) {
var benchmark:TestBenchmark = new TestBenchmark();
haxeTimes.push(benchmark.haxeTotalTime);
hscriptTimes.push(benchmark.hscriptTotalTime);
haxeTotalTime += benchmark.haxeTotalTime;
hscriptTotalTime += benchmark.hscriptTotalTime;
}
haxeTimes.sort((a, b) -> return a > b ? 1 : -1);
hscriptTimes.sort((a, b) -> return a > b ? 1 : -1);
var slowestHaxeTime:Float = haxeTimes[haxeTimes.length-1];
var slowestHscriptTime:Float = hscriptTimes[hscriptTimes.length-1];
var fastestHaxeTime:Float = haxeTimes[0];
var fastestHscriptTime:Float = hscriptTimes[0];
var haxeWon = hscriptTotalTime > haxeTotalTime;
Sys.println('${haxeWon ? "Haxe" : "Hscript"} was faster overall (Faster by: ${Util.roundWith((haxeWon ? hscriptTotalTime/haxeTotalTime : haxeTotalTime/hscriptTotalTime), 100)}x)');
Sys.println('Haxe average time: ${Util.convertToReadableTime(haxeTotalTime/iterations)} (Highest: ${Util.convertToReadableTime(slowestHaxeTime)}) (Lowest: ${Util.convertToReadableTime(fastestHaxeTime)})');
Sys.println('Hscript average time: ${Util.convertToReadableTime(hscriptTotalTime/iterations)} (Highest: ${Util.convertToReadableTime(slowestHscriptTime)}) (Lowest: ${Util.convertToReadableTime(fastestHscriptTime)})');
#else
Sys.println("Beginning tests");
if(!Main.SHOW_KNOWN_BUGS) {
Sys.println("Hiding known bugs [TEMPORARY]");
}
runTest("Array", new ArrayCase());
runTest("BinOp", new BinOpCase());
runTest("Enum", new EnumCase());
runTest("EvalOrder", new EvalOrderCase());
runTest("Error", new ErrorCase());
runTest("Float", new FloatCase());
runTest("IntIterator", new IntIteratorCase());
runTest("Lambda", new LambdaCase());
runTest("List", new ListCase());
runTest("Math", new MathCase());
runTest("Map", new MapCase());
runTest("Misc", new MiscCase());
runTest("Reflect", new ReflectCase());
runTest("Regex", new RegexCase());
runTest("Std", new StdCase());
runTest("String", new StringCase());
runTest("StringBuf", new StringBufCase());
runTest("StringTools", new StringToolsCase());
runTest("SwitchStatement", new SwitchCase());
// TODO: UnicodeCase.hx?
// TODO: UnicodeStringCase.hx?
runTest("Final", new FinalCase());
Util.printTestResults();
#end
}
#if !BENCHMARK
static function runTest(name:String, test:TestCase) {
Util.startUnitTest(name);
test.setup();
test.run();
test.teardown();
Util.endUnitTest();
}
#end
}
-330
View File
@@ -1,330 +0,0 @@
import hscript.Expr.Error;
import hscript.Printer;
import haxe.Constraints.IMap;
import haxe.EnumTools.EnumValueTools;
using StringTools;
class Util {
static inline function isNull(a:Dynamic):Bool {
return Type.enumEq(Type.typeof(a), TNull);
}
static inline function isFunction(a:Dynamic):Bool {
return Type.enumEq(Type.typeof(a), TFunction);
}
// TODO: check this for bugs
// Code from https://github.com/elnabo/equals/blob/master/src/equals/Equal.hx, (MIT License), but updated to work with haxe 4
public static function deepEqual<T> (a:T, b:T) : Bool {
if (a == b) { return true; } // if physical equality
if (isNull(a) || isNull(b)) {
return false;
}
switch (Type.typeof(a)) {
case TNull, TInt, TBool, TUnknown:
return a == b;
case TFloat:
return Math.isNaN(cast a) && Math.isNaN(cast b); // only valid true result remaining
case TFunction:
return Reflect.compareMethods(a, b); // only physical equality can be tested for function
case TEnum(_):
if (EnumValueTools.getIndex(cast a) != EnumValueTools.getIndex(cast b)) {
return false;
}
var a_args = EnumValueTools.getParameters(cast a);
var b_args = EnumValueTools.getParameters(cast b);
return deepEqual(a_args, b_args);
case TClass(_):
if ((a is String) && (b is String)) {
return a == b;
}
if ((a is Array) && (b is Array)) {
var a = cast(a, Array<Dynamic>);
var b = cast(b, Array<Dynamic>);
if (a.length != b.length) { return false; }
for (i in 0...a.length) {
if (!deepEqual(a[i], b[i])) {
return false;
}
}
return true;
}
if ((a is IMap) && (b is IMap)) {
var a = cast(a, IMap<Dynamic, Dynamic>);
var b = cast(b, IMap<Dynamic, Dynamic>);
var a_keys = [ for (key in a.keys()) key ];
var b_keys = [ for (key in b.keys()) key ];
a_keys.sort(Reflect.compare);
b_keys.sort(Reflect.compare);
if (!deepEqual(a_keys, b_keys)) { return false; }
for (key in a_keys) {
if (!deepEqual(a.get(key), b.get(key))) {
return false;
}
}
return true;
}
if ((a is Date) && (b is Date)) {
return cast(a, Date).getTime() == cast(b, Date).getTime();
}
if ((a is haxe.io.Bytes) && (b is haxe.io.Bytes)) {
return deepEqual(cast(a, haxe.io.Bytes).getData(), cast(b, haxe.io.Bytes).getData());
}
case TObject:
}
for (field in Reflect.fields(a)) {
var pa = Reflect.field(a, field);
var pb = Reflect.field(b, field);
if (isFunction(pa)) {
// ignore function as only physical equality can be tested, unless null
if (isNull(pa) != isNull(pb)) {
return false;
}
continue;
}
if (!deepEqual(pa, pb)) {
return false;
}
}
return true;
}
public static inline function passed() {
passedTestUnits++;
return true;
}
public static inline function failed() {
failedTestUnits++;
return false;
}
public static function assert(value:Bool, message:String, ?pos:haxe.PosInfos) {
if (value) {
return passed();
} else {
Sys.println("Assertion failed: " + message);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
public static function assertEq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
var equals = deepEqual(value, expected);
var _passed = equals;
if (_passed) {
return passed();
} else {
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
public static function assertEqPrintable(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
var equals = deepEqual(value, expected);
var _passed = equals;
if (_passed) {
return passed();
} else {
Sys.println("Assertion failed: " + message + " Expected: " + Printer.getEscapedString(expected) + " Got: " + Printer.getEscapedString(value));
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
public static function assertNeq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
var equals = deepEqual(value, expected);
var _passed = !equals;
// WARNING THIS MIGHT NOT WORK
if (_passed) {
return passed();
} else {
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
static var currentTestPath:Array<String> = [];
static var totalTestUnits:Int = 0;
static var passedTestUnits:Int = 0;
static var failedTestUnits:Int = 0;
static var skippedKnownBugs:Int = 0;
public static function runKnownBug(name:String, func:Void->Void) {
if (Main.SHOW_KNOWN_BUGS) {
Sys.println("Running known bug: " + name);
func();
} else {
skippedKnownBugs++;
}
}
public static function startUnitTest(path:String) {
Sys.println("Running unit tests in " + path);
currentTestPath.push(path);
totalTestUnits++;
}
public static function endUnitTest() {
var path = currentTestPath.pop();
//Sys.println("Finished unit tests " + path);
}
public static function printTestResults() {
Sys.println(Util.getTitle('RESULTS'));
Sys.println("Total Units: " + totalTestUnits);
Sys.println("Total tests passed: " + passedTestUnits);
Sys.println("Total tests failed: " + failedTestUnits);
if(skippedKnownBugs > 0)
Sys.println("Skipped known bugs: " + skippedKnownBugs);
//Sys.println("Finished unit tests " + currentTestPath.pop());
Sys.println(Util.getTitle('RESULTS'));
}
public static function parse(str:String) {
var p = new Parser();
p.allowTypes = true;
p.allowMetadata = true;
p.allowJSON = true;
var result = null;
try {
result = p.parseString(str);
} catch (e:Dynamic) {
Sys.println("## Error parsing: " + str);
Sys.println("## Error: " + e);
// Print stack trace
var stack = haxe.CallStack.toString(haxe.CallStack.exceptionStack());
Sys.println("## Stack trace: " + stack);
Sys.exit(1);
}
return result;
}
/**
* Parses a string without checking for errors
*/
public static function parseUnsafe(str:String) {
var p = new Parser();
p.allowTypes = true;
p.allowMetadata = true;
p.allowJSON = true;
return p.parseString(str);
}
public static var defaultImport = true;
public static function getInterp() {
var interp = new Interp();
//interp.importEnabled = true;
//interp.allowStaticVariables = true;
//interp.allowPublicVariables = true;
if(defaultImport) {
interp.variables.set("Math", Math);
interp.variables.set("Std", Std);
interp.variables.set("Type", Type);
interp.variables.set("StringTools", StringTools);
}
return interp;
}
public static inline function getTime():Float {
return untyped __global__.__time_stamp();
}
// expandScientificNotation but its WAY too long to write out
public static function exScienceN(value:Float):String {
var parts = Std.string(value).split("e");
var coefficient = Std.parseFloat(parts[0]);
var exponent = parts.length > 1 ? Std.parseInt(parts[1]) : 0;
var result = "";
if (exponent > 0) {
result += StringTools.replace(Std.string(coefficient), ".", "");
var decimalLength = Std.string(coefficient).split(".")[1].length;
var additionalZeros:Int = Std.int(Math.abs(exponent - decimalLength));
result += StringTools.lpad("", "0", additionalZeros); // repeat
} else {
result += "0.";
var leadingZeros:Int = Std.int(Math.abs(exponent) - 1);
result += StringTools.lpad("", "0", leadingZeros); // repeat
result += StringTools.replace(Std.string(coefficient), ".", "");
}
return result;
}
public static function convertToReadableTime(seconds:Float) {
if (seconds >= 1) return seconds + " s";
var milliseconds = seconds * 1000; // 1 second = 1,000 ms
if (milliseconds >= 1) return milliseconds + " ms";
var microseconds = seconds * 1000000; // 1 second = 1,000,000 μs
if (microseconds >= 1) return microseconds + " μs";
var nanoseconds = seconds * 1000000000; // 1 second = 1,000,000,000 ns
return nanoseconds + " ns";
}
public static function errorHandler(error:Error) {
#if hscriptPos
var fileName = error.origin;
var fn = '$fileName:${error.line}: ';
#else
var fn = '';
#end
var err = Std.string(error);
if (err.startsWith(fn)) err = err.substr(fn.length);
Sys.println("ERROR: " + fn + err);
//Logs.traceColored([
// Logs.logText(fn, GREEN),
// Logs.logText(err, RED)
//], ERROR);
}
public static function deepCompareArrays(a1: Array<Dynamic>, a2: Array<Dynamic>): Bool {
if (a1.length != a2.length)
return false;
for (i in 0...a1.length) {
var item1 = a1[i];
var item2 = a2[i];
if (Std.isOfType(item1, Array) && Std.isOfType(item2, Array)) {
if (!deepCompareArrays(item1, item2))
return false;
} else if (item1 != item2)
return false;
}
return true;
}
public static function roundDecimal(Value:Float, Precision:Int):Float {
var mult:Float = 1;
for (i in 0...Precision)
mult *= 10;
return Math.fround(Value * mult) / mult;
}
public inline static function roundWith(Value:Float, Mult:Int):Float {
return Math.fround(Value * Mult) / Mult;
}
public static function getTitle(title:String, ?dashsLen:Int = 46) {
var l = StringTools.lpad("", "-", Std.int((dashsLen - title.length - 2)/2));
return l + ' $title ' + l;
}
}
-74
View File
@@ -1,74 +0,0 @@
package benchmark;
import hscript.Expr;
class Benchmark extends HScriptRunner {
public var haxeTimes:Array<Float> = [];
public var haxeTotalTime:Float = 0;
public var hscriptTimes:Array<Float> = [];
public var hscriptTotalTime:Float = 0;
public function new(name:String, iterations:Int) {
super();
doBenchmark(name, iterations);
}
public function doBenchmark(name:String, iterations:Int) {
haxeTimes = cast new haxe.ds.Vector<Float>(iterations);
hscriptTimes = cast new haxe.ds.Vector<Float>(iterations);
for (i in 0...iterations) {
reset();
var haxeStartTime:Float = Util.getTime();
haxeBenchmark();
var haxeEndTime:Float = Util.getTime();
haxeTimes[i] = haxeEndTime-haxeStartTime;
reset();
var hscriptStartTime:Float = Util.getTime();
hscriptBenchmark();
var hscriptEndTime:Float = Util.getTime();
hscriptTimes[i] = hscriptEndTime-hscriptStartTime;
}
haxeTimes.sort(Reflect.compare);
hscriptTimes.sort(Reflect.compare);
for (time in haxeTimes) haxeTotalTime += time;
for (time in hscriptTimes) hscriptTotalTime += time;
Sys.println(Util.getTitle('$name BENCHMARK'));
if ((haxeTotalTime == 0 || hscriptTotalTime == 0) || (haxeTimes.length <= 0 || hscriptTimes.length <= 0)) {
Sys.println('BENCHMARK UNNOTICABLE, TOOK 0 SECONDS');
} else {
var haxeWon = hscriptTotalTime > haxeTotalTime;
Sys.println('${haxeWon ? "Haxe" : "Hscript"} was faster overall (Faster by: ${Util.roundWith((haxeWon ? hscriptTotalTime/haxeTotalTime : haxeTotalTime/hscriptTotalTime), 100)}x)');
Sys.println('Haxe average time: ${Util.convertToReadableTime(haxeTotalTime/iterations)} (Highest: ${haxeTimes[haxeTimes.length-1]})');
Sys.println('Hscript average time: ${Util.convertToReadableTime(hscriptTotalTime/iterations)} (Highest: ${hscriptTimes[haxeTimes.length-1]})');
}
Sys.println(Util.getTitle('$name BENCHMARK'));
}
public function reset() {}
public var expr:Expr;
public inline function cacheExpr(script:String) {
interp = getNewInterp();
interp.variables.set("self", this);
interp.scriptObject = this;
expr = Util.parse(headerCode + script + tailCode);
}
public override function execute(script:String):Dynamic {
if (expr == null)
cacheExpr(script);
return interp.execute(expr);
}
public function haxeBenchmark() {}
public function hscriptBenchmark() {}
}
-57
View File
@@ -1,57 +0,0 @@
package benchmark;
@:dce(off)
class TestBenchmark extends Benchmark {
public function new() {
super("Test", 10000);
}
//var hscript = "var a:Array<Float> = []; for (i in 0...1000) a.push(i * 2 + 1 / 6); if(true == true) a.push(1);";
var hscript = "
var a:Array<Float> = [];
for(i in 0...1000) {
switch(5) {
case true: a.push(1);
default: a.push(i / 1);
}
}
if(('a' == 'a') ? true : false) a.push(1);
if(true == true)
a.push(1);";
function test() {
return 5;
}
var aaa = null;
public override function reset() {
super.reset();
aaa = {
test: test
};
if(expr == null)
cacheExpr(hscript);
interp.variables.remove("a");
//interp.variables.set("aaa", aaa);
interp.variables.set("test", test);
}
public override function haxeBenchmark() {
var a:Array<Float> = [];
//for (i in 0...1000) a.push(i * 2 + 1 / 6);
//for (i in 0...1000) a.push(i / 2 + 3 / 6);
for(i in 0...1000) {
switch(5) {
case 0: a.push(1);
default: a.push(i / 1);
}
}
if(('a' == 'a') ? true : false) a.push(1);
if(true == true) a.push(1);
}
public override function hscriptBenchmark() {
interp.execute(expr);
}
}
-4
View File
@@ -1,4 +0,0 @@
import hscript.Interp;
import hscript.Parser;
import Util;
-19
View File
@@ -1,19 +0,0 @@
package tests;
class ArrayCase extends TestCase {
override function setup() {
super.setup();
}
override function run() {
assertEq("[].length", 0);
assertEq("[1].length", 1);
assertEq("[1,2,3].length", 3);
assertEq("var a = [];\na[4] = 1;\na.length", 5);
assertEq("var a = [];\na[4] = 1;\na[a.length] = 1;\na.length", 6);
}
override function teardown() {
super.teardown();
}
}
-161
View File
@@ -1,161 +0,0 @@
package tests;
class BinOpCase extends TestCase {
override function setup() {
super.setup();
headerCode = "
var a = 1;
var b = 2;
var c = 3;
";
//tailCode = "";
}
override function run() {
var a = 1;
var b = 2;
var c = 3;
assertEq("a + b", a + b);
assertEq("a - b", a - b);
assertEq("a * b", a * b);
assertEq("a / b", a / b);
assertEq("a % b", a % b);
assertEq("a & b", a & b);
assertEq("a | b", a | b);
assertEq("a ^ b", a ^ b);
assertEq("5 ^ 3", 5 ^ 3);
assertEq("a << b", a << b);
assertEq("a >> b", a >> b);
assertEq("a >>> b", a >>> b);
assertEq("a == b", a == b);
assertEq("a != b", a != b);
assertEq("a >= b", a >= b);
assertEq("a <= b", a <= b);
assertEq("a > b", a > b);
assertEq("a < b", a < b);
//assertEq("a || b", a || b);
//assertEq("a && b", a && b);
//assertEq("a is b", false);
// Order of operations
assertEq("a + b * c", a + b * c);
assertEq("a * b + c", a * b + c);
assertEq("a * (b + c)", a * (b + c));
assertEq("(a + b) * c", (a + b) * c);
assertEq("a / b / c", a / b / c);
assertEq("a % b % c", a % b % c);
assertEq("a & b & c", a & b & c);
assertEq("a | b | c", a | b | c);
assertEq("a ^ b ^ c", a ^ b ^ c);
assertEq("a << b << c", a << b << c);
assertEq("a >> b >> c", a >> b >> c);
assertEq("a >>> b >>> c", a >>> b >>> c);
// Precedence
assertEq("a + b + c", a + b + c);
assertEq("a + b - c", a + b - c);
assertEq("a * b / c", a * b / c);
assertEq("a / b * c", a / b * c);
assertEq("a % b | c", a % b | c);
assertEq("a & b ^ c", a & b ^ c);
assertEq("a << b >> c", a << b >> c);
assertEq("a >>> b << c", a >>> b << c);
headerCode = "
var a = null;
var b = 'hello';
var c = 'world';
var d = '!';
";
var a = null;
var b = 'hello';
var c = 'world';
var d = '!';
assertEq("a + b", a + b);
#if (haxe >= "4.3.0")
assertEq("a ?? b", a ?? b);
assertEq("a ?? b ?? c", a ?? b ?? c);
assertEq("a ?? b ?? c ?? d", a ?? b ?? c ?? d);
#else
Sys.println("Tests are inaccurate with Haxe 4.2.x");
assertEq("a ?? b", nc(a, b));
assertEq("a ?? b ?? c", nc(a, nc(b, c)));
assertEq("a ?? b ?? c ?? d", nc(a, nc(b, nc(c, d))));
#end
var tests:Array<Array<Dynamic>> = [
[null, "null"],
[{}, "{}"],
[{a: 1}, "{a: 1}"],
[{a: {b: 1}}, "{a: {b: 1}}"],
[{a: {b: {c: 1}}}, "{a: {b: {c: 1}}}"],
[{a: {b: {c: {d: 1}}}}, "{a: {b: {c: {d: 1}}}}"]
];
for(code in tests) {
headerCode = 'var a = ${code[1]};';
var a = code[0];
#if (haxe >= "4.3.0")
assertEq("a?.b", a?.b);
assertEq("a?.b?.c", a?.b?.c);
assertEq("a?.b?.c?.d", a?.b?.c?.d);
#else
assertEq("a?.b", nf(a, b));
assertEq("a?.b?.c", nf(nf(a, b), c));
assertEq("a?.b?.c?.d", nf(nf(nf(a, b), c), d));
#end
}
headerCode = "
var a = null;
var b = 'hello';
";
var a = null;
var b = 'hello';
#if (haxe >= "4.3.0")
a ??= b;
assertEq("a ??"+"= b; a", a);
#else
if(a == null) a = b;
assertEq("a ??"+"= b; a", a);
#end
headerCode = "
var a = 'test';
var b = 'hello';
";
var a = 'test';
var b = 'hello';
#if (haxe >= "4.3.0")
a ??= b;
assertEq("a ??"+"= b; a", a);
#else
if(a == null) a = b;
assertEq("a ??"+"= b; a", a);
#end
}
inline function nc(a:Dynamic, b:Dynamic):Dynamic {
return (a == null) ? b : a;
}
inline function nf(a:Dynamic, b:String):Dynamic {
return (a == null) ? null : Reflect.field(a, b);
}
override function teardown() {
super.teardown();
}
}
-68
View File
@@ -1,68 +0,0 @@
package tests;
class EnumCase extends TestCase {
override function setup() {
super.setup();
}
var importEnabled = true;
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("assertEq", Util.assertEq);
if(importEnabled)
interp.variables.set("SomeEnum", SomeEnum);
interp.variables.set("Type", Type);
return interp;
}
override function run() {
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
importEnabled = false;
headerCode = "import tests.EnumCase.SomeEnum;";
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
headerCode = "import tests.SomeEnum;";
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
}
override function teardown() {
super.teardown();
}
}
enum SomeEnum {
NoArguments;
OneArgument(arg:String);
TwoArguments(arg1:String, arg2:String);
}
-20
View File
@@ -1,20 +0,0 @@
package tests;
class ErrorCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
return interp;
}
override function run() {
Sys.println("TODO: write tests for errors");
}
override function teardown() {
super.teardown();
}
}
-280
View File
@@ -1,280 +0,0 @@
package tests;
class EvalOrderCase extends TestCase {
override function setup() {
super.setup();
headerCode = '
function func(i1:Dynamic, i2:Dynamic, i3:Dynamic) {
return i1 + ";" + i2 + ";" + i3;
}
';
//tailCode = "";
}
override function run() {
var a = 1;
var b = 2;
var c = 3;
function func(i1:Dynamic, i2:Dynamic, i3:Dynamic) {
return i1 + ";" + i2 + ";" + i3;
}
clearPrevious = true;
var i = 0;
assertEq("var i = 0; func(i++, i++, i++)", func(i++, i++, i++));
var i = 0;
assertEq("var i = 0; [i++, i++, i++].join(';')", [i++, i++, i++].join(";"));
var i = 0;
var obj = {
a: i++,
b: i++,
c: i++
}
assertEq("var i = 0; var obj = {
a: i++,
b: i++,
c: i++
}; func(obj.a, obj.b, obj.c)", func(obj.a, obj.b, obj.c));
var i = 0;
assertEq("var i = 0; func(i++, [i++, i++].join(';'), i++)", func(i++, [i++, i++].join(";"), i++));
clearPrevious = true;
headerCode += '
var buf:Array<Int> = [];
function a() {
buf.push(1);
return 1;
}
function b() {
buf.push(2);
return 2;
}
function c() {
buf.push(3);
return 3;
}
function d() {
buf.push(4);
return 4;
}
function e() {
buf.push(5);
return 5;
}
function f() {
buf.push(6);
return 6;
}
function begin() {
buf = [];
return function() {
return buf.join("_");
}
}
';
var buf:Array<Int> = [];
function a() {
buf.push(1);
return 1;
}
function b() {
buf.push(2);
return 2;
}
function c() {
buf.push(3);
return 3;
}
function d() {
buf.push(4);
return 4;
}
function e() {
buf.push(5);
return 5;
}
function f() {
buf.push(6);
return 6;
}
function begin() {
buf = [];
return function() {
return buf.join("_");
}
}
// &&
var end = begin();
(a() + b()) >= 0 && (c() + d()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 0 && (c() + d()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 99 && (c() + d()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 99 && (c() + d()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 0 && (c() + d()) >= 0 && (e() + f()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 0 && (c() + d()) >= 0 && (e() + f()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 99 && (c() + d()) >= 0 && (e() + f()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 99 && (c() + d()) >= 0 && (e() + f()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 0 && (c() + d()) >= 99 && (e() + f()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 0 && (c() + d()) >= 99 && (e() + f()) >= 0;
end()', end());
// ||
var end = begin();
(a() + b()) >= 0 || (c() + d()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 0 || (c() + d()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 99 || (c() + d()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 99 || (c() + d()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 0 || (c() + d()) >= 0 || (e() + f()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 0 || (c() + d()) >= 0 || (e() + f()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 99 || (c() + d()) >= 0 || (e() + f()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 99 || (c() + d()) >= 0 || (e() + f()) >= 0;
end()', end());
var end = begin();
(a() + b()) >= 99 || (c() + d()) >= 99 || (e() + f()) >= 0;
assertEq('var end = begin();
(a() + b()) >= 99 || (c() + d()) >= 99 || (e() + f()) >= 0;
end()', end());
headerCode += '
function arr(x, y) {
return [];
}
function idx(x, y) {
return 0;
}
function f1() {
buf.push(1);
return function(i) { };
}
function f2() {
buf.push(2);
return 2;
}
';
// []
function arr(x, y) {
return [];
}
function idx(x, y) {
return 0;
}
function f1() {
buf.push(1);
return function(i) { };
}
function f2() {
buf.push(2);
return 2;
}
var end = begin();
var _ = (arr(a(), b()))[idx(c(), d())];
assertEq('var end = begin();
var _ = (arr(a(), b()))[idx(c(), d())];
end()', end());
headerCode += '
var d:Dynamic = { f1: f1 };
function f3() {
buf.push(3);
d.f1 = function f3df1(i) {
buf.push(4);
return 4;
}
return 3;
}
';
var d:Dynamic = { f1: f1 };
function f3() {
buf.push(3);
d.f1 = function(i) {
buf.push(4);
return 4;
}
return 3;
}
Util.runKnownBug("Function arguments get called first, before function is evaluated", () ->{
var end = begin();
d.f1()(f3());
d.f1(f2());
assertEq('var end = begin();
// Expected internal behavior for `d.f1()(f3());`
//var func = d.f1();
//func(f3());
d.f1()(f3());
d.f1(f2());
end()', end());
});
}
override function teardown() {
super.teardown();
}
}
-157
View File
@@ -1,157 +0,0 @@
package tests;
class FinalCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
Util.defaultImport = false;
var interp = super.getNewInterp();
Util.defaultImport = true;
//interp.variables.set("Std", Std);
//interp.variables.set("String", String);
//interp.variables.set("Bool", Bool);
//interp.variables.set("Float", Float);
//interp.variables.set("Array", Array);
//interp.variables.set("Int", Int);
//interp.variables.set("IntIterator", IntIterator);
return interp;
}
override function run() {
headerCode = "var i = 5; var x = 10;";
var i = 5; var x = 10;
assertDisplay("(i - x) * 30 + 90", ((i-x) * 30) + 90);
assertDisplay("i - x * 30 + 90", i-x * 30 + 90);
/*
// Test operator precedence
assertDisplay("i / x * 30 + 90", i / x * 30 + 90);
assertDisplay("i / (x * 30) + 90", i / (x * 30) + 90);
assertDisplay("x * 30 / i + 90", x * 30 / i + 90);
//assertEq("(x * 30) / i + 90", x * 30 / i + 90);
// (_) -> {a = false;}
// Shouldnt be converted to (_) -> return {a = false;}
Util.parse("openCodesList(false, codesOpened ? false : true, previousOpen != open ? true : false);");
Util.parse("openCodesList(false, 'HELLO' ? false : false, 'WORLD' ? true : true);");
Util.parse('FlxTween.tween(newSprite, {"scale.x": 1, "scale.y": 1, alpha: 1, angle: 0}, 0.3, {ease: FlxEase.qaudInOut});');
Util.parse('
bloomShader.dim = dim = .8 + (.3 * FlxMath.fastSin(__totalTime));
bloomShader.size = size = 18 + (8 * FlxMath.fastSin(__totalTime));
');
*/
Util.parse('sprite.setPosition(codesList.x + (sprite.ID % 2 == 1 ? 240 : 67),codesList.y + (15 * sprite.ID) + (sprite.ID%2 == 1 ? 35 : 54));');
headerCode = "import tests.FinalCase.TestEnum;";
assertEq("TestEnum.A", TestEnum.A);
assertEq("TestEnum.B", TestEnum.B);
assertEq("TestEnum.C(1, 2)", TestEnum.C(1, 2));
assertEq("TestEnum.D(1, 2, 3)", TestEnum.D(1, 2, 3));
Util.parse('test ? 0xFF343434 : 0xFF92A2FF');
Util.parse('test ? 1 : 0.75;');
Util.runKnownBug("Function then array causes error, unless the function ends with ;", function() {
headerCode = "";
function isEven(i:Int):Bool { return i % 2 == 0; }
assertEq("function isEven(i:Int):Bool { return i % 2 == 0; }[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]);
});
assertEq("[for(i in 0...10) i]", [for(i in 0...10) i]);
assertEq("[for(i in 0...10) i => null]", [for(i in 0...10) i => null]);
assertEq("[for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']", [for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']);
assertEq("[for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']", [for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']);
assertEq("[for(i in 0...10) for(j in 0...10) i * j]", [for(i in 0...10) for(j in 0...10) i * j]);
headerCode = "function isEven(i:Int):Bool { return i % 2 == 0; };";
function isEven(i:Int):Bool { return i % 2 == 0; }
assertEq("[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]);
assertEq("[for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]", [for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]);
headerCode = "function area(a:Int, b:Int):Int { return a * b; };";
function area(a:Int, b:Int):Int { return a * b; }
assertEq("[for(i in 0...10) for(j in 0...10) area(i, j)]", [for(i in 0...10) for(j in 0...10) area(i, j)]);
headerCode = "";
assertEq("[0=>'hello', 1=>'world']", [0=>'hello', 1=>'world']);
assertEq("[0=>'hello', 1=>'world'][1]", [0=>'hello', 1=>'world'][1]);
assertEq("[0=>'hello', 5=>'world'][1]", [0=>'hello', 5=>'world'][1]);
assertEq("
import tests.FinalCase;
FinalCase.test1();
", FinalCase.test1());
assertEq("
import tests.FinalCase as FC;
FC.test1();
", FinalCase.test1());
assertEq("
import tests.FinalCase as FC;
import tests.FinalCase.test;
FC.test1() + test();
", FinalCase.test1() + test());
assertEq("
//import Std.isOfType;
Std.isOfType('', String);
", Std.isOfType('', String));
assertEq("
import Std.isOfType;
isOfType('', String);
", Std.isOfType('', String));
assertEq("
import Math.round as F;
F(2.9);", Math.round(2.9));
assertEq("
import Math.round;
round(1.9);", Math.round(1.9));
// Test EOF with preprocessor
}
static function test1() {
return "hello";
}
static function test() {
return "world";
}
function assertDisplay(script:String, expected:Dynamic) {
var _script = script;
script = '"${script}";\n' + script;
assertEq(script, expected, _script);
}
override function teardown() {
super.teardown();
}
}
enum TestEnum {
A;
B;
C(a:Int, b:Int);
D(a:Int, b:Int, c:Int);
}
-74
View File
@@ -1,74 +0,0 @@
package tests;
class FloatCase extends TestCase {
override function setup() {
super.setup();
headerCode = "
var nan = Math.NaN;
var pinf = Math.POSITIVE_INFINITY;
var ninf = Math.NEGATIVE_INFINITY;
var fl:Float = 0.0;
";
}
override function run() {
var nan = Math.NaN;
var pinf = Math.POSITIVE_INFINITY;
var ninf = Math.NEGATIVE_INFINITY;
var fl:Float = 0.0;
assertEq("fl > nan", fl > nan);
assertEq("fl < nan", fl < nan);
assertEq("fl >= nan", fl >= nan);
assertEq("fl <= nan", fl <= nan);
assertEq("fl == nan", fl == nan);
assertEq("fl != nan", fl != nan);
assertEq("nan > nan", nan > nan);
assertEq("nan < nan", nan < nan);
assertEq("nan >= nan", nan >= nan);
assertEq("nan <= nan", nan <= nan);
assertEq("nan == nan", nan == nan);
assertEq("nan != nan", nan != nan);
assertEq("pinf > nan", pinf > nan);
assertEq("pinf < nan", pinf < nan);
assertEq("pinf >= nan", pinf >= nan);
assertEq("pinf <= nan", pinf <= nan);
assertEq("pinf == nan", pinf == nan);
assertEq("pinf != nan", pinf != nan);
assertEq("ninf > nan", ninf > nan);
assertEq("ninf < nan", ninf < nan);
assertEq("ninf >= nan", ninf >= nan);
assertEq("ninf <= nan", ninf <= nan);
assertEq("ninf == nan", ninf == nan);
assertEq("ninf != nan", ninf != nan);
assertEq("nan > fl", nan > fl);
assertEq("nan < fl", nan < fl);
assertEq("nan >= fl", nan >= fl);
assertEq("nan <= fl", nan <= fl);
assertEq("nan == fl", nan == fl);
assertEq("nan != fl", nan != fl);
assertEq("nan > pinf", nan > pinf);
assertEq("nan < pinf", nan < pinf);
assertEq("nan >= pinf", nan >= pinf);
assertEq("nan <= pinf", nan <= pinf);
assertEq("nan == pinf", nan == pinf);
assertEq("nan != pinf", nan != pinf);
assertEq("nan > ninf", nan > ninf);
assertEq("nan < ninf", nan < ninf);
assertEq("nan >= ninf", nan >= ninf);
assertEq("nan <= ninf", nan <= ninf);
assertEq("nan == ninf", nan == ninf);
assertEq("nan != ninf", nan != ninf);
}
override function teardown() {
super.teardown();
}
}
-59
View File
@@ -1,59 +0,0 @@
package tests;
class IntIteratorCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("assertEq", Util.assertEq);
interp.variables.set("IntIterator", IntIterator);
return interp;
}
override function run() {
var ii = new IntIterator(0, 2);
var line = 19;
execute('
var ii = new IntIterator(0, 2);
assertEq(ii.hasNext(), ${ii.hasNext()}, "l${line++}");
assertEq(ii.next(), ${ii.next()}, "l${line++}");
assertEq(ii.hasNext(), ${ii.hasNext()}, "l${line++}");
assertEq(ii.next(), ${ii.next()}, "l${line++}");
assertEq(ii.hasNext(), ${ii.hasNext()}, "l${line++}");
');
var ii = new IntIterator(0, 2);
var r = [];
for (i in ii)
r.push(i);
assertEq('
var ii = new IntIterator(0, 2);
var r = [];
for (i in ii)
r.push(i);
r
', r);
var ii = new IntIterator(0, 2);
var r = [];
for (i in ii)
r.push(i);
for (i in ii)
r.push(i);
assertEq('
var ii = new IntIterator(0, 2);
var r = [];
for (i in ii)
r.push(i);
for (i in ii)
r.push(i);
r
', r);
}
override function teardown() {
super.teardown();
}
}
-35
View File
@@ -1,35 +0,0 @@
package tests;
class LambdaCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("Lambda", Lambda);
return interp;
}
override function run() {
// has
assertEq("Lambda.has([1,2,3],1)", Lambda.has([1,2,3],1));
assertEq("Lambda.has([1,2,3],4)", Lambda.has([1,2,3],4));
assertEq("Lambda.has([],null)", Lambda.has([],null));
assertEq("Lambda.has([null],null)", Lambda.has([null],null));
// exists
assertEq("Lambda.exists([1, 2, 3], function(i) return i == 2)", Lambda.exists([1, 2, 3], function(i) return i == 2));
assertEq("Lambda.exists([1, 2, 3], function(i) return i == 4)", Lambda.exists([1, 2, 3], function(i) return i == 4));
assertEq("Lambda.exists([], function(x) return true)", Lambda.exists([], function(x) return true));
// foreach
assertEq("Lambda.foreach([2, 4, 6],function(i) return i % 2 == 0)", Lambda.foreach([2, 4, 6],function(i) return i % 2 == 0));
assertEq("Lambda.foreach([2, 4, 7],function(i) return i % 2 == 0)", Lambda.foreach([2, 4, 7],function(i) return i % 2 == 0));
assertEq("Lambda.foreach([], function(x) return false)", Lambda.foreach([], function(x) return false));
}
override function teardown() {
super.teardown();
}
}
-39
View File
@@ -1,39 +0,0 @@
package tests;
class ListCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("List", List);
return interp;
}
override function run() {
var l4 = new List();
l4.add(1);
l4.add(2);
l4.add(3);
l4.add(5);
l4.add(8);
headerCode = "var l4 = new List();
l4.add(1);
l4.add(2);
l4.add(3);
l4.add(5);
l4.add(8);";
assertEq("[for (k=>v in l4) k]", [for (k=>v in l4) k]);
assertEq("[for (k=>v in l4) v]", [for (k=>v in l4) v]);
assertEq("[for (k=>v in l4) k*v]", [for (k=>v in l4) k*v]);
assertEq("[for (v in l4) v]", [for (v in l4) v]);
}
override function teardown() {
super.teardown();
}
}
-23
View File
@@ -1,23 +0,0 @@
package tests;
class MapCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("IntMap", haxe.ds.IntMap);
interp.variables.set("ObjectMap", haxe.ds.ObjectMap);
interp.variables.set("StringMap", haxe.ds.StringMap);
return interp;
}
override function run() {
Sys.println("TODO: Write tests for Map");
}
override function teardown() {
super.teardown();
}
}
-23
View File
@@ -1,23 +0,0 @@
package tests;
class MathCase extends TestCase {
override function setup() {
super.setup();
}
override function run() {
assertEq("Math.PI", Math.PI);
assertEq("1 + 2", 3);
assertEq("1 + 2 + 3", 6);
assertEq("1 + 2 + 3 + 4", 10);
assertEq("(1 + 2) + 3", 6);
assertEq("(1 + 2) + 3 + 4", 10);
assertEq("(1 + 2) * 3", 9);
assertEq("(1 + 2) * 3 * 4", 36);
assertEq("((1 + 2) * 3 * 4) / 2", 18);
}
override function teardown() {
super.teardown();
}
}
-164
View File
@@ -1,164 +0,0 @@
package tests;
class MiscCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("Std", Std);
interp.variables.set("String", String);
interp.variables.set("Bool", Bool);
interp.variables.set("Float", Float);
interp.variables.set("Array", Array);
interp.variables.set("Int", Int);
interp.variables.set("IntIterator", IntIterator);
return interp;
}
override function run() {
assertEq("", null);
assertEq("true", true);
assertEq("false", false);
assertEq("null", null);
Util.runKnownBug("Redefining a variable with the same name as a function", () ->{
// Redefining a function with the same name as a variable
var a = 1; function a() { return 4; }
assertEq("var a = 1; function a() { return 4; }; a", a);
});
var a = 1; var a = 2;
assertEq("var a = 1; var a = 2; a", a);
assertEq("0",0);
assertEq("0xFF", 255);
assertEq("0xFF_FF", 0xFFFF);
assertEq("0b101", 5); //assertEq("0b101", 0b101);
#if !(php || python)
#if haxe3
assertEq("0xBFFFFFFF", 0xBFFFFFFF);
assertEq("0x7FFFFFFF", 0x7FFFFFFF);
#elseif !neko
assertEq("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
assertEq("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
#end
#end
assertEq("-123",-123);
assertEq("- 123",-123);
assertEq("1.546",1.546);
assertEq(".545",.545);
assertEq("'bla'","bla");
assertEq("null",null);
assertEq("true",true);
assertEq("false",false);
assertEq("1 == 2",false);
assertEq("1.3 == 1.3",true);
assertEq("5 > 3",true);
assertEq("0 < 0",false);
assertEq("-1 <= -1",true);
assertEq("1 + 2",3);
assertEq("~545",-546);
assertEq("'abc' + 55","abc55");
assertEq("'abc' + 'de'","abcde");
assertEq("-1 + 2",1);
assertEq("1 / 5",0.2);
assertEq("3 * 2 + 5",11);
assertEq("3 * (2 + 5)",21);
assertEq("3 * 2 // + 5 \n + 6",12);
assertEq("3 /* 2\n */ + 5",8);
assertEq("[55,66,77][1]",66);
assertEq("var a = [55]; a[0] *= 2; a[0]",110);
assertEq("x",55,{ x : 55 });
assertEq("var y = 33; y",33);
assertEq("{ 1; 2; 3; }",3);
assertEq("o.val",55,{ o : { val : 55 } });
assertEq("o.val",null,{ o : {} });
assertEq("var a = 1; a++",1);
assertEq("var a = 1; a++; a",2);
assertEq("var a = 1; ++a",2);
assertEq("var a = 1; a *= 3",3);
assertEq("a = b = 3; a + b",6);
assertEq("add(1,2)",3,{ add : function(x,y) return x + y });
assertEq("a.push(5); a.pop() + a.pop()",{var a = [3]; a.push(5); a.pop() + a.pop();},{ a : [3] });
assertEq("if( true ) 1 else 2",1);
assertEq("if( false ) 1 else 2",2);
assertEq("var t = 0; for( x in [1,2,3] ) t += x; t",6);
assertEq("var a = new Array(); for( x in 0...5 ) a[x] = x; a.join('-')","0-1-2-3-4");
assertEq("(function(a,b) return a + b)(4,5)",9);
assertEq("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10);
assertEq("var a = false; do { a = true; } while (!a); a;",true);
assertEq("var t = 0; for( x in 1...10 ) t += x; t", 45);
#if haxe3
assertEq("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45);
#else
assertEq("var t = 0; for( x in new IntIter(1,10) ) t +=x; t", 45);
#end
assertEq("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",{var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x;});
assertEq("var i=2; if( true ) --i; i",1);
assertEq("var i=0; if( i++ > 0 ) i=3; i",1);
assertEq("var a = 5/2; a",2.5);
assertEq("{ x = 3; x; }", 3);
assertEq("{ x : 3, y : {} }.x", 3);
assertEq("function bug(){ \n }\nbug().x", null);
assertEq("1 + 2 == 3", true);
assertEq("-2 == 3 - 5", true);
assertEq("var x=-3; x", -3);
assertEq("var a:Array<Dynamic>=[1,2,4]; a[2]", 4);
assertEq("/**/0", 0);
assertEq("x=1;x*=-2", -2);
assertEq("var f = x -> x + 1; f(3)", 4);
assertEq("var f = () -> 55; f()", 55);
assertEq("var f = (x) -> x + 1; f(3)", 4);
assertEq("var f = (x:Int) -> x + 1; f(3)", 4);
assertEq("var f = (x,y) -> x + y; f(3,1)", 4);
assertEq("var f = (x,y:Int) -> x + y; f(3,1)", 4);
assertEq("var f = (x:Int,y:Int) -> x + y; f(3,1)", 4);
assertEq("var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1)", {var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1);});
assertEq("var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1)", {var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1);});
//assertEq("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", {var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2);});
//assertEq("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", {var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2);});
assertEq("var a = 10; var b = 5; a - -b", 15);
assertEq("var a = 10; var b = 5; a - b / 2", 7.5);
Util.runKnownBug("Redefining a variable in a scope overrides the previous definition", () -> {
assertEq("{ var x = 0; } x",55,{ x : 55 });
});
Util.runKnownBug("Global Y isnt updated from the inside of the function", () -> {
assertEq("var y = 0; var add = function(a) y += a; add(5); add(3); y", {var y = 0; var add = function(a) y += a; add(5); add(3); y;});
});
Util.runKnownBug("Throwing an exception inside a function doesnt return the correct value", () -> {
assertEq("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",{
var x = 1;
var f:Dynamic = function(x) throw x;
try
f(55)
catch( e:Dynamic )
e + x;
});
});
assertEq("var a = if( true ) 1 else 2; a",1);
assertEq("var a = if( false ) 1 else 2; a",2);
assertEq("if(true) 1; null", null);
assertEq("if(false) 1; null", null);
assertEq("[55,66,77][1]",66);
assertEq("{
var a = 1;
function b() { return a; }
a = 2;
b();
}", 2);
}
override function teardown() {
super.teardown();
}
}
-58
View File
@@ -1,58 +0,0 @@
package tests;
class ReflectCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("Reflect", Reflect);
return interp;
}
override function run() {
headerCode = "var x = { a: 1, b: null };";
var x = { a: 1, b: null };
assertEq("Reflect.field(x, 'a')", Reflect.field(x, 'a'));
assertEq("Reflect.field(x, 'b')", Reflect.field(x, 'b'));
assertEq("Reflect.field(x, 'c')", Reflect.field(x, 'c'));
assertEq("Reflect.hasField(x, 'a')", Reflect.hasField(x, 'a'));
assertEq("Reflect.hasField(x, 'b')", Reflect.hasField(x, 'b'));
assertEq("Reflect.hasField(x, 'c')", Reflect.hasField(x, 'c'));
headerCode = "var n = null;";
var n = null;
assertEq("Reflect.field(n, n)", Reflect.field(n, n));
assertEq("Reflect.field(1, 'foo')", Reflect.field(1, "foo"));
headerCode = "var x = { a: 1, b: null };";
Reflect.setField(x, 'a', 2); assertEq("Reflect.setField(x, 'a', 2); x.a", x.a);
Reflect.setField(x, 'c', 'foo'); assertEq("Reflect.setField(x, 'c', 'foo'); Reflect.field(x, 'c')", Reflect.field(x, 'c'));
headerCode = "var x = { a: 1, b: null };";
Reflect.setProperty(x, 'a', 2); assertEq("Reflect.setProperty(x, 'a', 2); x.a", x.a);
Reflect.setProperty(x, 'c', 'foo'); assertEq("Reflect.setProperty(x, 'c', 'foo'); Reflect.field(x, 'c')", Reflect.field(x, 'c'));
headerCode = "var x = function(t) return 1; var y = function(t) return -1; var z = function(t) return 1;";
var x = function(t) return 1;
var y = function(t) return -1;
var z = function(t) return 1;
assertEq("Reflect.compareMethods(x,y)", Reflect.compareMethods(x,y));
assertEq("Reflect.compareMethods(x,z)", Reflect.compareMethods(x,z));
assertEq("Reflect.compareMethods(y,z)", Reflect.compareMethods(y,z));
assertEq("Reflect.compareMethods(x,x)", Reflect.compareMethods(x,x));
assertEq("Reflect.compareMethods(y,y)", Reflect.compareMethods(y,y));
assertEq("Reflect.compareMethods(z,z)", Reflect.compareMethods(z,z));
}
override function teardown() {
super.teardown();
}
}
-120
View File
@@ -1,120 +0,0 @@
package tests;
class RegexCase extends TestCase {
override function setup() {
super.setup();
/*headerCode = "
var r = ~/a/;
var rg = ~/a/g;
var rg2 = ~/aa/g;
";*/
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("assertEq", Util.assertEq);
interp.variables.set("EReg", EReg);
return interp;
}
override function run() {
var r = new EReg("a", "");
var rg = new EReg("a", "g");
var rg2 = new EReg("aa", "g");
// TODO: implement better checking for regexes
//assertEq("new EReg('a', '');", r);
//assertEq("new EReg('a', 'g');", rg);
//assertEq("new EReg('aa', 'g');", rg2);
//@:privateAccess assertEq("new EReg('a', '').toString();", r.toString());
//@:privateAccess assertEq("new EReg('a', 'g').toString();", rg.toString());
//@:privateAccess assertEq("new EReg('aa', 'g').toString();", rg2.toString());
//assertEq(r.match("") == false);
//assertEq(r.match("b") == false);
//assertEq(r.match("a") == true);
//assertEq(r.matched(0) == "a");
//assertEq(r.matchedLeft() == "");
//assertEq(r.matchedRight() == "");
//var pos = r.matchedPos();
//assertEq(pos.pos == 0);
//assertEq(pos.len == 1);
headerCode = "var r = ~/a/;";
assertEq('[r.match(""), r.match("b"), r.match("a"), r.matched(0), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]', [r.match(""), r.match("b"), r.match("a"), r.matched(0), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]);
assertEq('[r.match("aa"), r.matched(0), r.match("a"), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]', [r.match("aa"), r.matched(0), r.match("a"), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]);
headerCode = "var rg = ~/a/g;";
assertEq('[rg.match("aa"), rg.matched(0), rg.matchedLeft(), rg.matchedRight(), {var pos = rg.matchedPos(); [pos.pos, pos.len];}]', [rg.match("aa"), rg.matched(0), rg.matchedLeft(), rg.matchedRight(), {var pos = rg.matchedPos(); [pos.pos, pos.len];}]);
headerCode = "var rg2 = ~/aa/g;";
assertEq('[rg2.match("aa"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]', [rg2.match("aa"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]);
assertEq('[rg2.match("AaaBaaC"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]', [rg2.match("AaaBaaC"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]);
headerCode = "";
// split
assertEq('~/a/.split("")', ~/a/.split(""));
assertEq('~/a/.split("a")', ~/a/.split("a"));
assertEq('~/a/.split("aa")', ~/a/.split("aa"));
assertEq('~/a/.split("b")', ~/a/.split("b"));
assertEq('~/a/.split("ab")', ~/a/.split("ab"));
assertEq('~/a/.split("ba")', ~/a/.split("ba"));
assertEq('~/a/.split("aba")', ~/a/.split("aba"));
assertEq('~/a/.split("bab")', ~/a/.split("bab"));
assertEq('~/a/.split("baba")', ~/a/.split("baba"));
// split + g
assertEq('~/a/g.split("")', ~/a/g.split(""));
assertEq('~/a/g.split("a")', ~/a/g.split("a"));
assertEq('~/a/g.split("aa")', ~/a/g.split("aa"));
assertEq('~/a/g.split("b")', ~/a/g.split("b"));
assertEq('~/a/g.split("ab")', ~/a/g.split("ab"));
assertEq('~/a/g.split("ba")', ~/a/g.split("ba"));
assertEq('~/a/g.split("aba")', ~/a/g.split("aba"));
assertEq('~/a/g.split("bab")', ~/a/g.split("bab"));
assertEq('~/a/g.split("baba")', ~/a/g.split("baba"));
// replace
assertEq('~/a/.replace("", "z")', ~/a/.replace("", "z"));
assertEq('~/a/.replace("a", "z")', ~/a/.replace("a", "z"));
assertEq('~/a/.replace("aa", "z")', ~/a/.replace("aa", "z"));
assertEq('~/a/.replace("b", "z")', ~/a/.replace("b", "z"));
assertEq('~/a/.replace("ab", "z")', ~/a/.replace("ab", "z"));
assertEq('~/a/.replace("ba", "z")', ~/a/.replace("ba", "z"));
assertEq('~/a/.replace("aba", "z")', ~/a/.replace("aba", "z"));
assertEq('~/a/.replace("bab", "z")', ~/a/.replace("bab", "z"));
assertEq('~/a/.replace("baba", "z")', ~/a/.replace("baba", "z"));
//Util.runKnownBug("Regex Syntax doesn't work", () -> {
assertCompiles("~/a/;");
assertCompiles("~/(a)a\\0\\//g;");
assertCompiles("~/(a)a\\0\\//g+5;");
assertError("~/(a)a\\0\\//ga;", Parser.getBaseError(ECustom("Invalid regex expression option \"a\"")));
//assertEq("~/a/+5", null);//~/a/+5);
//});
// replace + g
assertEq('~/a/g.replace("", "z")', ~/a/g.replace("", "z"));
assertEq('~/a/g.replace("a", "z")', ~/a/g.replace("a", "z"));
assertEq('~/a/g.replace("aa", "z")', ~/a/g.replace("aa", "z"));
assertEq('~/a/g.replace("b", "z")', ~/a/g.replace("b", "z"));
assertEq('~/a/g.replace("ab", "z")', ~/a/g.replace("ab", "z"));
assertEq('~/a/g.replace("ba", "z")', ~/a/g.replace("ba", "z"));
assertEq('~/a/g.replace("aba", "z")', ~/a/g.replace("aba", "z"));
assertEq('~/a/g.replace("bab", "z")', ~/a/g.replace("bab", "z"));
assertEq('~/a/g.replace("baba", "z")', ~/a/g.replace("baba", "z"));
// var 0 = 5; // Missing variable identifier
}
override function teardown() {
super.teardown();
}
}
-214
View File
@@ -1,214 +0,0 @@
package tests;
class StdCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("Std", Std);
interp.variables.set("String", String);
interp.variables.set("Bool", Bool);
interp.variables.set("Float", Float);
interp.variables.set("Array", Array);
interp.variables.set("Int", Int);
return interp;
}
override function run() {
var known:String = null; assertEq('var known:String = null; known is String;', known is String);
var unknown = null; assertEq('var unknown = null; unknown is String;', unknown is String);
assertEq('null is String;', null is String);
assertEq('"" is String;', ("" is String));
assertEq('false is Bool;', (false is Bool));
assertEq("1 is Int;", (1 is Int));
assertEq("1.5 is Int;", (1.5 is Int));
assertEq("1.5 is Float;", (1.5 is Float));
assertEq("[] is Array;", ([] is Array));
// isOfType
assertEq("var known:String = null; Std.isOfType(known, String);", Std.isOfType(known, String));
assertEq("var unknown = null; Std.isOfType(unknown, String);", Std.isOfType(unknown, String));
assertEq("Std.isOfType(null, String);", Std.isOfType(null, String));
assertEq("Std.isOfType('', String);", Std.isOfType("", String));
assertEq("Std.isOfType(false, Bool);", Std.isOfType(false, Bool));
assertEq("Std.isOfType(1, Int)", Std.isOfType(1, Int));
assertEq("Std.isOfType(1.5, Int);", Std.isOfType(1.5, Int));
assertEq("Std.isOfType(1.5, Float);", Std.isOfType(1.5, Float));
assertEq("Std.isOfType([], Array);", Std.isOfType([], Array));
// Std.isOfType(cast unit.MyEnum.A, Array) == false;
// instance
#if !js
assertEq("Std.downcast('', String)", Std.downcast("", String));
Std.downcast("", String) == "";
#end
var a = [];
assertEq("var a = []; Std.downcast(a, Array)", Std.downcast(a, Array));
assertEq("Std.downcast(null, Array)", Std.downcast(null, Array));
assertEq("Std.downcast(null, String)", Std.downcast(null, String));
// string
/*var cwts = new ClassWithToString();
var cwtsc = new ClassWithToStringChild();
var cwtsc2 = new ClassWithToStringChild2();
Std.string(cwts) == "ClassWithToString.toString()";
Std.string(cwtsc) == "ClassWithToString.toString()";
Std.string(cwtsc2) == "ClassWithToStringChild2.toString()";
Std.string(SomeEnum.NoArguments) == "NoArguments";
Std.string(SomeEnum.OneArgument("foo")) == "OneArgument(foo)";*/
assertEq("Std.string(null)", Std.string(null));
// int
assertEq("Std.int(-1.7)", Std.int(-1.7));
assertEq("Std.int(-1.2)", Std.int(-1.2));
assertEq("Std.int(-0.7)", Std.int(-0.7));
assertEq("Std.int(-0.2)", Std.int(-0.2));
assertEq("Std.int(0.7)", Std.int(0.7));
assertEq("Std.int(0.2)", Std.int(0.2));
// general
assertEq("Std.parseInt('0')", Std.parseInt("0"));
assertEq("Std.parseInt('-1')", Std.parseInt("-1"));
// preceeding zeroes
assertEq("Std.parseInt('0001')", Std.parseInt("0001"));
assertEq("Std.parseInt('0010')", Std.parseInt("0010"));
// trailing text
assertEq("Std.parseInt('100x123')", Std.parseInt("100x123"));
assertEq("Std.parseInt('12foo13')", Std.parseInt("12foo13"));
assertEq("Std.parseInt('23e2')", Std.parseInt("23e2"));
assertEq("Std.parseInt('0x10z')", Std.parseInt("0x10z"));
assertEq("Std.parseInt('0x10x123')", Std.parseInt("0x10x123"));
assertEq("Std.parseInt('0x10x123\\n')", Std.parseInt("0x10x123\n"));
assertEq("Std.parseInt('0xff\\n')", Std.parseInt("0xff\n"));
// hexadecimals
assertEq("Std.parseInt('0xff')", Std.parseInt("0xff"));
assertEq("Std.parseInt('0x123')", Std.parseInt("0x123"));
assertEq("Std.parseInt('0XFF')", Std.parseInt("0XFF"));
assertEq("Std.parseInt('0X123')", Std.parseInt("0X123"));
assertEq("Std.parseInt('0X01')", Std.parseInt("0X01"));
assertEq("Std.parseInt('0x01')", Std.parseInt("0x01"));
// signs
assertEq("Std.parseInt('123')", Std.parseInt("123"));
assertEq("Std.parseInt('+123')", Std.parseInt("+123"));
assertEq("Std.parseInt('-123')", Std.parseInt("-123"));
assertEq("Std.parseInt('0xa0')", Std.parseInt("0xa0"));
assertEq("Std.parseInt('+0xa0')", Std.parseInt("+0xa0"));
assertEq("Std.parseInt('-0xa0')", Std.parseInt("-0xa0"));
// whitespace: space, horizontal tab, newline, vertical tab, form feed, and carriage return
assertEq("Std.parseInt(' 5')", Std.parseInt(" 5"));
// whitespace and signs
assertEq("Std.parseInt(' 16')", Std.parseInt(" 16"));
assertEq("Std.parseInt(' -16')", Std.parseInt(" -16"));
assertEq("Std.parseInt(' +16')", Std.parseInt(" +16"));
assertEq("Std.parseInt(' 0x10')", Std.parseInt(" 0x10"));
assertEq("Std.parseInt(' -0x10')", Std.parseInt(" -0x10"));
assertEq("Std.parseInt(' +0x10')", Std.parseInt(" +0x10"));
// binary and octal unsupported
assertEq("Std.parseInt('010')", Std.parseInt("010"));
assertEq("Std.parseInt('0b10')", Std.parseInt("0b10"));
// null
assertEq("Std.parseInt(null)", Std.parseInt(null));
// no number
assertEq("Std.parseInt('')", Std.parseInt(""));
assertEq("Std.parseInt('abcd')", Std.parseInt("abcd"));
assertEq("Std.parseInt('a10')", Std.parseInt("a10"));
// invalid use of signs
assertEq("Std.parseInt('++123')", Std.parseInt("++123"));
assertEq("Std.parseInt('+-123')", Std.parseInt("+-123"));
assertEq("Std.parseInt('-+123')", Std.parseInt("-+123"));
assertEq("Std.parseInt('--123')", Std.parseInt("--123"));
assertEq("Std.parseInt('+ 123')", Std.parseInt("+ 123"));
assertEq("Std.parseInt('- 123')", Std.parseInt("- 123"));
assertEq("Std.parseInt('++0x123')", Std.parseInt("++0x123"));
assertEq("Std.parseInt('+-0x123')", Std.parseInt("+-0x123"));
assertEq("Std.parseInt('-+0x123')", Std.parseInt("-+0x123"));
assertEq("Std.parseInt('--0x123')", Std.parseInt("--0x123"));
assertEq("Std.parseInt('+ 0x123')", Std.parseInt("+ 0x123"));
assertEq("Std.parseInt('- 0x123')", Std.parseInt("- 0x123"));
// hexadecimal prefix with no number
//unspec(Std.parseInt.bind("0x"));
//unspec(Std.parseInt.bind("0x C"));
//unspec(Std.parseInt.bind("0x+A"));
// parseFloat
// general
assertEq("Std.parseFloat('0')", Std.parseFloat('0'));
assertEq("Std.parseFloat('0.0')", Std.parseFloat('0.0'));
// preceeding zeroes
assertEq("Std.parseFloat('0001')", Std.parseFloat('0001'));
assertEq("Std.parseFloat('0010')", Std.parseFloat('0010'));
// trailing text
assertEq("Std.parseFloat('100x123')", Std.parseFloat('100x123'));
assertEq("Std.parseFloat('12foo13')", Std.parseFloat('12foo13'));
assertEq("Std.parseFloat('5.3 ')", Std.parseFloat('5.3 '));
assertEq("Std.parseFloat('5.3 1')", Std.parseFloat('5.3 1'));
// signs
assertEq("Std.parseFloat('123.45')", Std.parseFloat('123.45'));
assertEq("Std.parseFloat('+123.45')", Std.parseFloat('+123.45'));
// whitespace: space, horizontal tab, newline, vertical tab, form feed, and carriage return
// whitespace and signs
assertEq("Std.parseFloat(' 1.6')", Std.parseFloat(' 1.6'));
assertEq("Std.parseFloat(' -1.6')", Std.parseFloat(' -1.6'));
assertEq("Std.parseFloat(' +1.6')", Std.parseFloat(' +1.6'));
// exponent
assertEq("Std.parseFloat('2.426670815e12')", Std.parseFloat('2.426670815e12'));
assertEq("Std.parseFloat('2.426670815E12')", Std.parseFloat('2.426670815E12'));
assertEq("Std.parseFloat('2.426670815e+12')", Std.parseFloat('2.426670815e+12'));
assertEq("Std.parseFloat('2.426670815E+12')", Std.parseFloat('2.426670815E+12'));
assertEq("Std.parseFloat('2.426670815e-12')", Std.parseFloat('2.426670815e-12'));
assertEq("Std.parseFloat('2.426670815E-12')", Std.parseFloat('2.426670815E-12'));
#if !interp
assertEq("Std.parseFloat('6e')", Std.parseFloat('6e'));
assertEq("Std.parseFloat('6e')", Std.parseFloat('6e'));
#end
// null
assertEq("Math.isNaN(Std.parseFloat(null))", Math.isNaN(Std.parseFloat(null)));
// no number
assertEq("Math.isNaN(Std.parseFloat(''))", Math.isNaN(Std.parseFloat("")));
assertEq("Math.isNaN(Std.parseFloat('abcd'))", Math.isNaN(Std.parseFloat("abcd")));
assertEq("Math.isNaN(Std.parseFloat('a10'))", Math.isNaN(Std.parseFloat("a10")));
// invalid use of signs
assertEq("Math.isNaN(Std.parseFloat('++12.3'))", Math.isNaN(Std.parseFloat("++12.3")));
assertEq("Math.isNaN(Std.parseFloat('+-12.3'))", Math.isNaN(Std.parseFloat("+-12.3")));
assertEq("Math.isNaN(Std.parseFloat('-+12.3'))", Math.isNaN(Std.parseFloat("-+12.3")));
assertEq("Math.isNaN(Std.parseFloat('--12.3'))", Math.isNaN(Std.parseFloat("--12.3")));
assertEq("Math.isNaN(Std.parseFloat('+ 12.3'))", Math.isNaN(Std.parseFloat("+ 12.3")));
assertEq("Math.isNaN(Std.parseFloat('- 12.3'))", Math.isNaN(Std.parseFloat("- 12.3")));
// random
// var x = Std.random(2);
// //x in [0,1];
// Std.random(1) == 0;
// Std.random(0) == 0;
// Std.random(-100) == 0;
Util.runKnownBug("Not all escape sequences are supported", () -> {
assertEq("Std.parseInt(' \\t\\n\\x0b\\x0c\\r16')", Std.parseInt(" \t\n\x0b\x0c\r16"));
assertEq("Std.parseInt(' \\t\\n\\x0b\\x0c\\r0xa')", Std.parseInt(" \t\n\x0b\x0c\r0xa"));
assertEq("Std.parseFloat(' \\t\\n\\x0b\\x0c\\r1.6')", Std.parseFloat('\t\n\x0b\x0c\r1.6'));
});
}
override function teardown() {
super.teardown();
}
}
-34
View File
@@ -1,34 +0,0 @@
package tests;
class StringBufCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("Std", Std);
interp.variables.set("StringBuf", StringBuf);
return interp;
}
override function run() {
var x = new StringBuf();
x.addSub("abcdefg", 1);
assertEq('var x = new StringBuf(); x.addSub("abcdefg", 1); x.toString()', x.toString());
Util.runKnownBug("Emojis dont work", () -> {
var x = new StringBuf();
x.add("👽");
assertEq('var x = new StringBuf(); x.add("👽"); x.toString()', x.toString());
});
var x = new StringBuf();
x.add(0x1F47D);
assertEq('var x = new StringBuf(); x.add(0x1F47D); x.toString()', x.toString());
}
override function teardown() {
super.teardown();
}
}
-180
View File
@@ -1,180 +0,0 @@
package tests;
class StringCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("Std", Std);
interp.variables.set("String", String);
return interp;
}
override function run() {
assertEq('"foo"', "foo");
var str = "foo";
assertEq('var str = "foo"; str == new String(str)', str == new String(str));
assertEq('"foo".toUpperCase()', "foo".toUpperCase());
assertEq('"_bar".toUpperCase()', "_bar".toUpperCase());
assertEq('"123b".toUpperCase()', "123b".toUpperCase());
assertEq('"".toUpperCase()', "".toUpperCase());
assertEq('"A".toUpperCase()', "A".toUpperCase());
assertEq('"FOO".toLowerCase()', "FOO".toLowerCase());
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
assertEq('"123B".toLowerCase()', "123B".toLowerCase());
assertEq('"".toLowerCase()', "".toLowerCase());
assertEq('"a".toLowerCase()', "a".toLowerCase());
headerCode = 'var s = "foo1bar";';
var s = "foo1bar";
assertEq("s.charAt(0)", s.charAt(0));
assertEq("s.charAt(1)", s.charAt(1));
assertEq("s.charAt(2)", s.charAt(2));
assertEq("s.charAt(3)", s.charAt(3));
assertEq("s.charAt(4)", s.charAt(4));
assertEq("s.charAt(5)", s.charAt(5));
assertEq("s.charAt(6)", s.charAt(6));
assertEq("s.charAt(7)", s.charAt(7));
assertEq("s.charAt(-1)", s.charAt(-1));
assertEq('"".charAt(0)', "".charAt(0));
assertEq('"".charAt(1)', "".charAt(1));
assertEq('"".charAt(-1)', "".charAt(-1));
headerCode = 'var s = "foo1bar";';
var s = "foo1bar";
assertEq("s.charCodeAt(0)", s.charCodeAt(0));
assertEq("s.charCodeAt(1)", s.charCodeAt(1));
assertEq("s.charCodeAt(2)", s.charCodeAt(2));
assertEq("s.charCodeAt(3)", s.charCodeAt(3));
assertEq("s.charCodeAt(4)", s.charCodeAt(4));
assertEq("s.charCodeAt(5)", s.charCodeAt(5));
assertEq("s.charCodeAt(6)", s.charCodeAt(6));
assertEq("s.charCodeAt(7)", s.charCodeAt(7));
assertEq("s.charCodeAt(-1)", s.charCodeAt(-1));
headerCode = '';
assertEq('("3" > "11")', ("3" > "11"));
assertEq('(" 3" < "3")', (" 3" < "3"));
assertEq('("a" < "b")', ("a" < "b"));
assertEq('("a" <= "b")', ("a" <= "b"));
assertEq('("a" > "b")', ("a" > "b"));
assertEq('("a" >= "b")', ("a" >= "b"));
assertEq('"foo".toUpperCase()', "foo".toUpperCase());
assertEq('"_bar".toUpperCase()', "_bar".toUpperCase());
assertEq('"123b".toUpperCase()', "123b".toUpperCase());
assertEq('"".toUpperCase()', "".toUpperCase());
assertEq('"A".toUpperCase()', "A".toUpperCase());
assertEq('"FOO".toLowerCase()', "FOO".toLowerCase());
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
assertEq('"123B".toLowerCase()', "123B".toLowerCase());
assertEq('"".toLowerCase()', "".toLowerCase());
assertEq('"a".toLowerCase()', "a".toLowerCase());
assertEqPrintable("' \\t\\n\\x0b\\x0c\\r16'", " \t\n\x0b\x0c\r16");
assertEqPrintable("' \\t\\n\\x0b\\x0c\\r0xa'", " \t\n\x0b\x0c\r0xa");
assertEqPrintable("' \\t\\n\\x0b\\x0c\\r1.6'", ' \t\n\x0b\x0c\r1.6');
assertEqPrintable("'The \\x54\\141b\\tch\\141r\\141ct\\145r:\\n'", 'The \x54\141b\tch\141r\141ct\145r:\n');
assertEqPrintable("'\\t\\\"\\101scii\\\"\\n'", '\t\"\101scii\"\n');
assertEqPrintable("'\\tcontains\\n'", '\tcontains\n');
assertEqPrintable("'\\tspecial\\\\backslash\\\\codes,\\n'", '\tspecial\\backslash\\codes,\n');
assertEqPrintable("'\\tdouble quotes \\\"like this\\\",\\n'", '\tdouble quotes \"like this\",\n');
assertEqPrintable("'\\tsingle quotes \\'and this\\',\\n'", '\tsingle quotes \'and this\',\n');
assertEqPrintable("'\\tASCII bell\\x07and others.\\n'", '\tASCII bell\x07and others.\n');
assertEqPrintable("'Unicode samples: Greek \\u03B1 (alpha), smiley \\u263A,\\n'", 'Unicode samples: Greek \u03B1 (alpha), smiley \u263A,\n');
assertEqPrintable("'regional indicators \\u{1F1FA}\\u{1F1F8}, and musical note \\u{1F3B5}.'", 'regional indicators \u{1F1FA}\u{1F1F8}, and musical note \u{1F3B5}.');
assertEqPrintable("'\\u{10FFFF}\\u{1F1FA}\\u{3042}\\u{3B1}\\u{F1}\\u{A}'", '\u{10FFFF}\u{1F1FA}\u{3042}\u{3B1}\u{F1}\u{A}');
//trace(hscript.Printer.getEscapedString("\u{10FFFF}\u{1F1FA}\u{3042}\u{3B1}\u{F1}\u{A}"));
assertEq('"\\""', "\"");
assertEq("'\\''", '\'');
assertEq("'\\\\'", '\\');
assertEq("'\\n'", '\n');
assertEq("'\\r'", '\r');
assertEq("'\\t'", '\t');
assertEq("'\\101'", '\101');
assertEq("'/'", '/');
assertEq('"$${5}"', "${5}");
assertEq('"$${5},$${({})}"', "${5},${({})}");
assertEq('"$${5},$${\\"Hello\\"}"', "${5},${\"Hello\"}");
assertEq('"$${5},$${\'Hello\'}"', "${5},${'Hello'}");
assertEq("'$${({})}'", '$${({})}');
assertEq("'${5},${({})}'", '${5},${({})}');
assertEq("'${5},${\"Hello\"}'", '${5},${"Hello"}');
assertEq("'${5},${'Hello'}'", '${5},${'Hello'}');
assertEq("'$${5}'", '$${5}');
assertEq("'${5}'", '${5}');
assertEq("'${'${6}world'}'", '${'${6}world'}');
var a = 5;
headerCode = 'var a = 5;';
assertEq("'$a'", '$a');
assertEq("'$$a'", '$$a');
assertEq("'$ '", '$ ');
assertEq("'$0'", '$0');
assertEq("'$a+5 Hello ${a+5}'", '$a+5 Hello ${a+5}');
//assertEq("'$a+5 Hello ${a+5}'", "" + a + "+5 Hello " + a + 5);
headerCode = '';
assertEq("'hello ${5} world'", 'hello ${5} world');
assertEq("'hello ${5}'", 'hello ${5}');
assertEq('"f".code', "f".code);
assertEq('"o".code', "o".code);
assertEq('"1".code', "1".code);
assertEq('"b".code', "b".code);
assertEq('"a".code', "a".code);
assertEq('"r".code', "r".code);
assertEq('"r".code', "r".code);
assertError('"foo".code', Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI))); // multiple chars causes error
assertError('"bar".code', Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI))); // multiple chars causes error
headerCode = '';
var a = 103;
assertEq('String.fromCharCode(65)', String.fromCharCode(65));
assertEq('String.fromCharCode(97)', String.fromCharCode(97));
assertEq('String.fromCharCode(98)', String.fromCharCode(98));
assertEq('String.fromCharCode(99)', String.fromCharCode(99));
assertEq('String.fromCharCode(100)', String.fromCharCode(100));
assertEq('String.fromCharCode(101)', String.fromCharCode(101));
assertEq('String.fromCharCode(102)', String.fromCharCode(102));
assertEq('String.fromCharCode(103)', String.fromCharCode(103));
assertEq('var a = 103; String.fromCharCode(a)', String.fromCharCode(a));
assertError("'hello ${}'", Parser.getBaseError(EPreset(EMPTY_INTERPOLATION)));
assertEq('"".charAt(0)', "".charAt(0));
assertEq('"".charAt(1)', "".charAt(1));
assertEq('"".charAt(-1)', "".charAt(-1));
headerCode = '';
assertEq("'foo1bar'.charCodeAt(0)", 'foo1bar'.charCodeAt(0));
assertEq("'foo1bar'.charCodeAt(1)", 'foo1bar'.charCodeAt(1));
assertEq("'foo1bar'.charCodeAt(2)", 'foo1bar'.charCodeAt(2));
assertEq("'foo1bar'.charCodeAt(3)", 'foo1bar'.charCodeAt(3));
assertEq("'foo1bar'.charCodeAt(4)", 'foo1bar'.charCodeAt(4));
assertEq("'foo1bar'.charCodeAt(5)", 'foo1bar'.charCodeAt(5));
assertEq("'foo1bar'.charCodeAt(6)", 'foo1bar'.charCodeAt(6));
assertEq("'foo1bar'.charCodeAt(7)", 'foo1bar'.charCodeAt(7));
assertEq("'foo1bar'.charCodeAt(-1)", 'foo1bar'.charCodeAt(-1));
assertEq("'foo1bar'.length", 'foo1bar'.length);
assertEq("'foo1bar'.split('1')", 'foo1bar'.split('1'));
}
override function teardown() {
super.teardown();
}
}
-25
View File
@@ -1,25 +0,0 @@
package tests;
using StringTools;
class StringToolsCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("Std", Std);
interp.variables.set("String", String);
return interp;
}
override function run() {
Util.runKnownBug("Using StringTools; not working", () -> {
});
}
override function teardown() {
super.teardown();
}
}
-38
View File
@@ -1,38 +0,0 @@
package tests;
class SwitchCase extends TestCase {
override function setup() {
super.setup();
}
override function run() {
assertEq("switch(1) { case 1: 1; case 2: 2; default: -1; }", switch(1) { case 1: 1; case 2: 2; default: -1; });
assertEq("switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; });
assertEq("switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; });
assertEq("switch(6) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(6) { case 1: 1; case 2: 2; case 3: 3; default: -1; });
// TODO: add https://haxe.org/manual/lf-pattern-matching-enums.html
// TODO: add https://haxe.org/manual/lf-pattern-matching-variable-capture.html (including case _)
// TODO: add https://haxe.org/manual/lf-pattern-matching-structure.html
// TODO: add https://haxe.org/manual/lf-pattern-matching-array.html
// TODO: add https://haxe.org/manual/lf-pattern-matching-guards.html
// TODO: add https://haxe.org/manual/lf-pattern-matching-tuples.html
// TODO: add https://haxe.org/manual/lf-pattern-matching-extractors.html
// TODO: detect https://haxe.org/manual/lf-pattern-matching-exhaustiveness.html
// TODO: maybe? https://haxe.org/manual/lf-pattern-matching-unused.html
// TODO: add https://haxe.org/manual/lf-pattern-matching-single.html
assertEq("switch(5) { case 1|4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1|4: 'error'; case 5: 0; default: -1; });
assertEq("switch(5) { case 1|4: 'error'; default: -1; }", switch(5) { case 1|4: 'error'; default: -1; });
assertEq("switch(5) { case 1,4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1,4: 'error'; case 5: 0; default: -1; });
Util.runKnownBug("Switch statement with multiple cases in parentheses", () -> {
assertEq("switch(5) { case (1|4): 'error'; default: -1; }", switch(5) { case (1|4): 'error'; default: -1; });
assertEq("switch(6) { case (1|4|(6|7)): 6; default: -1; }", switch(6) { case (1|4|(6|7)): 6; default: -1; });
});
}
override function teardown() {
super.teardown();
}
}
-93
View File
@@ -1,93 +0,0 @@
package tests;
import hscript.Printer;
import hscript.Tools;
import hscript.Expr.Error;
@:access(hscript.Interp)
@:access(hscript.Parser)
class TestCase extends HScriptRunner {
public function assertEq(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
if(message == null)
message = script;
var result = if(vars != null)
executeWithVars(script, vars);
else
execute(script);
if(!Util.assertEq(result, expected, message, pos)) {
Sys.println("> " + Printer.convertExprToString(lastExpr));
return false;
}
return true;
}
public function assertCompiles(script:String, ?message:String, ?pos:haxe.PosInfos) {
if(message == null)
message = script;
try {
var result = Util.parseUnsafe(script);
} catch(e:Error) {
var e = Printer.getPrintableError(e);
Sys.println("# Error trying to compile: " + script);
Sys.println("## Error: " + e);
return Util.failed();
}
return Util.passed();
}
public function assertError(script:String, expectedError:Error, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
var expectedError = Printer.getPrintableError(expectedError);
if(message == null)
message = script;
try {
var result = if(vars != null)
executeWithVarsUnsafe(script, vars);
else
executeUnsafe(script);
Sys.println("# For script: " + script);
Sys.println("## Expected error: " + expectedError);
Sys.println("## Got result: " + result);
Sys.println("> " + Printer.convertExprToString(lastExpr));
return Util.failed();
} catch(e:hscript.Error) {
var e = Printer.getPrintableError(e);
if(Type.enumEq(e, expectedError)) {
return Util.passed();
} else {
Sys.println("# For script: " + script);
Sys.println("## Expected error: " + expectedError);
Sys.println("## Actual error: " + e);
return Util.failed();
}
}
return Util.failed();
}
public function assertEqPrintable(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
if(message == null)
message = script;
var result = if(vars != null)
executeWithVars(script, vars);
else
execute(script);
if(!Util.assertEqPrintable(result, expected, message, pos)) {
Sys.println("> " + Printer.convertExprToString(lastExpr));
return false;
}
return true;
}
public function assertNeq(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
if(message == null)
message = script;
var result = if(vars != null)
executeWithVars(script, vars);
else
execute(script);
if(!Util.assertNeq(result, expected, message, pos)) {
Sys.println("> " + Printer.convertExprToString(lastExpr));
return false;
}
return true;
}
}
-25
View File
@@ -1,25 +0,0 @@
# Generic Haxe stuffs to make the project compile
-cp src
-cp ../
-D analyzer-optimize
-main Main
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
# --library hscript-improved
# --library hxcpp-debug-server
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
--define hscriptPos
--define HXCPP_CHECK_POINTER
--define HXCPP_STACK_LINE
--define HXCPP_DEBUG_LINK
--dce no
# File Specific Defines
--define windows
# Copying the assets from source code to bin folder
# --cmd Xcopy assets\ bin\cpp\assets\ /E /H /C /I /Y
--cpp bin/cpp
--cmd cd bin/cpp
--cmd Main.exe