Compare commits
59
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ad52138fe | ||
|
|
a616f2affe | ||
|
|
6721a838ca | ||
|
|
826f0184bf | ||
|
|
6c20ad3a8f | ||
|
|
fd5a2d9143 | ||
|
|
8da9ed942d | ||
|
|
f31f380602 | ||
|
|
85ea6c6f44 | ||
|
|
6daea2fb5b | ||
|
|
bc8a59877e | ||
|
|
4347fe9305 | ||
|
|
a41222328a | ||
|
|
080c441f24 | ||
|
|
d6d6e7a45e | ||
|
|
cb645d6bb1 | ||
|
|
5d58ea56c9 | ||
|
|
4170a4aafc | ||
|
|
19021058c4 | ||
|
|
15bd6b038a | ||
|
|
4d274954e3 | ||
|
|
aff2d0766d | ||
|
|
38634e1528 | ||
|
|
5ef937d6d4 | ||
|
|
22bbe04f77 | ||
|
|
18382c4621 | ||
|
|
ad93c1bc18 | ||
|
|
238c24501b | ||
|
|
d38ddbb2b4 | ||
|
|
fb8f35e2a5 | ||
|
|
6c77566fea | ||
|
|
b7c12943fa | ||
|
|
50e87c504d | ||
|
|
aaac98d7b0 | ||
|
|
b6df96a4b0 | ||
|
|
c0d4540d4b | ||
|
|
ad05952685 | ||
|
|
340d63003f | ||
|
|
177bb9f75b | ||
|
|
e876db00e4 | ||
|
|
cd3bc5b194 | ||
|
|
b13d449b78 | ||
|
|
006b0e1f32 | ||
|
|
0ca6eab16e | ||
|
|
df1c5ad97c | ||
|
|
8c1aaf5454 | ||
|
|
9288a9df10 | ||
|
|
a727148447 | ||
|
|
a16e74dee6 | ||
|
|
fdde089908 | ||
|
|
6346f82edf | ||
|
|
10dd51ebe8 | ||
|
|
e97c79d14c | ||
|
|
418843c606 | ||
|
|
049daf4b0d | ||
|
|
b105ac0453 | ||
|
|
d2aa99eff7 | ||
|
|
934848ff8d | ||
|
|
f512ac4cde |
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
/hscript.swf
|
/hscript.swf
|
||||||
/release.zip
|
/release.zip
|
||||||
dump/*
|
|
||||||
tests/bin/*
|
tests/bin/*
|
||||||
@@ -3,7 +3,7 @@ hscript-improved
|
|||||||
|
|
||||||
How to install
|
How to install
|
||||||
```
|
```
|
||||||
haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
|
haxelib git hscript-improved https://github.com/FNF-CNE-Devs/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
|
||||||
|
|||||||
+9
-5
@@ -26,17 +26,18 @@ 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);
|
||||||
@@ -57,7 +58,6 @@ 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,7 +81,11 @@ 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
@@ -1,4 +1,4 @@
|
|||||||
--macro keep('IntIterator')
|
--macro keep('IntIterator')
|
||||||
--macro hscript.macros.AbstractHandler.init()
|
--macro hscript.macros.UsingHandler.init()
|
||||||
--macro hscript.macros.UsingMacro.init()
|
|
||||||
--macro hscript.macros.ClassExtendMacro.init()
|
--macro hscript.macros.ClassExtendMacro.init()
|
||||||
|
--define HSCRIPT_NEW
|
||||||
|
|||||||
+7
-7
@@ -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, opt: false, value: null }], e, name), e);
|
return mk(EFunction([{ name : arg, t : 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, opt: false, value: null }], e, name), e);
|
return mk(EFunction([for( a in arg ) { name : a, t : 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, opt: false, value: null } );
|
args.unshift( { name : "_onEnd", t : 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:Array<SwitchCase> = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
|
var cases = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, 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 );
|
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 );
|
||||||
case EThrow(v):
|
case EThrow(v):
|
||||||
return toCps(v, mk(EFunction([ { name : "_v", t : null, opt: false, value: null } ], mk(EThrow(v),v)), v), exit);
|
return toCps(v, mk(EFunction([ { name : "_v", t : 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 = [];
|
var funs = new Array();
|
||||||
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 });
|
||||||
|
|||||||
+26
-17
@@ -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 : Map<String,Int>;
|
var hstrings : #if haxe3 Map<String,Int> #else Hash<Int> #end;
|
||||||
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 = new Map();
|
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
||||||
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 = new Map();
|
hstrings = #if haxe3 new Map() #else new Hash() #end;
|
||||||
nstrings = 1;
|
nstrings = 1;
|
||||||
}
|
}
|
||||||
hstrings.set(v,nstrings);
|
hstrings.set(v,nstrings);
|
||||||
@@ -85,6 +85,15 @@ 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));
|
||||||
@@ -111,6 +120,13 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -124,17 +140,10 @@ 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):
|
||||||
@@ -269,7 +278,7 @@ class Bytes {
|
|||||||
case 3:
|
case 3:
|
||||||
EParent(doDecode());
|
EParent(doDecode());
|
||||||
case 4:
|
case 4:
|
||||||
var a = [];
|
var a = new Array();
|
||||||
for( i in 0...bin.get(pin++) )
|
for( i in 0...bin.get(pin++) )
|
||||||
a.push(doDecode());
|
a.push(doDecode());
|
||||||
EBlock(a);
|
EBlock(a);
|
||||||
@@ -286,7 +295,7 @@ class Bytes {
|
|||||||
EUnop(op,prefix,doDecode());
|
EUnop(op,prefix,doDecode());
|
||||||
case 8:
|
case 8:
|
||||||
var e = doDecode();
|
var e = doDecode();
|
||||||
var params = [];
|
var params = new Array();
|
||||||
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);
|
||||||
@@ -308,7 +317,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(), opt: false, value: null, t: null });
|
params.push({ name : doDecodeString() });
|
||||||
var e = doDecode();
|
var e = doDecode();
|
||||||
var name = doDecodeString();
|
var name = doDecodeString();
|
||||||
EFunction(params,e,(name == "") ? null: name);
|
EFunction(params,e,(name == "") ? null: name);
|
||||||
@@ -318,13 +327,13 @@ class Bytes {
|
|||||||
var e = doDecode();
|
var e = doDecode();
|
||||||
EArray(e,doDecode());
|
EArray(e,doDecode());
|
||||||
case 17:
|
case 17:
|
||||||
var el = [];
|
var el = new Array();
|
||||||
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 = [];
|
var el = new Array();
|
||||||
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);
|
||||||
@@ -335,7 +344,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:Array<ObjectField> = [];
|
var fl = new Array();
|
||||||
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();
|
||||||
@@ -349,7 +358,7 @@ class Bytes {
|
|||||||
ETernary(cond, e1, e2);
|
ETernary(cond, e1, e2);
|
||||||
case 23:
|
case 23:
|
||||||
var e = doDecode();
|
var e = doDecode();
|
||||||
var cases:Array<SwitchCase> = [];
|
var cases = [];
|
||||||
while( true ) {
|
while( true ) {
|
||||||
var v = doDecode();
|
var v = doDecode();
|
||||||
if( v == null ) break;
|
if( v == null ) break;
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ 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 = [
|
||||||
@@ -26,8 +21,4 @@ class Config {
|
|||||||
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public static final DISALLOW_USING = [
|
|
||||||
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
+43
-133
@@ -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,146 +23,104 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
|||||||
this.extend = extend;
|
this.extend = extend;
|
||||||
this.interfaces = interfaces;
|
this.interfaces = interfaces;
|
||||||
|
|
||||||
//this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
|
this.cl = extend == null ? TemplateClass : 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 {
|
||||||
// TODO: clean this up, it sucks, i hate it
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
// TODO: make static vars work correctly
|
throw "todo: do all this lmao -lunar";
|
||||||
|
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);
|
||||||
|
|
||||||
_class.__real_fields = disallowCopy;
|
for (key => value in capturedLocals) {
|
||||||
|
if(!disallowCopy.contains(key)) {
|
||||||
// todo: make it so you can use variables from the same scope as where the class was defined
|
interp.locals.set(key, {r: value, depth: -1});
|
||||||
|
}
|
||||||
//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
|
|
||||||
|
|
||||||
//trace("Before: " + [for(key => value in interp.variables) key]);
|
var comparisonMap = new Map();
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
//trace("After: " + [for(key => value in interp.variables) key]);
|
interp.variables.set("super", staticHandler);
|
||||||
|
|
||||||
|
_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 = [
|
var classVariables = [for(key => value in interp.variables) if(!comparisonMap.exists(key) || comparisonMap[key] != value) key => value];
|
||||||
for(key => value in interp.variables)
|
for(variable => value in classVariables) {
|
||||||
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
|
if(variable == "this" || variable == "super") continue;
|
||||||
key => value
|
@:privateAccess
|
||||||
];
|
if(!interp.__instanceFields.contains(variable)) {
|
||||||
//for(variable => value in classVariables) {
|
interp.__instanceFields.push(variable);
|
||||||
// if(variable == "this" || variable == "super" || variable == "new") continue;
|
}
|
||||||
// @:privateAccess
|
}
|
||||||
// if(!interp.__instanceFields.contains(variable)) {
|
|
||||||
// interp.__instanceFields.push(variable);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//_class.__class__fields = [for(key => value in classVariables) key];
|
//trace([for(key => value in classVariables) key]);
|
||||||
|
|
||||||
//trace(_class.__class__fields);
|
|
||||||
//@:privateAccess
|
//@:privateAccess
|
||||||
//trace(interp.__instanceFields);
|
//trace(interp.__instanceFields);
|
||||||
|
|
||||||
//_class.__interp = interp;
|
_class.__allowSetGet = false;
|
||||||
//_class.__allowSetGet = false;
|
|
||||||
interp.scriptObject = _class;
|
|
||||||
|
|
||||||
for(variable => value in interp.variables) {
|
for(variable => value in interp.variables) {
|
||||||
if(variable == "this" || variable == "super" || variable == "new") continue;
|
if(variable == "this") 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 CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
|
class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour {
|
||||||
public var __interp:Interp;
|
public var __interp:Interp; // TODO: Remove
|
||||||
public var __customClass:hscript.customclass.CustomClass;
|
|
||||||
public var __allowSetGet:Bool = true;
|
|
||||||
public var __custom__variables:Map<String, Dynamic>;
|
public var __custom__variables:Map<String, Dynamic>;
|
||||||
public var __real_fields:Array<String>;
|
public var __allowSetGet:Bool = true;
|
||||||
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))
|
||||||
@@ -171,19 +129,14 @@ class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHS
|
|||||||
__custom__variables.set(name, val);
|
__custom__variables.set(name, val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
if(__real_fields.contains(name)) {
|
|
||||||
UnsafeReflect.setProperty(this, name, val);
|
UnsafeReflect.setProperty(this, name, val);
|
||||||
return UnsafeReflect.field(this, name);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,49 +155,6 @@ class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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() {}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+293
-141
@@ -21,13 +21,13 @@
|
|||||||
*/
|
*/
|
||||||
package hscript;
|
package hscript;
|
||||||
|
|
||||||
typedef Int8 = #if cpp cpp.Int8 #elseif java java.Int8 #elseif cs cs.Int8 #else Int #end;
|
typedef Int8 = #if cpp cpp.Int8 #else Int #end;
|
||||||
typedef Int16 = #if cpp cpp.Int16 #elseif java java.Int16 #elseif cs cs.Int16 #else Int #end;
|
typedef Int16 = #if cpp cpp.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 #elseif java java.Int64 #elseif cs cs.Int64 #else Int #end;
|
typedef Int64 = #if cpp cpp.Int64 #else Int #end;
|
||||||
|
|
||||||
typedef UInt8 = #if cpp cpp.UInt8 #elseif cs cs.UInt8 #else Int #end;
|
typedef UInt8 = #if cpp cpp.UInt8 #else Int #end;
|
||||||
typedef UInt16 = #if cpp cpp.UInt16 #elseif cs cs.UInt16 #else Int #end;
|
typedef UInt16 = #if cpp cpp.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,102 +35,29 @@ 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
|
||||||
@:structInit
|
class Expr {
|
||||||
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;
|
||||||
@@ -138,35 +65,277 @@ class Error {
|
|||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
}
|
}
|
||||||
public function toString(): String {
|
|
||||||
return Printer.errorToString(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
enum ErrorDef {
|
enum ExprDef
|
||||||
#else
|
#else
|
||||||
enum Error {
|
typedef ExprDef = Expr;
|
||||||
|
enum Expr
|
||||||
#end
|
#end
|
||||||
EInvalidChar( c : Int );
|
{
|
||||||
EUnexpected( s : String );
|
EConst( c : Const );
|
||||||
EUnterminatedString;
|
EIdent( v : VarN );
|
||||||
EUnterminatedComment;
|
EVar( n : VarN, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool );
|
||||||
EInvalidPreprocessor( msg : String );
|
EParent( e : Expr, ?noOptimize : Bool );
|
||||||
EUnknownVariable( v : String );
|
EBlock( e : Array<Expr> );
|
||||||
EInvalidIterator( v : String );
|
EField( e : Expr, f : String , ?safe : Bool );
|
||||||
EInvalidOp( op : String );
|
EBinop( op : Binop, e1 : Expr, e2 : Expr );
|
||||||
EInvalidAccess( f : String );
|
EUnop( op : Unop, prefix : Bool, e : Expr );
|
||||||
ECustom( msg : String );
|
ECall( e : Expr, params : Array<Expr> );
|
||||||
EInvalidClass( className : String);
|
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
|
||||||
EAlreadyExistingClass( className : String);
|
EWhile( cond : Expr, e : Expr );
|
||||||
|
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, ?asname:String );
|
DImport( path : Array<String>, ?everything : Bool );
|
||||||
DUsing( path : Array<String>);
|
|
||||||
DTypedef( c : TypeDecl );
|
|
||||||
DClass( c : ClassDecl );
|
DClass( c : ClassDecl );
|
||||||
|
DTypedef( c : TypeDecl );
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ModuleType = {
|
typedef ModuleType = {
|
||||||
@@ -208,38 +377,21 @@ enum FieldKind {
|
|||||||
KVar( v : VarDecl );
|
KVar( v : VarDecl );
|
||||||
}
|
}
|
||||||
|
|
||||||
@:structInit
|
typedef FunctionDecl = {
|
||||||
final class FunctionDecl {
|
var args : Array<Argument>;
|
||||||
public var args : Array<Argument>;
|
var expr : Expr;
|
||||||
public var body : Expr;
|
var ret : Null<CType>;
|
||||||
public var ret : Null<CType>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef VarDecl = {
|
typedef VarDecl = {
|
||||||
var get : FieldPropertyAccess;
|
var get : Null<String>;
|
||||||
var set : FieldPropertyAccess;
|
var set : Null<String>;
|
||||||
var isFinal : Bool;
|
|
||||||
var expr : Null<Expr>;
|
var expr : Null<Expr>;
|
||||||
var type : Null<CType>;
|
var type : Null<CType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef TypeParamDecl = {};
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
abstract VarN(Dynamic) from Int from String to Int to String {}
|
||||||
enum EnumType {
|
#else
|
||||||
ESimple(name: String);
|
typedef VarN = String;
|
||||||
EConstructor(name: String, args: Array<Argument>);
|
#end
|
||||||
}
|
|
||||||
|
|
||||||
enum abstract FieldPropertyAccess(UInt8) {
|
|
||||||
var ADefault;
|
|
||||||
var ANull;
|
|
||||||
var AGet;
|
|
||||||
var ASet;
|
|
||||||
var ADynamic;
|
|
||||||
var ANever;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AccessContext {
|
|
||||||
CInner(cl:String);
|
|
||||||
COuter;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,16 @@
|
|||||||
package hscript;
|
package hscript;
|
||||||
|
|
||||||
|
import hscript.Interp;
|
||||||
|
|
||||||
interface IHScriptCustomClassBehaviour {
|
interface IHScriptCustomClassBehaviour {
|
||||||
public var __customClass:hscript.customclass.CustomClass;
|
public var __interp:Interp; // TODO: Remove
|
||||||
public var __real_fields:Array<String>;
|
public var __custom__variables:Map<String, Dynamic>;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
+804
-1252
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
|||||||
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"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
+54
-4
@@ -29,13 +29,23 @@ 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);
|
||||||
@@ -63,7 +73,9 @@ 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
|
||||||
@@ -90,8 +102,15 @@ 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 = [];
|
var b = new Array();
|
||||||
for( x in a )
|
for( x in a )
|
||||||
b.push(f(x));
|
b.push(f(x));
|
||||||
return b;
|
return b;
|
||||||
@@ -137,8 +156,16 @@ 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
|
||||||
|
if( isType(v) )
|
||||||
|
EConst(CType(v));
|
||||||
|
else
|
||||||
|
#end
|
||||||
EConst(CIdent(v));
|
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) } ]);
|
||||||
@@ -147,6 +174,11 @@ 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
|
||||||
|
if( isType(f) )
|
||||||
|
EType(convert(e), f);
|
||||||
|
else
|
||||||
|
#end
|
||||||
EField(convert(e), f);
|
EField(convert(e), f);
|
||||||
case EBinop(op, e1, e2):
|
case EBinop(op, e1, e2):
|
||||||
var b = binops.get(op);
|
var b = binops.get(op);
|
||||||
@@ -166,11 +198,23 @@ 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 = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
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));
|
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||||
#else
|
#elseif (haxe_211 || haxe3)
|
||||||
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
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));
|
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;
|
||||||
@@ -195,6 +239,12 @@ 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):
|
||||||
|
|||||||
@@ -0,0 +1,643 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
+855
-635
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+303
-85
@@ -30,23 +30,33 @@ class Printer {
|
|||||||
public function new() {
|
public function new() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function exprToString( e : Expr ):String {
|
public function exprToString( e : Expr ) {
|
||||||
buf = new StringBuf();
|
buf = new StringBuf();
|
||||||
tabs = "";
|
tabs = "";
|
||||||
expr(e);
|
expr(e);
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function typeToString( t : CType ):String {
|
public static function convertExprToString( e : Expr ) {
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline function add<T>(s:T):Void buf.add(s);
|
public static function convertTypeToString( t : CType ) {
|
||||||
|
var printer = new Printer();
|
||||||
|
return printer.typeToString(t);
|
||||||
|
}
|
||||||
|
|
||||||
function type( t : CType ):Void {
|
inline function add<T>(s:T) buf.add(s);
|
||||||
|
|
||||||
|
function type( t : CType ) {
|
||||||
switch( t ) {
|
switch( t ) {
|
||||||
case CTOpt(t):
|
case CTOpt(t):
|
||||||
add('?');
|
add('?');
|
||||||
@@ -68,11 +78,14 @@ 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('(');
|
||||||
for (a in args)
|
var first = true;
|
||||||
|
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):
|
||||||
@@ -101,26 +114,167 @@ class Printer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addType( t : CType ):Void {
|
function addType( t : CType ) {
|
||||||
if( t != null ) {
|
if( t != null ) {
|
||||||
add(" : ");
|
add(" : ");
|
||||||
type(t);
|
type(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function expr( e : Expr ):Void {
|
function block(e : Expr, addSpaceIfBlock : Bool = true) {
|
||||||
|
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 EIgnore(_):
|
case EImport(c, mode):
|
||||||
case EImport(c, n):
|
|
||||||
add("import " + c);
|
add("import " + c);
|
||||||
if(n != null)
|
switch(mode) {
|
||||||
add(' as $n');
|
case IAs(name): add(' as $name');
|
||||||
|
case IAll: add('.*');
|
||||||
|
default:
|
||||||
|
}
|
||||||
case EClass(name, fields, extend, interfaces):
|
case EClass(name, fields, extend, interfaces):
|
||||||
add('class $name');
|
add('class ${getVar(name)}');
|
||||||
if (extend != null)
|
if (extend != null)
|
||||||
add(' extends $extend');
|
add(' extends $extend');
|
||||||
for(_interface in interfaces) {
|
for(_interface in interfaces) {
|
||||||
@@ -128,25 +282,43 @@ class Printer {
|
|||||||
}
|
}
|
||||||
add(' {\n');
|
add(' {\n');
|
||||||
tabs += "\t";
|
tabs += "\t";
|
||||||
|
// TODO: Print fields
|
||||||
//for(field in fields) {
|
//for(field in fields) {
|
||||||
// expr(field);
|
// expr(field);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
tabs = tabs.substr(1);
|
tabs = tabs.substr(1);
|
||||||
add("}");
|
add("}");
|
||||||
case ERedirect(n, cl):
|
|
||||||
add('typedef $n = $cl');
|
|
||||||
case EConst(c):
|
case EConst(c):
|
||||||
switch( c ) {
|
switch( c ) {
|
||||||
case CInt(i): add(i);
|
case CInt(i): add(i);
|
||||||
case CFloat(f): add(f);
|
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 CString(s):
|
||||||
|
add('"');
|
||||||
|
add(getEscapedString(s));
|
||||||
|
add('"');
|
||||||
}
|
}
|
||||||
case EIdent(v):
|
case EIdent(v):
|
||||||
add(v);
|
add(getVar(v));
|
||||||
case EVar(n, t, e): // TODO: static, public, override
|
case EVar(n, t, e): // TODO: static, public, override
|
||||||
add("var " + n);
|
add("var " + getVar(n));
|
||||||
|
if( t != null )
|
||||||
addType(t);
|
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 ) {
|
if( e != null ) {
|
||||||
add(" = ");
|
add(" = ");
|
||||||
expr(e);
|
expr(e);
|
||||||
@@ -165,16 +337,51 @@ class Printer {
|
|||||||
add(";\n");
|
add(";\n");
|
||||||
}
|
}
|
||||||
tabs = tabs.substr(1);
|
tabs = tabs.substr(1);
|
||||||
|
add(tabs);
|
||||||
add("}");
|
add("}");
|
||||||
}
|
}
|
||||||
case EField(e, f, s):
|
case EField(e, f, s):
|
||||||
expr(e);
|
expr(e);
|
||||||
add((s == true ? "?." : ".") + f);
|
add((s == true ? "?." : ".") + f);
|
||||||
case EBinop(op, e1, e2):
|
case EBinop(op, e1, e2):
|
||||||
|
/*var op1 = switch(Tools.expr(e1)) {
|
||||||
|
case EBinop(op, _, _): op;
|
||||||
|
case EConst(_): "_";
|
||||||
|
case EIdent(_): "_";
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
var op2 = switch(Tools.expr(e2)) {
|
||||||
|
case EBinop(op, _, _): op;
|
||||||
|
case EConst(_): "_";
|
||||||
|
case EIdent(_): "_";
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
var paran = Tools.checkOpPrecedence(op, op1, op2);*/
|
||||||
|
var paran = 2;
|
||||||
|
|
||||||
|
var op = getBinaryOp(op);
|
||||||
|
|
||||||
|
if(paran == 0 || paran == 2) {
|
||||||
|
add("(");
|
||||||
expr(e1);
|
expr(e1);
|
||||||
|
add(")");
|
||||||
|
} else {
|
||||||
|
expr(e1);
|
||||||
|
}
|
||||||
|
if(op == "...")
|
||||||
|
add(op);
|
||||||
|
else
|
||||||
add(" " + op + " ");
|
add(" " + op + " ");
|
||||||
|
if(paran == 1 || paran == 2) {
|
||||||
|
add("(");
|
||||||
expr(e2);
|
expr(e2);
|
||||||
|
add(")");
|
||||||
|
} else {
|
||||||
|
expr(e2);
|
||||||
|
}
|
||||||
case EUnop(op, pre, e):
|
case EUnop(op, pre, e):
|
||||||
|
var op = getUnaryOp(op);
|
||||||
|
|
||||||
if( pre ) {
|
if( pre ) {
|
||||||
add(op);
|
add(op);
|
||||||
expr(e);
|
expr(e);
|
||||||
@@ -185,7 +392,7 @@ class Printer {
|
|||||||
case ECall(e, args):
|
case ECall(e, args):
|
||||||
if( e == null )
|
if( e == null )
|
||||||
expr(e);
|
expr(e);
|
||||||
else switch( #if hscriptPos e.e #else e #end ) {
|
else switch( Tools.expr(e) ) {
|
||||||
case EField(_), EIdent(_), EConst(_):
|
case EField(_), EIdent(_), EConst(_):
|
||||||
expr(e);
|
expr(e);
|
||||||
default:
|
default:
|
||||||
@@ -203,31 +410,35 @@ class Printer {
|
|||||||
case EIf(cond,e1,e2):
|
case EIf(cond,e1,e2):
|
||||||
add("if( ");
|
add("if( ");
|
||||||
expr(cond);
|
expr(cond);
|
||||||
add(" ) ");
|
add(" )");
|
||||||
expr(e1);
|
var wasBlock = block(e1, true);
|
||||||
|
|
||||||
if( e2 != null ) {
|
if( e2 != null ) {
|
||||||
|
if(!wasBlock) add("\n" + tabs);
|
||||||
add(" else ");
|
add(" else ");
|
||||||
expr(e2);
|
block(e2);
|
||||||
}
|
}
|
||||||
case EWhile(cond,e):
|
case EWhile(cond,e):
|
||||||
add("while( ");
|
add("while( ");
|
||||||
expr(cond);
|
expr(cond);
|
||||||
add(" ) ");
|
add(" )");
|
||||||
expr(e);
|
block(e, true);
|
||||||
case EDoWhile(cond,e):
|
case EDoWhile(cond,e):
|
||||||
add("do ");
|
add("do");
|
||||||
expr(e);
|
block(e, true);
|
||||||
add(" while ( ");
|
add(" while ( ");
|
||||||
expr(cond);
|
expr(cond);
|
||||||
add(" )");
|
add(" )");
|
||||||
case EFor(v, it, e, ithv):
|
case EFor(v, it, e):
|
||||||
if(ithv != null)
|
add("for( "+getVar(v)+" in ");
|
||||||
add("for( "+ithv+" => "+v+" in ");
|
|
||||||
else
|
|
||||||
add("for( "+v+" in ");
|
|
||||||
expr(it);
|
expr(it);
|
||||||
add(" ) ");
|
add(" )");
|
||||||
expr(e);
|
block(e, true);
|
||||||
|
case EForKeyValue(v, it, e, ithv):
|
||||||
|
add("for( "+getVar(ithv)+" => "+getVar(v)+" in ");
|
||||||
|
expr(it);
|
||||||
|
add(" )");
|
||||||
|
block(e, true);
|
||||||
case EBreak:
|
case EBreak:
|
||||||
add("break");
|
add("break");
|
||||||
case EContinue:
|
case EContinue:
|
||||||
@@ -235,13 +446,13 @@ class Printer {
|
|||||||
case EFunction(params, e, name, ret): // TODO: static, public, override
|
case EFunction(params, e, name, ret): // TODO: static, public, override
|
||||||
add("function");
|
add("function");
|
||||||
if( name != null )
|
if( name != null )
|
||||||
add(" " + name);
|
add(" " + getVar(name));
|
||||||
add("(");
|
add("(");
|
||||||
var first = true;
|
var first = true;
|
||||||
for( a in params ) {
|
for( a in params ) {
|
||||||
if( first ) first = false else add(", ");
|
if( first ) first = false else add(", ");
|
||||||
if( a.opt ) add("?");
|
if( a.opt ) add("?");
|
||||||
add(a.name);
|
add(getVar(a.name));
|
||||||
addType(a.t);
|
addType(a.t);
|
||||||
}
|
}
|
||||||
add(")");
|
add(")");
|
||||||
@@ -259,7 +470,17 @@ class Printer {
|
|||||||
add("[");
|
add("[");
|
||||||
expr(index);
|
expr(index);
|
||||||
add("]");
|
add("]");
|
||||||
case EArrayDecl(el, _):
|
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("[");
|
add("[");
|
||||||
var first = true;
|
var first = true;
|
||||||
for( e in el ) {
|
for( e in el ) {
|
||||||
@@ -268,7 +489,7 @@ class Printer {
|
|||||||
}
|
}
|
||||||
add("]");
|
add("]");
|
||||||
case ENew(cl, args):
|
case ENew(cl, args):
|
||||||
add("new " + cl + "(");
|
add("new " + getVar(cl) + "(");
|
||||||
var first = true;
|
var first = true;
|
||||||
for( e in args ) {
|
for( e in args ) {
|
||||||
if( first ) first = false else add(", ");
|
if( first ) first = false else add(", ");
|
||||||
@@ -279,25 +500,31 @@ class Printer {
|
|||||||
add("throw ");
|
add("throw ");
|
||||||
expr(e);
|
expr(e);
|
||||||
case ETry(e, v, t, ecatch):
|
case ETry(e, v, t, ecatch):
|
||||||
add("try ");
|
add("try");
|
||||||
expr(e);
|
var wasBlock = block(e);
|
||||||
add(" catch( " + v);
|
if( !wasBlock ) {
|
||||||
|
add("\n" + tabs);
|
||||||
|
} else add(" ");
|
||||||
|
add("catch( " + v);
|
||||||
addType(t);
|
addType(t);
|
||||||
add(") ");
|
add(" )");
|
||||||
expr(ecatch);
|
block(ecatch);
|
||||||
case EObject(fl):
|
case EObject(fl):
|
||||||
if( fl.length == 0 ) {
|
if( fl.length == 0 ) {
|
||||||
add("{}");
|
add("{}");
|
||||||
} else {
|
} else {
|
||||||
tabs += "\t";
|
tabs += "\t";
|
||||||
add("{\n");
|
add("{\n");
|
||||||
for( f in fl ) {
|
for( i=>f in fl ) {
|
||||||
add(tabs);
|
add(tabs);
|
||||||
add(f.name+" : ");
|
var name = isJson(f.name) ? "\"" + f.name + "\"" : f.name;
|
||||||
|
add(name+" : ");
|
||||||
expr(f.e);
|
expr(f.e);
|
||||||
add(",\n");
|
if( i != fl.length - 1 ) add(",");
|
||||||
|
add("\n");
|
||||||
}
|
}
|
||||||
tabs = tabs.substr(1);
|
tabs = tabs.substr(1);
|
||||||
|
add(tabs);
|
||||||
add("}");
|
add("}");
|
||||||
}
|
}
|
||||||
case ETernary(c,e1,e2):
|
case ETernary(c,e1,e2):
|
||||||
@@ -309,23 +536,28 @@ class Printer {
|
|||||||
case ESwitch(e, cases, def):
|
case ESwitch(e, cases, def):
|
||||||
add("switch( ");
|
add("switch( ");
|
||||||
expr(e);
|
expr(e);
|
||||||
add(") {");
|
add(" ) {\n");
|
||||||
|
tabs += "\t";
|
||||||
for( c in cases ) {
|
for( c in cases ) {
|
||||||
|
add(tabs);
|
||||||
add("case ");
|
add("case ");
|
||||||
var first = true;
|
var first = true;
|
||||||
for( v in c.values ) {
|
for( v in c.values ) {
|
||||||
if( first ) first = false else add(", ");
|
if( first ) first = false else add(", ");
|
||||||
expr(v);
|
expr(v);
|
||||||
}
|
}
|
||||||
add(": ");
|
add(":");
|
||||||
expr(c.expr);
|
block(c.expr, true);
|
||||||
add(";\n");
|
add(";\n");
|
||||||
}
|
}
|
||||||
if( def != null ) {
|
if( def != null ) {
|
||||||
|
add(tabs);
|
||||||
add("default: ");
|
add("default: ");
|
||||||
expr(def);
|
block(def, true);
|
||||||
add(";\n");
|
add(";\n");
|
||||||
}
|
}
|
||||||
|
tabs = tabs.substr(1);
|
||||||
|
add(tabs);
|
||||||
add("}");
|
add("}");
|
||||||
case EMeta(name, args, e):
|
case EMeta(name, args, e):
|
||||||
add("@");
|
add("@");
|
||||||
@@ -347,61 +579,47 @@ class Printer {
|
|||||||
add(" : ");
|
add(" : ");
|
||||||
addType(t);
|
addType(t);
|
||||||
add(")");
|
add(")");
|
||||||
case EUsing(name):
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
add("using ");
|
case EInfo(_, e):
|
||||||
add(name);
|
expr(e);
|
||||||
case EEnum(name, params):
|
#end
|
||||||
if (params.length == 0) {
|
|
||||||
add("enum " + name + " {}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
add("enum " + name + " {\n");
|
|
||||||
tabs += "\t";
|
|
||||||
for (p in params) {
|
|
||||||
add(tabs);
|
|
||||||
switch p {
|
|
||||||
case EConstructor(name, args):
|
|
||||||
add(name);
|
|
||||||
add("(");
|
|
||||||
for (a in args)
|
|
||||||
addArgument(a);
|
|
||||||
add(")");
|
|
||||||
case ESimple(name):
|
|
||||||
add(name);
|
|
||||||
}
|
|
||||||
add(";\n");
|
|
||||||
}
|
|
||||||
tabs = tabs.substr(1);
|
|
||||||
add(tabs);
|
|
||||||
add("}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addArgument(a: Argument) {
|
public inline static function toString( e : Expr ) {
|
||||||
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 static function errorToString( e : Expr.Error ):String {
|
public inline static function compareErrors( e1 : Error, e2 : Error ) {
|
||||||
var message = switch( #if hscriptPos e.e #else e #end ) {
|
return Type.enumEq(Tools.cleanError(e1), Tools.cleanError(e2));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|||||||
+199
-137
@@ -21,18 +21,17 @@
|
|||||||
*/
|
*/
|
||||||
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 ):Void {
|
public static function iter( e : Expr, f : Expr -> Void ) {
|
||||||
switch( expr(e) ) {
|
switch( expr(e) ) {
|
||||||
case EConst(_), EIdent(_):
|
case EConst(_), EIdent(_): f(e);
|
||||||
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);
|
||||||
@@ -42,10 +41,12 @@ class Tools {
|
|||||||
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 EMapDecl(type, keys, values): for( e in keys ) f(e); for( e in values ) f(e);
|
||||||
case EArrayDecl(el): for( e in el ) f(e);
|
case EArrayDecl(el): for( e in el ) f(e);
|
||||||
case ENew(_,el): for( e in el ) f(e);
|
case ENew(_,el): for( e in el ) f(e);
|
||||||
case EThrow(e): f(e);
|
case EThrow(e): f(e);
|
||||||
@@ -61,17 +62,64 @@ class Tools {
|
|||||||
if( def != null ) f(def);
|
if( def != null ) f(def);
|
||||||
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
||||||
case ECheckType(e,_): f(e);
|
case ECheckType(e,_): f(e);
|
||||||
default:
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
case EInfo(info, e): f(e);
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function map( e : Expr, f : Expr -> Expr ):Expr {
|
public static function iterExprRecursive( ve : Expr, f : Expr -> Void ) {
|
||||||
|
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, isPublic, isStatic, isPrivate): EVar(n, t, if( e != null ) f(e) else null, isPublic, isStatic, isPrivate);
|
case EVar(n, t, e, p, s): EVar(n, t, if( e != null ) f(e) else null, p, s);
|
||||||
case EParent(e): EParent(f(e));
|
case EParent(e, no): EParent(f(e), no);
|
||||||
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
||||||
case EField(e, fi): EField(f(e),fi);
|
case EField(e, fi, s): EField(f(e),fi,s);
|
||||||
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)]);
|
||||||
@@ -79,21 +127,25 @@ class Tools {
|
|||||||
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 EFunction(args, e, name, t, isPublic, isStatic, isOverride, isPrivate): EFunction(args, f(e), name, t, isPublic, isStatic, isOverride, isPrivate);
|
case EForKeyValue(v, it, e, ithv): EForKeyValue(v, f(it), f(e), ithv);
|
||||||
|
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 EMapDecl(type, keys, values): EMapDecl(type, [for( e in keys ) f(e)], [for( e in values ) f(e)]);
|
||||||
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
|
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
|
||||||
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
|
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
|
||||||
case EThrow(e): EThrow(f(e));
|
case EThrow(e): EThrow(f(e));
|
||||||
case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
|
case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
|
||||||
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
|
case EObject(fl): EObject([for( fi in fl ) new ObjectField(fi.name, f(fi.e))]);
|
||||||
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
|
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
|
||||||
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 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 EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
|
case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
|
||||||
case ECheckType(e,t): ECheckType(f(e), t);
|
case ECheckType(e,t): ECheckType(f(e), t);
|
||||||
case EImport(c): EImport(c);
|
case EImport(c, m): EImport(c, m);
|
||||||
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
||||||
default: #if hscriptPos e.e #else e #end;
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
case EInfo(info, e): EInfo(info, f(e));
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
return mk(edef, e);
|
return mk(edef, e);
|
||||||
}
|
}
|
||||||
@@ -106,155 +158,165 @@ class Tools {
|
|||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
public static inline function mk( e : ExprDef, p : Expr ):Expr {
|
public static inline function cleanError( e : Error ) {
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
|
return e.e;
|
||||||
#else
|
#else
|
||||||
return e;
|
return e;
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
@:access(hscript.Interp)
|
public static inline function mk( e : ExprDef, p : Expr ):Expr {
|
||||||
public static function clearClasses() {
|
#if hscriptPos
|
||||||
#if CUSTOM_CLASSES
|
return new Expr(e, p.pmin, p.pmax, p.origin, p.line);
|
||||||
Interp._customClasses.clear();
|
|
||||||
Interp._customClassAliases.clear();
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function removeInnerClass(name: String): String {
|
|
||||||
var ll = name.lastIndexOf(".");
|
|
||||||
if (name.indexOf(".") != ll) { // checks if there are 2 or more dots
|
|
||||||
// wtf did i make -neo
|
|
||||||
// this is awesome -crow
|
|
||||||
return name.substr(0, name.lastIndexOf(".", ll - 1)) + "." + name.substr(ll + 1);
|
|
||||||
}
|
|
||||||
// only one dot or no dots (will work since -1 + 1 = 0)
|
|
||||||
return name.substr(ll + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: maybe use this function for import since also check for innerclasses
|
|
||||||
public static function getClass(name: String): Dynamic {
|
|
||||||
var splitClassName = [for (e in name.split(".")) e.trim()];
|
|
||||||
var realClassName = splitClassName.join(".");
|
|
||||||
|
|
||||||
var c: Dynamic = Type.resolveClass(realClassName);
|
|
||||||
if (c == null) // try importing as enum
|
|
||||||
try
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DO NOT USE INLINE ON THIS FUNCTION
|
|
||||||
**/
|
|
||||||
public static function argCount(func: haxe.Constraints.Function): Int {
|
|
||||||
#if cpp
|
|
||||||
return untyped __cpp__("{0}->__ArgCount()", func);
|
|
||||||
#elseif js
|
|
||||||
return untyped js.Syntax.code("{0}.length", func);
|
|
||||||
#else
|
#else
|
||||||
return -1;
|
return e;
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getClassDeclFields(classDecl:hscript.Expr.ClassDecl, onlyStatic:Bool = false):Array<FieldDecl> {
|
public static function isValidBinOp(op:String):Bool {
|
||||||
var fields = classDecl.fields.filter(function(field:FieldDecl) {
|
if(op == ("??"+"=")) return true;
|
||||||
var isStatic = field.access.contains(AStatic);
|
return switch(op) {
|
||||||
return onlyStatic ? isStatic : !isStatic;
|
case "+" | "-" | "*" | "/" | "%" | "&" | "|" | "^" | "<<" | ">>" | ">>>" | "==" | "!=" | ">=" | "<=" | ">" | "<" | "||" | "&&" | "is" | "=" | "??" | "..." | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | ">>>=": true;
|
||||||
});
|
case "=>": true;
|
||||||
return fields;
|
default: false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public static function getOpEnum(op:String):Binop {
|
||||||
|
return switch(op) {
|
||||||
/**
|
case "+": OpAdd;
|
||||||
* @see https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L217
|
case "-": OpSub;
|
||||||
*/
|
case "*": OpMult;
|
||||||
class EnumValue {
|
case "/": OpDiv;
|
||||||
public var enumName: String;
|
case "%": OpMod;
|
||||||
public var name: String;
|
case "&": OpAnd;
|
||||||
public var index: Int;
|
case "|": OpOr;
|
||||||
public var args: Array<Dynamic>;
|
case "^": OpXor;
|
||||||
public var enumParent:Dynamic;
|
case "<<": OpShl;
|
||||||
|
case ">>": OpShr;
|
||||||
public function new(enumName: String, name: String, index: Int, enumParent:Dynamic, ?args: Array<Dynamic>) {
|
case ">>>": OpUShr;
|
||||||
this.enumName = enumName;
|
case "==": OpEq;
|
||||||
this.name = name;
|
case "!=": OpNotEq;
|
||||||
this.index = index;
|
case ">=": OpGte;
|
||||||
this.enumParent = enumParent;
|
case "<=": OpLte;
|
||||||
this.args = args;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toString(): String {
|
public static function getUnopEnum(op:String):Unop {
|
||||||
if (args == null)
|
return switch(op) {
|
||||||
return enumName + "." + name;
|
case "++": OpIncrement;
|
||||||
return enumName + "." + name + "(" + [for (arg in args) arg].join(", ") + ")";
|
case "--": OpDecrement;
|
||||||
|
case "!": OpNot;
|
||||||
|
case "-": OpNeg;
|
||||||
|
case "~": OpNegBits;
|
||||||
|
case "...": OpSpread;
|
||||||
|
default: throw "Unknown unary operator: " + op;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public inline function getEnumName(): String
|
static var priorities = [
|
||||||
return this.enumName;
|
["%"],
|
||||||
|
["*", "/"],
|
||||||
|
["+", "-"],
|
||||||
|
["<<", ">>", ">>>"],
|
||||||
|
["|", "&", "^"],
|
||||||
|
["==", "!=", ">", "<", ">=", "<="],
|
||||||
|
["..."],
|
||||||
|
["&&"],
|
||||||
|
["||"],
|
||||||
|
["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^=","=>","??"+"="],
|
||||||
|
["->", "??"],
|
||||||
|
["is"]
|
||||||
|
];
|
||||||
|
public static function checkOpPrecedence(mainOp:String, leftOp:String, rightOp:String):Int {
|
||||||
|
var mainOpGroup = getOpGroup(mainOp);
|
||||||
|
var leftOpGroup = getOpGroup(leftOp);
|
||||||
|
var rightOpGroup = getOpGroup(rightOp);
|
||||||
|
|
||||||
public inline function getConstructorArgs(): Array<Dynamic>
|
var leftParam = false;
|
||||||
return this.args != null ? this.args : [];
|
var rightParam = false;
|
||||||
|
|
||||||
public function compare(other: EnumValue): Bool {
|
if(leftOpGroup > mainOpGroup && leftOpGroup != -1) leftParam = true;
|
||||||
if (enumName != other.enumName || name != other.name)
|
if(rightOpGroup > mainOpGroup && rightOpGroup != -1) rightParam = true;
|
||||||
return false;
|
|
||||||
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) {
|
if(!leftParam && !rightParam) {
|
||||||
// TODO: allow deep comparison, like arrays
|
var mainOpIndex = getOpIndex(mainOp);
|
||||||
if (args[i] != other.args[i])
|
var leftOpIndex = getOpIndex(leftOp);
|
||||||
return false;
|
var rightOpIndex = getOpIndex(rightOp);
|
||||||
|
|
||||||
|
if(mainOpGroup == rightOpGroup) {
|
||||||
|
if(rightOpIndex < mainOpIndex) rightParam = true;
|
||||||
|
}
|
||||||
|
if(leftOpGroup == mainOpGroup) {
|
||||||
|
if(leftOpIndex < mainOpIndex) leftParam = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
// Convert to index
|
||||||
|
if(!leftParam && !rightParam) return -1;
|
||||||
|
if(leftParam && rightParam) return 2;
|
||||||
|
if(leftParam) return 0;
|
||||||
|
if(rightParam) return 1;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class HScriptEnum implements IHScriptCustomBehaviour {
|
public static function getOpIndex(op:String):Int {
|
||||||
public var enumValues(default, null) = {};
|
if(op == "_") return -1;
|
||||||
|
var i = 0;
|
||||||
public function new() {}
|
for(p in priorities) {
|
||||||
|
for(pp in p) {
|
||||||
public function setEnum(name:String, enumValue:Dynamic) {
|
if(op == pp)
|
||||||
Reflect.setField(enumValues, name, enumValue);
|
return i;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public function getEnumValue(name:String):Null<Dynamic> {
|
return -1;
|
||||||
if(Reflect.hasField(enumValues, name)) return Reflect.field(enumValues, name);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hget(name:String):Dynamic {
|
public static function getOpGroup(op:String):Int {
|
||||||
return getEnumValue(name);
|
if(op == "_") return -1;
|
||||||
|
var i = 0;
|
||||||
|
for(p in priorities) {
|
||||||
|
for(pp in p) {
|
||||||
|
if(op == pp)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function hset(name:String, val:Dynamic):Dynamic {
|
public static function getEnum(cl:Enum<Dynamic>):Dynamic {
|
||||||
return null;
|
var enumThingy:Dynamic = {};
|
||||||
|
for (c in cl.getConstructors()) {
|
||||||
|
try {
|
||||||
|
UnsafeReflect.setField(enumThingy, c, cl.createByName(c));
|
||||||
|
} catch(e) {
|
||||||
|
try {
|
||||||
|
UnsafeReflect.setField(enumThingy, c, Reflect.makeVarArgs((args:Array<Dynamic>) -> cl.createByName(c, args)));
|
||||||
|
} catch(ex) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enumThingy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,512 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -60,19 +60,20 @@ 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(false && cl.superClass != null) {
|
if(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 = FixedTypeTools.toSimpleField(field);
|
var nfield = @:privateAccess TypeTools.toField(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)
|
||||||
@@ -85,9 +86,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)
|
||||||
return null;
|
continue;
|
||||||
|
|
||||||
//sfun.ret = Utils.fixStdTypes(fun.ret);
|
fun.ret = Utils.fixStdTypes(fun.ret);
|
||||||
|
|
||||||
var metas = nfield.meta;
|
var metas = nfield.meta;
|
||||||
var defaultValues:Map<String, Dynamic> = [];
|
var defaultValues:Map<String, Dynamic> = [];
|
||||||
@@ -106,7 +107,7 @@ class ClassExtendMacro {
|
|||||||
if(m.name == ":generic")
|
if(m.name == ":generic")
|
||||||
isGeneric = true;
|
isGeneric = true;
|
||||||
}
|
}
|
||||||
if(isGeneric) return null;
|
if(isGeneric) continue;
|
||||||
|
|
||||||
if(defaultEntry != null)
|
if(defaultEntry != null)
|
||||||
metas.remove(defaultEntry);
|
metas.remove(defaultEntry);
|
||||||
@@ -118,80 +119,20 @@ class ClassExtendMacro {
|
|||||||
arg.opt = false;
|
arg.opt = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
arg.type = null;//Utils.fixStdTypes(arg.type);
|
arg.type = 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:
|
||||||
}
|
}
|
||||||
return nfield;
|
superFields.push(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 = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,20 +152,11 @@ 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) || f.access.contains(AFinal))
|
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline))
|
||||||
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
|
||||||
@@ -254,7 +186,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;
|
||||||
@@ -263,17 +195,12 @@ 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;
|
||||||
@@ -283,12 +210,6 @@ class ClassExtendMacro {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
|
|
||||||
__customClass.callFunction(name, [$a{arguments}]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.$name($a{arguments});
|
super.$name($a{arguments});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -359,64 +280,45 @@ class ClassExtendMacro {
|
|||||||
Utils.setupMetas(shadowClass, imports);
|
Utils.setupMetas(shadowClass, imports);
|
||||||
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
||||||
|
|
||||||
shadowClass.fields.push({
|
|
||||||
name: "__cachedFields",
|
|
||||||
pos: Context.currentPos(),
|
|
||||||
kind: FVar(macro: Map<String, Dynamic>),
|
|
||||||
access: [APublic, AStatic]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Adding hscript getters and setters
|
// Adding hscript getters and setters
|
||||||
/*
|
|
||||||
|
// TODO: Remove
|
||||||
shadowClass.fields.push({
|
shadowClass.fields.push({
|
||||||
name: "__interp",
|
name: "__interp",
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
kind: FVar(macro: hscript.Interp),
|
kind: FVar(TPath({
|
||||||
|
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(macro: Map<String, Dynamic>),
|
kind: FVar(TPath({
|
||||||
|
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(macro: Bool, macro true),
|
kind: FVar(TPath({
|
||||||
|
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: macro: Dynamic,
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
params: [],
|
params: [],
|
||||||
expr: macro {
|
expr: macro {
|
||||||
__allowSetGet = false;
|
__allowSetGet = false;
|
||||||
@@ -429,7 +331,7 @@ class ClassExtendMacro {
|
|||||||
name: "name",
|
name: "name",
|
||||||
opt: false,
|
opt: false,
|
||||||
meta: [],
|
meta: [],
|
||||||
type: macro: String
|
type: TPath({name: "String", pack: []})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
@@ -440,7 +342,7 @@ class ClassExtendMacro {
|
|||||||
name: "__callSetter",
|
name: "__callSetter",
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
kind: FFun({
|
kind: FFun({
|
||||||
ret: macro: Dynamic,
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
params: [],
|
params: [],
|
||||||
expr: macro {
|
expr: macro {
|
||||||
__allowSetGet = false;
|
__allowSetGet = false;
|
||||||
@@ -453,19 +355,19 @@ class ClassExtendMacro {
|
|||||||
name: "name",
|
name: "name",
|
||||||
opt: false,
|
opt: false,
|
||||||
meta: [],
|
meta: [],
|
||||||
type: macro: String
|
type: TPath({name: "String", pack: []})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "val",
|
name: "val",
|
||||||
opt: false,
|
opt: false,
|
||||||
meta: [],
|
meta: [],
|
||||||
type: macro: Dynamic
|
type: TPath({name: "Dynamic", pack: []})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
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);
|
||||||
@@ -508,67 +410,43 @@ class ClassExtendMacro {
|
|||||||
|
|
||||||
var hgetField = if(hasHgetInSuper) {
|
var hgetField = if(hasHgetInSuper) {
|
||||||
macro {
|
macro {
|
||||||
/*
|
|
||||||
if(__custom__variables != null) {
|
|
||||||
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 super.hget(name);
|
return super.hget(name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
macro {
|
macro {
|
||||||
/*
|
|
||||||
if(__custom__variables != null) {
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hsetField = if(hasHsetInSuper) {
|
var hsetField = if(hasHsetInSuper) {
|
||||||
macro {
|
macro {
|
||||||
/*
|
|
||||||
if(__custom__variables != null) {
|
|
||||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||||
return __callSetter(name, val);
|
return __callSetter(name, val);
|
||||||
if (__custom__variables.exists(name)) {
|
if (__custom__variables.exists(name)) {
|
||||||
__custom__variables.set(name, val);
|
__custom__variables.set(name, val);
|
||||||
return 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(__custom__variables != null) {
|
|
||||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||||
return __callSetter(name, val);
|
return __callSetter(name, val);
|
||||||
if (__custom__variables.exists(name)) {
|
if (__custom__variables.exists(name)) {
|
||||||
__custom__variables.set(name, val);
|
__custom__variables.set(name, val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
*/
|
|
||||||
if(__real_fields != null && __real_fields.contains(name)) {
|
|
||||||
UnsafeReflect.setProperty(this, name, val);
|
UnsafeReflect.setProperty(this, name, val);
|
||||||
return UnsafeReflect.field(this, name);
|
return UnsafeReflect.field(this, name);
|
||||||
}
|
}
|
||||||
//if(__custom__variables != null) __custom__variables.set(name, val);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//if(hasHsetInSuper || hasHgetInSuper) return fields;
|
//if(hasHsetInSuper || hasHgetInSuper) return fields;
|
||||||
@@ -580,7 +458,7 @@ class ClassExtendMacro {
|
|||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
|
access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
|
||||||
kind: FFun({
|
kind: FFun({
|
||||||
ret: macro: Dynamic,
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
params: [],
|
params: [],
|
||||||
expr: hsetField,
|
expr: hsetField,
|
||||||
args: [
|
args: [
|
||||||
@@ -588,13 +466,13 @@ class ClassExtendMacro {
|
|||||||
name: "name",
|
name: "name",
|
||||||
opt: false,
|
opt: false,
|
||||||
meta: [],
|
meta: [],
|
||||||
type: macro: String
|
type: TPath({name: "String", pack: []})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "val",
|
name: "val",
|
||||||
opt: false,
|
opt: false,
|
||||||
meta: [],
|
meta: [],
|
||||||
type: macro: Dynamic
|
type: TPath({name: "Dynamic", pack: []})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -605,7 +483,7 @@ class ClassExtendMacro {
|
|||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
|
access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
|
||||||
kind: FFun({
|
kind: FFun({
|
||||||
ret: macro: Dynamic,
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
params: [],
|
params: [],
|
||||||
expr: hgetField,
|
expr: hgetField,
|
||||||
args: [
|
args: [
|
||||||
@@ -613,7 +491,7 @@ class ClassExtendMacro {
|
|||||||
name: "name",
|
name: "name",
|
||||||
opt: false,
|
opt: false,
|
||||||
meta: [],
|
meta: [],
|
||||||
type: macro: String
|
type: TPath({name: "String", pack: []})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -631,32 +509,6 @@ 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 {
|
||||||
|
|||||||
@@ -1,529 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,12 +10,12 @@ import haxe.macro.Compiler;
|
|||||||
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
class AbstractHandler {
|
class UsingHandler {
|
||||||
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.AbstractHandler.build())');
|
Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.UsingHandler.build())');
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,364 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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, value);
|
Reflect.setProperty(o, field);
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ class UnsafeReflect {
|
|||||||
return untyped func.__Run(args);
|
return untyped func.__Run(args);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
return Reflect.callMethod(o, Reflect.field(o, field), args);
|
return Reflect.callMethod(o, func, args);
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
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 {}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
bin/*
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#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
|
||||||
|
}
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
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() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import hscript.Interp;
|
||||||
|
import hscript.Parser;
|
||||||
|
|
||||||
|
import Util;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# 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
|
||||||
Reference in New Issue
Block a user