Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67bb4bab98 | ||
|
|
927f771024 | ||
|
|
8221dca2b0 | ||
|
|
4c218116a6 | ||
|
|
fc15dbd382 | ||
|
|
bb3785c53c | ||
|
|
1892f0793f | ||
|
|
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,3 +1,5 @@
|
||||
--macro keep('IntIterator')
|
||||
--macro hscript.macros.UsingHandler.init()
|
||||
--macro hscript.macros.ClassExtendMacro.init()
|
||||
--macro hscript.macros.AbstractHandler.init()
|
||||
--define HSCRIPT_NEW
|
||||
|
||||
@@ -0,0 +1,678 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.Expr;
|
||||
import hscript.Error;
|
||||
import hscript.Tools;
|
||||
import hscript.Parser;
|
||||
|
||||
enum AVarType {
|
||||
VUnknown;
|
||||
VVar(name:String, type:ADeclType);
|
||||
}
|
||||
|
||||
typedef ATypeInfo = {
|
||||
impl: String,
|
||||
cl: Class<Any>,
|
||||
type: AType,
|
||||
}
|
||||
|
||||
enum AType {
|
||||
TUnknown;
|
||||
TBasic(name:String);
|
||||
TAbstract(type:Class<Any>, impl:String);
|
||||
}
|
||||
|
||||
@:structInit
|
||||
class ADeclVar {
|
||||
public var name:String;
|
||||
public var type:AVarType;
|
||||
public var depth:Int;
|
||||
|
||||
public function toString():String {
|
||||
return "var " + name + " : " + type + " @ " + depth;
|
||||
}
|
||||
}
|
||||
|
||||
@:structInit
|
||||
class ADeclType {
|
||||
public var name:String;
|
||||
public var inf:ATypeInfo;
|
||||
public var depth:Int;
|
||||
|
||||
public function toString():String {
|
||||
return "type " + name + " : " + inf.type + " @ " + depth;
|
||||
}
|
||||
}
|
||||
|
||||
typedef AbstractData = {
|
||||
var impl:String;
|
||||
var funcs:Array<Array<Dynamic>>;
|
||||
var props:Array<Array<String>>;
|
||||
}
|
||||
|
||||
@:access(hscript.Parser)
|
||||
class AbstractProcessor {
|
||||
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);
|
||||
}
|
||||
|
||||
private static var depth:Int = 0;
|
||||
private static var declared:Array<ADeclVar>;
|
||||
private static var declaredTypes:Array<ADeclType>;
|
||||
private static var abstractData:Map<String, AbstractData>;
|
||||
private static var unusedImports:Map<String, String>;
|
||||
|
||||
private static function addDeclared(e:String, type:AVarType = VUnknown) {
|
||||
declared.push({
|
||||
name: e,
|
||||
type: type,
|
||||
depth: depth
|
||||
});
|
||||
}
|
||||
|
||||
private static function addTypeDeclared(e:String, inf:ATypeInfo) {
|
||||
declaredTypes.push({
|
||||
name: e,
|
||||
inf: inf,
|
||||
depth: depth
|
||||
});
|
||||
}
|
||||
|
||||
private static function getDeclared(e:String):ADeclVar {
|
||||
var len = declared.length;
|
||||
for(i in 0...len) {
|
||||
var idx = len - i - 1;
|
||||
var v = declared[idx];
|
||||
if(v.name == e) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getDeclaredType(e:String):ADeclType {
|
||||
var len = declaredTypes.length;
|
||||
for(i in 0...len) {
|
||||
var idx = len - i - 1;
|
||||
var v = declaredTypes[idx];
|
||||
if(v.name == e) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
var abstr = resolveAbstract(e);
|
||||
if(abstr != null) {
|
||||
return {
|
||||
name: getFieldName(e),
|
||||
inf: abstr,
|
||||
depth: depth
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static function removeUnusedDeclared() {
|
||||
while(declared.length > 0 && declared[declared.length - 1].depth > depth) {
|
||||
declared.pop();
|
||||
}
|
||||
while(declaredTypes.length > 0 && declaredTypes[declaredTypes.length - 1].depth > depth) {
|
||||
declaredTypes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
public static function process(e:Expr):Expr {
|
||||
importStackName = [];
|
||||
importStackMode = [];
|
||||
depth = 0;
|
||||
declared = new Array();
|
||||
declaredTypes = new Array();
|
||||
abstractData = new Map();
|
||||
unusedImports = new Map();
|
||||
storedTypes = [];
|
||||
|
||||
var e = _process(e, true);
|
||||
|
||||
// Remove data
|
||||
declared = null;
|
||||
declaredTypes = null;
|
||||
abstractData = null;
|
||||
unusedImports = null;
|
||||
storedTypes = null;
|
||||
|
||||
e = removeMetaType(e);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
static function removeMetaType(e:Expr) {
|
||||
if(e == null)
|
||||
return null;
|
||||
switch(Tools.expr(e)) {
|
||||
case EMeta(":$type", [expr(_) => EConst(CInt(t))], e):
|
||||
return e;
|
||||
default:
|
||||
}
|
||||
return Tools.map(e, removeMetaType);
|
||||
}
|
||||
|
||||
static function getAbstractImport(name:String, m:KImportMode = INormal):String {
|
||||
var typeInfo = resolveAbstract(name);
|
||||
if(typeInfo == null) return null;
|
||||
|
||||
var fname = getFieldName(name);
|
||||
var varName = switch(m) {
|
||||
case IAs(a): a;
|
||||
default: fname;
|
||||
}
|
||||
addTypeDeclared(varName, typeInfo);
|
||||
trace("Adding abstract " + name + " as " + varName);
|
||||
|
||||
return typeInfo.impl;
|
||||
}
|
||||
|
||||
public static function loadAbstractData(name:String, ?type:Class<Any>) {
|
||||
if(abstractData.exists(name)) return abstractData.get(name);
|
||||
|
||||
if(type == null) {
|
||||
var t = resolveClassAbstract(name);
|
||||
if(t == null) return null;
|
||||
type = t.cl;
|
||||
if(type == null) return null;
|
||||
}
|
||||
|
||||
var data:AbstractData = {
|
||||
//var t:Dynamic = Type.createInstance(type, []);
|
||||
var t:Dynamic = type;
|
||||
{
|
||||
impl: t.__hx_impl__,
|
||||
funcs: t.abstract_funcs(),
|
||||
props: t.abstract_props()
|
||||
}
|
||||
};
|
||||
|
||||
abstractData.set(name, data);
|
||||
abstractData.set(data.impl, data);
|
||||
trace("Loading abstract " + name + " as " + data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static function resolveAbstract(name:String, m:KImportMode = INormal) {
|
||||
var typeInfo = resolveClassAbstract(name);
|
||||
if(typeInfo == null) return null;
|
||||
|
||||
//loadAbstractData(name, type);
|
||||
addImport(typeInfo.impl, m);
|
||||
typeInfo.type = TAbstract(typeInfo.cl, typeInfo.impl);
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
static function resolveClassAbstract(name:String):ATypeInfo {
|
||||
var cl = Type.resolveClass(name + "_HSA");
|
||||
trace("Resolving " + name + "_HSA");
|
||||
if(cl == null) {
|
||||
var spr = name.split(".");
|
||||
if(spr.length < 2)
|
||||
return null;
|
||||
spr.splice(-2, 1); // remove the last last name;
|
||||
trace("Resolving " + spr.join(".") + "_HSA");
|
||||
cl = Type.resolveClass(spr.join(".") + "_HSA");
|
||||
}
|
||||
if(cl != null) {
|
||||
var cld:Dynamic = cl;
|
||||
return {
|
||||
impl: cld.__hx_impl__,
|
||||
cl: cl,
|
||||
type: null,
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static function findAbstractFromFieldName(name:String):AType {
|
||||
if(name.indexOf(".") == -1) {
|
||||
trace("Finding abstract from field name " + name + " " + unusedImports);
|
||||
if(unusedImports.exists(name)) {
|
||||
var fullName = unusedImports.get(name);
|
||||
addImport(getAbstractImport(fullName, INormal), INormal);
|
||||
unusedImports.remove(name);
|
||||
var v = resolveAbstract(fullName, INormal);
|
||||
if(v != null)
|
||||
return v.type;
|
||||
}
|
||||
}
|
||||
|
||||
trace("Finding abstract from field name " + name);
|
||||
var v = resolveAbstract(name, INormal);
|
||||
if(v != null)
|
||||
return v.type;
|
||||
return null;
|
||||
}
|
||||
|
||||
static function getFieldName(e:String) {
|
||||
var name = e.substr(e.lastIndexOf(".") + 1);
|
||||
return name;
|
||||
}
|
||||
|
||||
private static var storedTypes:Array<AType>;
|
||||
|
||||
static function setStoredType(type:AType):Int {
|
||||
var idx = storedTypes.indexOf(type);
|
||||
if(idx != -1) return idx;
|
||||
storedTypes.push(type);
|
||||
return storedTypes.length - 1;
|
||||
}
|
||||
|
||||
static inline function getStoredType(idx:Int):AType {
|
||||
return storedTypes[idx];
|
||||
}
|
||||
|
||||
static function wrapType(e:Expr, type:AType):Expr {
|
||||
return mk(EMeta(":$type", [mk(EConst(CInt(setStoredType(type))), e)], e), e);
|
||||
}
|
||||
|
||||
private static function _process(e:Expr, top:Bool = false):Expr {
|
||||
if(e == null)
|
||||
return null;
|
||||
|
||||
// If stuff looks wrong, add this back
|
||||
//e = Tools.map(e, function(e) {
|
||||
// return _process(e, false);
|
||||
//});
|
||||
|
||||
e = endProcess(e);
|
||||
|
||||
if(e == null) return null;
|
||||
|
||||
var ge = e;
|
||||
|
||||
switch(expr(e)) {
|
||||
case EVar(vname, t, e, p, s):
|
||||
var type = null;
|
||||
switch(Tools.expr(e)) {
|
||||
case ENew(n, _):
|
||||
type = n;
|
||||
case EIdent(n): // i dont think this worksc
|
||||
// var a = Abstract;
|
||||
// var b = a; // Gets detected
|
||||
type = getDeclaredType(n).name;
|
||||
default:
|
||||
}
|
||||
|
||||
if(t != null) {
|
||||
type = Printer.convertTypeToString(t);
|
||||
}
|
||||
|
||||
if(type != null) {
|
||||
var v = getDeclaredType(type);
|
||||
if(v != null) {
|
||||
addDeclared(vname, VVar(vname, v));
|
||||
trace("Adding var " + vname + " with type " + v.name);
|
||||
//return mk(EField(mk(EIdent(v.name), e), "new", p), e);
|
||||
}
|
||||
} else {
|
||||
trace("No type for " + vname + " " + t);
|
||||
addDeclared(vname, VUnknown);
|
||||
}
|
||||
|
||||
// TODO: make this not be only inside EVar
|
||||
var e = switch(Tools.expr(e)) {
|
||||
case ENew(n, args):
|
||||
var type = getDeclaredType(n);
|
||||
if(type == null) return endProcess(ge);
|
||||
|
||||
var stype = t != null ? Printer.convertTypeToString(t) : null;
|
||||
trace("New " + n + " " + stype);
|
||||
|
||||
switch(type.inf.type) {
|
||||
case TAbstract(at, impl):
|
||||
var helper:AbstractDataHelper = new AbstractDataHelper(type.name, at);
|
||||
var constructor = helper.getFuncForConstructor();
|
||||
trace("Converting new call to " + constructor);
|
||||
_process(mk(
|
||||
ECall(
|
||||
mk(
|
||||
EField(
|
||||
mk(
|
||||
EIdent(getFieldName(impl)),
|
||||
e
|
||||
),
|
||||
constructor),
|
||||
e), args
|
||||
), e
|
||||
));
|
||||
default: null;
|
||||
}
|
||||
default: null;
|
||||
};
|
||||
if(e != null)
|
||||
return mk(EVar(vname, t, e, p, s), ge);
|
||||
case EField(e, f, p):
|
||||
var typeInfo = switch(Tools.expr(e)) {
|
||||
case EIdent(n):
|
||||
var a = getDeclared(n);
|
||||
if(a != null) {
|
||||
switch(a.type) {
|
||||
case VVar(name, type): type.inf.type;
|
||||
default: null;
|
||||
}
|
||||
} else {
|
||||
null;
|
||||
}
|
||||
case EMeta(":$type", [expr(_) => EConst(CInt(t))], _):
|
||||
getStoredType(t);
|
||||
default:
|
||||
null;
|
||||
}
|
||||
if(typeInfo == null) return endProcess(ge);
|
||||
trace("EField " + e + " " + f);
|
||||
trace("Type: " + typeInfo);
|
||||
|
||||
var e = switch(typeInfo) {
|
||||
case TAbstract(at, impl):
|
||||
var helper:AbstractDataHelper = new AbstractDataHelper(impl, at);
|
||||
|
||||
var resolve = helper.getResolveFunc();
|
||||
trace("Resolve func: " + resolve);
|
||||
if(resolve != null) { // a.b -> a.resolve(b);
|
||||
var returnType = helper.getTypeOfFunc(resolve);
|
||||
var retType = findAbstractFromFieldName(returnType);
|
||||
trace("Expr: " + Printer.toString(e));
|
||||
|
||||
var field = mk(EField(mk(EIdent(getFieldName(impl)), e), resolve, p), e);
|
||||
var call = mk(ECall(field, [e, mk(EConst(CString(f)), e)]), e);
|
||||
|
||||
if(retType != null) {
|
||||
call = wrapType(call, retType);
|
||||
}
|
||||
return endProcess(call);
|
||||
}
|
||||
|
||||
// access.has -> Access_Impl_.get_has()
|
||||
var v = helper.getProps(f);
|
||||
if(v != null) // get
|
||||
{
|
||||
var getterType = helper.getTypeOfFunc(v[0]);
|
||||
if(getterType != null) {
|
||||
trace("Getter: " + getterType);
|
||||
trace("v:" + v);
|
||||
|
||||
var retType = findAbstractFromFieldName(getterType);
|
||||
|
||||
var field = mk(EField(mk(EIdent(getFieldName(impl)), e), v[0], p), e);
|
||||
var call = mk(ECall(field, [e]), e);
|
||||
var meta = wrapType(call, retType);
|
||||
|
||||
trace("Expr: " + Printer.toString(meta));
|
||||
endProcess(meta);
|
||||
} else {
|
||||
null;
|
||||
}
|
||||
}
|
||||
else
|
||||
null;
|
||||
default: null;
|
||||
}
|
||||
|
||||
if(e != null)
|
||||
return e;
|
||||
case EImport(n, m):
|
||||
var abstractImport = getAbstractImport(n, m);
|
||||
if(abstractImport != null) {
|
||||
addImport(abstractImport, m);
|
||||
if(n == "haxe.xml.Access") {
|
||||
var toImport = [
|
||||
"haxe.xml.Access.NodeAccess",
|
||||
"haxe.xml.Access.AttribAccess",
|
||||
"haxe.xml.Access.HasAttribAccess",
|
||||
"haxe.xml.Access.HasNodeAccess",
|
||||
"haxe.xml.Access.NodeListAccess",
|
||||
];
|
||||
for(n in toImport) {
|
||||
//var m = INormal;//IAs("$" + n);
|
||||
//addImport(getAbstractImport(n, m), m);
|
||||
unusedImports.set(getFieldName(n), n);
|
||||
}
|
||||
}
|
||||
return null;//mk(abstractImport, e);
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
static function endProcess(e:Expr):Expr {
|
||||
if(e == null) return null;
|
||||
var ge = e;
|
||||
return Tools.map(e, function(e) {
|
||||
if(e == null) return mk(EBlock([]), ge); // Hacky
|
||||
switch(Tools.expr(e)) {
|
||||
case EBlock(exprs):
|
||||
depth++;
|
||||
for(i in 0...exprs.length) {
|
||||
exprs[i] = _process(exprs[i]);
|
||||
}
|
||||
while(exprs.contains(null)) exprs.remove(null);
|
||||
depth--;
|
||||
removeUnusedDeclared();
|
||||
return mk(EBlock(exprs), e);
|
||||
case EFunction(args, expr, name, a, isPublic, isStatic, isOverride):
|
||||
depth++;
|
||||
_process(expr);
|
||||
//for(i in 0...expr.length) {
|
||||
// expr[i] = _process(expr[i]);
|
||||
//}
|
||||
depth--;
|
||||
removeUnusedDeclared();
|
||||
return mk(EFunction(args, expr, name, a, isPublic, isStatic, isOverride), e);
|
||||
default:
|
||||
}
|
||||
return _process(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;
|
||||
}
|
||||
}
|
||||
|
||||
class AbstractDataHelper {
|
||||
public var data:AbstractData;
|
||||
|
||||
public function new(name:String, ?cls: Class<Any>) {
|
||||
data = AbstractProcessor.loadAbstractData(name, cls);
|
||||
}
|
||||
|
||||
// [name, args, ret, access, special, ?op]
|
||||
|
||||
public function getProps(fieldName:String):Array<String> {
|
||||
var prop = data.props;
|
||||
for(v in prop) {
|
||||
if(v[0] == fieldName) {
|
||||
return [v[1], v[2]];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getResolveFunc(?wantedType:Null<String>):String {
|
||||
var t = getFuncForOp("a.b", wantedType);
|
||||
if(t != null) return t;
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getTypeOfFunc(f:String):String {
|
||||
var funcs = data.funcs;
|
||||
for(func in funcs) {
|
||||
if(func[0] == f) {
|
||||
return func[2];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getFuncForOp(op:String, ?wantedType:Null<String>):String {
|
||||
var funcs = data.funcs;
|
||||
for(func in funcs) {
|
||||
if(func[4] & 16 != 0) {
|
||||
if(func[5] == op) {
|
||||
if(wantedType != null) {
|
||||
var types:Array<String> = func[1].split("|");
|
||||
var wtypes:Array<String> = wantedType.split("|");
|
||||
if(types.length != wtypes.length) continue;
|
||||
var didBreak = false;
|
||||
for(i=>type in types) {
|
||||
if(type != wtypes[i]) {
|
||||
didBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(didBreak) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return func[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getFuncForArrayRead(?wantedType:Null<String>):String {
|
||||
var funcs = data.funcs;
|
||||
for(func in funcs) {
|
||||
if(func[4] & 1 != 0) {
|
||||
if(wantedType != null) {
|
||||
var types:Array<String> = func[1].split("|");
|
||||
var wtypes:Array<String> = wantedType.split("|");
|
||||
if(types.length != wtypes.length) continue;
|
||||
var didBreak = false;
|
||||
for(i=>type in types) {
|
||||
if(type != wtypes[i]) {
|
||||
didBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(didBreak) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return func[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getFuncForArrayWrite(?wantedType:Null<String>):String {
|
||||
var funcs = data.funcs;
|
||||
for(func in funcs) {
|
||||
if(func[4] & 2 != 0) {
|
||||
if(wantedType != null) {
|
||||
var types:Array<String> = func[1].split("|");
|
||||
var wtypes:Array<String> = wantedType.split("|");
|
||||
if(types.length != wtypes.length) continue;
|
||||
var didBreak = false;
|
||||
for(i=>type in types) {
|
||||
if(type != wtypes[i]) {
|
||||
didBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(didBreak) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return func[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getFuncForConstructor(?wantedType:Null<String>):String {
|
||||
var funcs = data.funcs;
|
||||
for(func in funcs) {
|
||||
if(func[4] & 4 != 0) {
|
||||
if(wantedType != null) {
|
||||
var types:Array<String> = func[1].split("|");
|
||||
var wtypes:Array<String> = wantedType.split("|");
|
||||
if(types.length != wtypes.length) continue;
|
||||
var didBreak = false;
|
||||
for(i=>type in types) {
|
||||
if(type != wtypes[i]) {
|
||||
didBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(didBreak) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return func[0];
|
||||
}
|
||||
}
|
||||
for(func in funcs) {
|
||||
if(func[0] == "_new")
|
||||
return func[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getFuncForToConversion(?wantedType:Null<String>):Array<Dynamic> {
|
||||
var funcs = data.funcs;
|
||||
for(func in funcs) {
|
||||
if(func[4] & 8 != 0) {
|
||||
if(wantedType != null) {
|
||||
var types = func[2];
|
||||
var wtypes = wantedType;
|
||||
if(types != wantedType)
|
||||
continue;
|
||||
}
|
||||
return func[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.Interp.DeclaredVar;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
@@ -11,27 +14,28 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
public var extend:String;
|
||||
public var interfaces:Array<String>;
|
||||
|
||||
public var cl:Class<Dynamic>;
|
||||
|
||||
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
|
||||
this.ogInterp = ogInterp;
|
||||
this.name = name;
|
||||
this.fields = fields;
|
||||
this.extend = extend;
|
||||
this.interfaces = interfaces;
|
||||
|
||||
this.cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
if(cl == null)
|
||||
ogInterp.error(EInvalidClass(extend));
|
||||
}
|
||||
|
||||
public function hnew(args:Array<Dynamic>):Dynamic {
|
||||
var interp = new Interp();
|
||||
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
|
||||
var cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
if(cl == null)
|
||||
ogInterp.error(EInvalidClass(extend));
|
||||
|
||||
var _class = Type.createInstance(cl, args);
|
||||
var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
|
||||
|
||||
var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||
var capturedLocals:Map<String, {r:Dynamic, depth:Int}> = [];
|
||||
var capturedLocals:Map<String, DeclaredVar> = [];
|
||||
for(k=>e in __capturedLocals)
|
||||
if (e != null && e.depth <= 0)
|
||||
capturedLocals.set(k, e);
|
||||
@@ -49,6 +53,8 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
_class.__custom__variables = interp.variables;
|
||||
|
||||
for(expr in fields) {
|
||||
@:privateAccess
|
||||
interp.exprReturn(expr);
|
||||
@@ -56,16 +62,20 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
|
||||
interp.variables.set("super", staticHandler);
|
||||
|
||||
_class.__interp = interp;
|
||||
_class.__interp = interp; // TODO: Remove
|
||||
interp.scriptObject = _class;
|
||||
|
||||
var newFunc = interp.variables.get("new");
|
||||
if(newFunc != null) {
|
||||
Reflect.callMethod(null, newFunc, args);
|
||||
}
|
||||
|
||||
for(variable => value in interp.variables) {
|
||||
if(variable == "this") continue;
|
||||
|
||||
if(variable.startsWith("set_") || variable.startsWith("get_")) {
|
||||
_class.__allowSetGet = false;
|
||||
}
|
||||
}
|
||||
|
||||
var newFunc = interp.variables.get("new");
|
||||
if(newFunc != null) {
|
||||
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
|
||||
}
|
||||
|
||||
return _class;
|
||||
@@ -76,26 +86,41 @@ class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateClass implements IHScriptCustomBehaviour {
|
||||
public var __interp:Interp;
|
||||
class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour {
|
||||
public var __interp:Interp; // TODO: Remove
|
||||
public var __custom__variables:Map<String, Dynamic>;
|
||||
public var __allowSetGet:Bool = true;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(this.__interp.variables.exists("set_" + name)) {
|
||||
return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function
|
||||
}
|
||||
if (this.__interp.variables.exists(name)) {
|
||||
this.__interp.variables.set(name, val);
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
Reflect.setProperty(this, name, val);
|
||||
return Reflect.field(this, name);
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
if(this.__interp.variables.exists("get_" + name))
|
||||
return this.__interp.variables.get("get_" + name)();
|
||||
if (this.__interp.variables.exists(name))
|
||||
return this.__interp.variables.get(name);
|
||||
return Reflect.getProperty(this, name);
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
|
||||
public function __callGetter(name:String):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
|
||||
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(Int) from Int to Int {
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
-73
@@ -30,67 +30,19 @@ enum Const {
|
||||
#end
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
typedef Expr = {
|
||||
var e : ExprDef;
|
||||
var pmin : Int;
|
||||
var pmax : Int;
|
||||
var origin : String;
|
||||
var line : Int;
|
||||
}
|
||||
enum ExprDef {
|
||||
#else
|
||||
typedef ExprDef = Expr;
|
||||
enum Expr {
|
||||
#end
|
||||
EConst( c : Const );
|
||||
EIdent( v : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool );
|
||||
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 );
|
||||
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<{ name : String, e : Expr }> );
|
||||
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
|
||||
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?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> );
|
||||
enum MapType {
|
||||
IntMap;
|
||||
StringMap;
|
||||
EnumMap;
|
||||
ObjectMap;
|
||||
UnknownMap;
|
||||
}
|
||||
|
||||
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
|
||||
|
||||
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 );
|
||||
}
|
||||
typedef Error = hscript.Error.Error_;
|
||||
|
||||
#if hscriptPos
|
||||
class Error {
|
||||
public var e : ErrorDef;
|
||||
class Expr {
|
||||
public var e : ExprDef;
|
||||
public var pmin : Int;
|
||||
public var pmax : Int;
|
||||
public var origin : String;
|
||||
@@ -102,28 +54,102 @@ class Error {
|
||||
this.origin = origin;
|
||||
this.line = line;
|
||||
}
|
||||
public function toString(): String {
|
||||
return Printer.errorToString(this);
|
||||
|
||||
public function toString() {
|
||||
return "<Expr " + e + ">";
|
||||
}
|
||||
}
|
||||
enum ErrorDef {
|
||||
enum ExprDef
|
||||
#else
|
||||
enum Error {
|
||||
typedef ExprDef = Expr;
|
||||
enum Expr
|
||||
#end
|
||||
EInvalidChar( c : Int );
|
||||
EUnexpected( s : String );
|
||||
EUnterminatedString;
|
||||
EUnterminatedComment;
|
||||
EInvalidPreprocessor( msg : String );
|
||||
EUnknownVariable( v : String );
|
||||
EInvalidIterator( v : String );
|
||||
EInvalidOp( op : String );
|
||||
EInvalidAccess( f : String );
|
||||
ECustom( msg : String );
|
||||
EInvalidClass( className : String);
|
||||
EAlreadyExistingClass( className : String);
|
||||
{
|
||||
EConst( c : Const );
|
||||
EIdent( v : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool );
|
||||
EParent( e : Expr, ?noOptimize : Bool );
|
||||
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);
|
||||
EForKeyValue( 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 );
|
||||
EReturn( ?e : Expr );
|
||||
EArray( e : Expr, index : Expr );
|
||||
EMapDecl( type: MapType, keys: Array<Expr>, values: Array<Expr> );
|
||||
EArrayDecl( e : Array<Expr> );
|
||||
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, mode: KImportMode );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
|
||||
}
|
||||
|
||||
enum KImportMode {
|
||||
INormal;
|
||||
IAs( name : String );
|
||||
IAll;
|
||||
}
|
||||
|
||||
class ObjectField {
|
||||
public var name:String;
|
||||
public var e:Expr;
|
||||
|
||||
public function new(name, e) {
|
||||
this.name = name;
|
||||
this.e = e;
|
||||
}
|
||||
}
|
||||
|
||||
class SwitchCase {
|
||||
public var values : Array<Expr>;
|
||||
public var expr : Expr;
|
||||
|
||||
public function new(values, expr) {
|
||||
this.values = values;
|
||||
this.expr = expr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
|
||||
class Argument {
|
||||
public var name : String;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
DPackage( path : Array<String> );
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package hscript;
|
||||
|
||||
import hscript.Interp;
|
||||
|
||||
interface IHScriptCustomClassBehaviour {
|
||||
public var __interp:Interp; // TODO: Remove
|
||||
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;
|
||||
}
|
||||
+326
-230
@@ -28,12 +28,13 @@
|
||||
*/
|
||||
package hscript;
|
||||
|
||||
import haxe.iterators.StringKeyValueIteratorUnicode;
|
||||
import haxe.EnumTools;
|
||||
import haxe.display.Protocol.InitializeResult;
|
||||
import haxe.PosInfos;
|
||||
import hscript.Expr;
|
||||
import haxe.Constraints.IMap;
|
||||
import haxe.EnumTools;
|
||||
import haxe.PosInfos;
|
||||
import haxe.display.Protocol.InitializeResult;
|
||||
import haxe.iterators.StringKeyValueIteratorUnicode;
|
||||
import hscript.Expr;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -43,21 +44,61 @@ private enum Stop {
|
||||
SReturn;
|
||||
}
|
||||
|
||||
enum abstract ScriptObjectType(Int) {
|
||||
var SClass;
|
||||
var SObject;
|
||||
var SStaticClass;
|
||||
var SNull;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
class DeclaredVar {
|
||||
public var r:Dynamic;
|
||||
public var depth:Int;
|
||||
}
|
||||
|
||||
@:structInit
|
||||
class RedeclaredVar {
|
||||
public var n:String;
|
||||
public var old:DeclaredVar;
|
||||
public var depth:Int;
|
||||
}
|
||||
|
||||
class Interp {
|
||||
public var scriptObject(default, set):Dynamic;
|
||||
private var _hasScriptObject(default, null):Bool = false;
|
||||
private var _scriptObjectType(default, null):ScriptObjectType = SNull;
|
||||
public function set_scriptObject(v:Dynamic) {
|
||||
__instanceFields = (v == null) ? [] : Type.getInstanceFields(Type.getClass(v));
|
||||
switch(Type.typeof(v)) {
|
||||
case TClass(c): // Class Access
|
||||
__instanceFields = Type.getInstanceFields(c);
|
||||
_scriptObjectType = SClass;
|
||||
case TObject: // Object Access or Static Class Access
|
||||
var cls = Type.getClass(v);
|
||||
switch(Type.typeof(cls)) {
|
||||
case TClass(c): // Static Class Access
|
||||
__instanceFields = Type.getInstanceFields(c);
|
||||
_scriptObjectType = SStaticClass;
|
||||
default: // Object Access
|
||||
__instanceFields = Reflect.fields(v);
|
||||
_scriptObjectType = SObject;
|
||||
}
|
||||
default: // Null or other
|
||||
__instanceFields = [];
|
||||
_scriptObjectType = SNull;
|
||||
}
|
||||
_hasScriptObject = v != null;
|
||||
return scriptObject = v;
|
||||
}
|
||||
public var errorHandler:Error->Void;
|
||||
public var importFailedCallback:Array<String>->Bool;
|
||||
#if haxe3
|
||||
#if haxe3 // Also Applies to 4.0.0 and above
|
||||
public var customClasses:Map<String, Dynamic>;
|
||||
public var variables:Map<String, Dynamic>;
|
||||
public var publicVariables:Map<String, Dynamic>;
|
||||
public var staticVariables:Map<String, Dynamic>;
|
||||
|
||||
public var locals:Map<String, {r:Dynamic, depth:Int}>;
|
||||
public var locals:Map<String, DeclaredVar>;
|
||||
var binops:Map<String, Expr->Expr->Dynamic>;
|
||||
#else
|
||||
public var customClasses:Hash<Dynamic>;
|
||||
@@ -65,13 +106,13 @@ class Interp {
|
||||
public var publicVariables:Hash<Dynamic>;
|
||||
public var staticVariables:Hash<Dynamic>;
|
||||
|
||||
public var locals:Hash<{r:Dynamic, depth:Int}>;
|
||||
public var locals:Hash<DeclaredVar>;
|
||||
var binops:Hash<Expr->Expr->Dynamic>;
|
||||
#end
|
||||
|
||||
var depth:Int = 0;
|
||||
var inTry:Bool;
|
||||
var declared:Array<{n:String, old:{r:Dynamic, depth:Int}, depth:Int}>;
|
||||
var declared:Array<RedeclaredVar>;
|
||||
var returnValue:Dynamic;
|
||||
|
||||
var isBypassAccessor:Bool = false;
|
||||
@@ -117,6 +158,14 @@ class Interp {
|
||||
variables.set("null", null);
|
||||
variables.set("true", true);
|
||||
variables.set("false", false);
|
||||
#if !NO_FROM_CHAR_CODE_FIX
|
||||
if(!Preprocessor.isStringFromCharCodeFixed) {
|
||||
// DONT CALL THIS DIRECTLY, USE String.fromCharCode, the preprocessor will call it for you
|
||||
variables.set("__StringWorkaround__fromCharCode", function(a:Int) { // TODO: make hscript only add this if its used
|
||||
return String.fromCharCode(a);
|
||||
});
|
||||
}
|
||||
#end
|
||||
variables.set("trace", Reflect.makeVarArgs(function(el) {
|
||||
var inf = posInfos();
|
||||
var v = el.shift();
|
||||
@@ -183,14 +232,13 @@ class Interp {
|
||||
assignOp("<<=", function(v1, v2) return v1 << v2);
|
||||
assignOp(">>=", function(v1, v2) return v1 >> v2);
|
||||
assignOp(">>>=", function(v1, v2) return v1 >>> v2);
|
||||
assignOp("??=", function(v1, v2) return v1 == null ? v2 : v1);
|
||||
assignOp("??"+"=", function(v1, v2) return v1 == null ? v2 : v1);
|
||||
}
|
||||
|
||||
function checkIsType(e1,e2): Bool {
|
||||
var expr1:Dynamic = expr(e1);
|
||||
|
||||
return switch(Tools.expr(e2))
|
||||
{
|
||||
return switch(Tools.expr(e2)) {
|
||||
case EIdent("Class"):
|
||||
Std.isOfType(expr1, Class);
|
||||
case EIdent("Map") | EIdent("IMap"):
|
||||
@@ -216,20 +264,21 @@ class Interp {
|
||||
case EIdent(id):
|
||||
var l = locals.get(id);
|
||||
if (l == null) {
|
||||
if (!variables.exists(id) && !staticVariables.exists(id) && !publicVariables.exists(id) && scriptObject != null) {
|
||||
if (Type.typeof(scriptObject) == TObject) {
|
||||
Reflect.setField(scriptObject, id, v);
|
||||
if (!variables.exists(id) && !staticVariables.exists(id) && !publicVariables.exists(id) && _hasScriptObject) {
|
||||
if (_scriptObjectType == SObject) {
|
||||
UnsafeReflect.setField(scriptObject, id, v);
|
||||
} else {
|
||||
if (isBypassAccessor) {
|
||||
if (__instanceFields.contains(id)) {
|
||||
Reflect.setField(scriptObject, id, v);
|
||||
UnsafeReflect.setField(scriptObject, id, v);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
if (__instanceFields.contains(id)) {
|
||||
Reflect.setProperty(scriptObject, id, v);
|
||||
UnsafeReflect.setProperty(scriptObject, id, v);
|
||||
} else if (__instanceFields.contains('set_$id')) { // setter
|
||||
Reflect.getProperty(scriptObject, 'set_$id')(v);
|
||||
UnsafeReflect.getProperty(scriptObject, 'set_$id')(v);
|
||||
} else {
|
||||
setVar(id, v);
|
||||
}
|
||||
@@ -275,10 +324,16 @@ class Interp {
|
||||
var l = locals.get(id);
|
||||
v = fop(expr(e1), expr(e2));
|
||||
if (l == null) {
|
||||
if (__instanceFields.contains(id)) {
|
||||
Reflect.setProperty(scriptObject, id, v);
|
||||
} else if (__instanceFields.contains('set_$id')) { // setter
|
||||
Reflect.getProperty(scriptObject, 'set_$id')(v);
|
||||
if(_hasScriptObject) {
|
||||
if(_scriptObjectType == SObject) {
|
||||
UnsafeReflect.setField(scriptObject, id, v);
|
||||
} else if (__instanceFields.contains(id)) {
|
||||
UnsafeReflect.setProperty(scriptObject, id, v);
|
||||
} else if (__instanceFields.contains('set_$id')) { // setter
|
||||
UnsafeReflect.getProperty(scriptObject, 'set_$id')(v);
|
||||
} else {
|
||||
setVar(id, v);
|
||||
}
|
||||
} else {
|
||||
setVar(id, v);
|
||||
}
|
||||
@@ -294,8 +349,10 @@ class Interp {
|
||||
var arr:Dynamic = expr(e);
|
||||
var index:Dynamic = expr(index);
|
||||
if (isMap(arr)) {
|
||||
v = fop(getMapValue(arr, index), expr(e2));
|
||||
setMapValue(arr, index, v);
|
||||
var map = getMap(arr);
|
||||
|
||||
v = fop(map.get(index), expr(e2));
|
||||
map.set(index, v);
|
||||
} else {
|
||||
v = fop(arr[index], expr(e2));
|
||||
arr[index] = v;
|
||||
@@ -340,12 +397,13 @@ class Interp {
|
||||
var arr:Dynamic = expr(e);
|
||||
var index:Dynamic = expr(index);
|
||||
if (isMap(arr)) {
|
||||
var v = getMapValue(arr, index);
|
||||
var map = getMap(arr);
|
||||
var v = map.get(index);
|
||||
if (prefix) {
|
||||
v += delta;
|
||||
setMapValue(arr, index, v);
|
||||
map.set(index, v);
|
||||
} else {
|
||||
setMapValue(arr, index, v + delta);
|
||||
map.set(index, v + delta);
|
||||
}
|
||||
return v;
|
||||
} else {
|
||||
@@ -422,7 +480,7 @@ class Interp {
|
||||
}
|
||||
}
|
||||
|
||||
public inline function error(e:#if hscriptPos ErrorDef #else Error #end, rethrow = false):Dynamic {
|
||||
public inline function error(e:#if hscriptPos Error.ErrorDef #else Error #end, rethrow = false):Dynamic {
|
||||
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
|
||||
|
||||
if (rethrow) {
|
||||
@@ -449,28 +507,44 @@ class Interp {
|
||||
if (l != null)
|
||||
return l.r;
|
||||
|
||||
var v = variables.get(id);
|
||||
for(map in [variables, publicVariables, staticVariables, customClasses])
|
||||
if (map.exists(id))
|
||||
return map[id];
|
||||
|
||||
if (scriptObject != null) {
|
||||
if (_hasScriptObject) {
|
||||
// search in object
|
||||
if (id == "this") {
|
||||
return scriptObject;
|
||||
} else if ((Type.typeof(scriptObject) == TObject) && Reflect.hasField(scriptObject, id)) {
|
||||
return Reflect.field(scriptObject, id);
|
||||
} else if (_scriptObjectType == SObject && UnsafeReflect.hasField(scriptObject, id)) {
|
||||
return UnsafeReflect.field(scriptObject, id);
|
||||
} else {
|
||||
if (__instanceFields.contains(id)) {
|
||||
return Reflect.getProperty(scriptObject, id);
|
||||
return UnsafeReflect.getProperty(scriptObject, id);
|
||||
} else if (__instanceFields.contains('get_$id')) { // getter
|
||||
return Reflect.getProperty(scriptObject, 'get_$id')();
|
||||
return UnsafeReflect.getProperty(scriptObject, 'get_$id')();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (doException)
|
||||
error(EUnknownVariable(id));
|
||||
return v;
|
||||
//var v = variables.get(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
function getClass(c:String):haxe.ds.Either<Class<Any>, Enum<Any>> {
|
||||
if (importBlocklist.contains(c))
|
||||
return null;
|
||||
|
||||
var en = Type.resolveEnum(c);
|
||||
if(en != null)
|
||||
return Right(en);
|
||||
|
||||
var cl = Type.resolveClass(c);
|
||||
if (cl == null)
|
||||
cl = Type.resolveClass(c + '_HSC');
|
||||
if(cl != null)
|
||||
return Left(cl);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function expr(e:Expr):Dynamic {
|
||||
@@ -490,74 +564,67 @@ class Interp {
|
||||
return variable == null ? thing : Type.getClassName(variable);
|
||||
}
|
||||
customClasses.set(name, new CustomClassHandler(this, name, fields, importVar(extend), [for (i in interfaces) importVar(i)]));
|
||||
case EImport(c, n):
|
||||
if (!importEnabled)
|
||||
case EImport(c, mode):
|
||||
if (!importEnabled) return null;
|
||||
|
||||
if(mode == IAll) {
|
||||
throw "TODO";
|
||||
return null;
|
||||
var splitClassName = [for (e in c.split(".")) e.trim()];
|
||||
var realClassName = splitClassName.join(".");
|
||||
var claVarName = splitClassName[splitClassName.length - 1];
|
||||
var toSetName = n != null ? n : claVarName;
|
||||
var oldClassName = realClassName;
|
||||
var oldSplitName = splitClassName.copy();
|
||||
}
|
||||
|
||||
if (variables.exists(toSetName)) // class is already imported
|
||||
return null;
|
||||
var splitClassName:Array<String> = c.split(".");
|
||||
if (splitClassName.length <= 0) return null;
|
||||
|
||||
if (importBlocklist.contains(realClassName))
|
||||
return null;
|
||||
var cl = Type.resolveClass(realClassName);
|
||||
if (cl == null)
|
||||
cl = Type.resolveClass('${realClassName}_HSC');
|
||||
var lastName:String = splitClassName[splitClassName.length-1];
|
||||
var varName:String = switch(mode) {
|
||||
case IAs(name): name;
|
||||
default: lastName;
|
||||
};
|
||||
|
||||
var en = Type.resolveEnum(realClassName);
|
||||
// Class is already imported
|
||||
if (variables.exists(varName))
|
||||
return variables.get(varName);
|
||||
|
||||
//trace(realClassName, cl, en, splitClassName);
|
||||
// Orginal class
|
||||
var importedClass = getClass(c);
|
||||
|
||||
// Allow for flixel.ui.FlxBar.FlxBarFillDirection;
|
||||
if (cl == null && en == null) {
|
||||
if(splitClassName.length > 1) {
|
||||
splitClassName.splice(-2, 1); // Remove the last last item
|
||||
realClassName = splitClassName.join(".");
|
||||
if (importedClass == null) {
|
||||
var newClassName:Array<String> = splitClassName.copy();
|
||||
newClassName.splice(-2, 1); // Remove the last last item
|
||||
|
||||
if (importBlocklist.contains(realClassName))
|
||||
return null;
|
||||
|
||||
cl = Type.resolveClass(realClassName);
|
||||
if (cl == null)
|
||||
cl = Type.resolveClass('${realClassName}_HSC');
|
||||
|
||||
en = Type.resolveEnum(realClassName);
|
||||
|
||||
//trace(realClassName, cl, en, splitClassName);
|
||||
}
|
||||
importedClass = getClass(newClassName.join("."));
|
||||
}
|
||||
|
||||
if (cl == null && en == null) {
|
||||
if (importFailedCallback == null || !importFailedCallback(oldSplitName))
|
||||
error(EInvalidClass(oldClassName));
|
||||
} else {
|
||||
if (en != null) {
|
||||
// ENUM!!!!
|
||||
var enumThingy = {};
|
||||
for (c in en.getConstructors()) {
|
||||
try {
|
||||
Reflect.setField(enumThingy, c, en.createByName(c));
|
||||
} catch(e) {
|
||||
try {
|
||||
Reflect.setField(enumThingy, c, Reflect.field(en, c));
|
||||
} catch(ex) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
variables.set(toSetName, enumThingy);
|
||||
} else {
|
||||
variables.set(toSetName, cl);
|
||||
}
|
||||
// Allow for Std.isOfType;
|
||||
var isField:Bool = false;
|
||||
if (importedClass == null) {
|
||||
importedClass = getClass(c.substring(0, c.lastIndexOf(".")));
|
||||
isField = true;
|
||||
}
|
||||
|
||||
// Import the .isOfType
|
||||
if (importedClass != null) {
|
||||
var classOrEnum:Dynamic = switch(importedClass) {
|
||||
case Left(e): e;
|
||||
case Right(e): Tools.getEnum(e);
|
||||
};
|
||||
if(isField) {
|
||||
var v:Dynamic = UnsafeReflect.getProperty(classOrEnum, lastName);
|
||||
if(v == null) v = UnsafeReflect.field(classOrEnum, lastName);
|
||||
if(v == null) error(EInvalidAccess(lastName, c));
|
||||
|
||||
variables.set(varName, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
variables.set(varName, classOrEnum);
|
||||
return classOrEnum;
|
||||
}
|
||||
|
||||
if (importFailedCallback == null || !importFailedCallback(c.split("."))) // Incase of custom import
|
||||
error(EInvalidClass(c));
|
||||
return null;
|
||||
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
case CInt(v): return v;
|
||||
@@ -574,7 +641,7 @@ class Interp {
|
||||
locals.set(n, {r: (e == null) ? null : expr(e), depth: depth});
|
||||
if (depth == 0) {
|
||||
if(isStatic == true) {
|
||||
if(!staticVariables.exists(n)) {
|
||||
if(!staticVariables.exists(n)) { // dont overwrite existing static variables
|
||||
staticVariables.set(n, locals[n].r);
|
||||
}
|
||||
return null;
|
||||
@@ -598,8 +665,10 @@ class Interp {
|
||||
return get(field, f);
|
||||
case EBinop(op, e1, e2):
|
||||
var fop = binops.get(op);
|
||||
#if debug
|
||||
if (fop == null)
|
||||
error(EInvalidOp(op));
|
||||
#end
|
||||
return fop(e1, e2);
|
||||
case EUnop(op, prefix, e):
|
||||
switch (op) {
|
||||
@@ -622,8 +691,8 @@ class Interp {
|
||||
}
|
||||
case ECall(e, params):
|
||||
var args = new Array();
|
||||
for (p in params)
|
||||
args.push(expr(p));
|
||||
for(i in 0...params.length)
|
||||
args.push(expr(params[i]));
|
||||
|
||||
switch (Tools.expr(e)) {
|
||||
case EField(e, f, s):
|
||||
@@ -644,8 +713,11 @@ class Interp {
|
||||
case EDoWhile(econd, e):
|
||||
doWhileLoop(econd, e);
|
||||
return null;
|
||||
case EFor(v, it, e, ithv):
|
||||
forLoop(v, it, e, ithv);
|
||||
case EFor(v, it, e):
|
||||
forLoop(v, it, e);
|
||||
return null;
|
||||
case EForKeyValue(v, it, e, ithv):
|
||||
forLoopKeyValue(v, it, e, ithv);
|
||||
return null;
|
||||
case EBreak:
|
||||
throw SBreak;
|
||||
@@ -656,7 +728,7 @@ class Interp {
|
||||
throw SReturn;
|
||||
case EFunction(params, fexpr, name, _, isPublic, isStatic, isOverride):
|
||||
var __capturedLocals = duplicate(locals);
|
||||
var capturedLocals:Map<String, {r:Dynamic, depth:Int}> = [];
|
||||
var capturedLocals:Map<String, DeclaredVar> = [];
|
||||
for(k=>e in __capturedLocals)
|
||||
if (e != null && e.depth > 0)
|
||||
capturedLocals.set(k, e);
|
||||
@@ -727,82 +799,60 @@ class Interp {
|
||||
} else {
|
||||
// function-in-function is a local function
|
||||
declared.push({n: name, old: locals.get(name), depth: depth});
|
||||
var ref = {r: f, depth: depth};
|
||||
var ref:DeclaredVar = {r: f, depth: depth};
|
||||
locals.set(name, ref);
|
||||
capturedLocals.set(name, ref); // allow self-recursion
|
||||
}
|
||||
}
|
||||
return f;
|
||||
case EArrayDecl(arr, wantedType):
|
||||
var isMap = false;
|
||||
var isTypeMap = false;
|
||||
if(!isMap && wantedType != null) {
|
||||
isMap = wantedType.match(CTPath(["Map"], [_, _]));
|
||||
isTypeMap = true;
|
||||
} else {
|
||||
isMap = arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _));
|
||||
}
|
||||
if (isMap) {
|
||||
var isAllString:Bool = true;
|
||||
var isAllInt:Bool = true;
|
||||
var isAllObject:Bool = true;
|
||||
var isAllEnum:Bool = true;
|
||||
var keys:Array<Dynamic> = [];
|
||||
var values:Array<Dynamic> = [];
|
||||
for (e in arr) {
|
||||
switch (Tools.expr(e)) {
|
||||
case EBinop("=>", eKey, eValue): {
|
||||
var key:Dynamic = expr(eKey);
|
||||
var value:Dynamic = expr(eValue);
|
||||
isAllString = isAllString && (key is String);
|
||||
isAllInt = isAllInt && (key is Int);
|
||||
isAllObject = isAllObject && Reflect.isObject(key);
|
||||
isAllEnum = isAllEnum && Reflect.isEnumValue(key);
|
||||
keys.push(key);
|
||||
values.push(value);
|
||||
}
|
||||
default: throw("=> expected");
|
||||
}
|
||||
case EMapDecl(type, _keys, _values):
|
||||
var keys:Array<Dynamic> = [];
|
||||
var values:Array<Dynamic> = [];
|
||||
if(type == UnknownMap) {
|
||||
var isKeyString:Bool = false;
|
||||
var isKeyInt:Bool = false;
|
||||
var isKeyObject:Bool = false;
|
||||
var isKeyEnum:Bool = false;
|
||||
for (i in 0..._keys.length) {
|
||||
var key:Dynamic = expr(_keys[i]);
|
||||
var value:Dynamic = expr(_values[i]);
|
||||
|
||||
if(!isKeyString) isKeyString = (key is String);
|
||||
if(!isKeyInt) isKeyInt = (key is Int);
|
||||
if(!isKeyObject) isKeyObject = Reflect.isObject(key);
|
||||
if(!isKeyEnum) isKeyEnum = Reflect.isEnumValue(key);
|
||||
|
||||
keys.push(key);
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
if(isTypeMap) {
|
||||
if(wantedType != null) {
|
||||
isAllString = wantedType.match(CTPath(["Map"], [CTPath(["String"], _), _]));
|
||||
isAllInt = wantedType.match(CTPath(["Map"], [CTPath(["Int"], _), _]));
|
||||
if(isAllString || isAllInt) {
|
||||
isAllObject = false;
|
||||
isAllEnum = false;
|
||||
} else {
|
||||
if(!isAllObject && !isAllEnum) {
|
||||
throw("Unknown Type Key");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var t = b2i(isKeyString) + b2i(isKeyInt) + b2i(isKeyObject) + b2i(isKeyEnum);
|
||||
|
||||
var map:Dynamic = {
|
||||
if (isAllInt)
|
||||
new haxe.ds.IntMap<Dynamic>();
|
||||
else if (isAllString)
|
||||
new haxe.ds.StringMap<Dynamic>();
|
||||
else if (isAllEnum)
|
||||
new haxe.ds.EnumValueMap<Dynamic, Dynamic>();
|
||||
else if (isAllObject)
|
||||
new haxe.ds.ObjectMap<Dynamic, Dynamic>();
|
||||
else
|
||||
throw 'Inconsistent key types';
|
||||
}
|
||||
for (n in 0...keys.length) {
|
||||
setMapValue(map, keys[n], values[n]);
|
||||
}
|
||||
return map;
|
||||
if(t != 1)
|
||||
error(EPreset(UNKNOWN_MAP_TYPE_RUNTIME));
|
||||
else if(isKeyInt) type = IntMap;
|
||||
else if(isKeyString) type = StringMap;
|
||||
else if(isKeyEnum) type = EnumMap;
|
||||
else if(isKeyObject) type = ObjectMap;
|
||||
} else {
|
||||
var a = new Array();
|
||||
for (e in arr) {
|
||||
a.push(expr(e));
|
||||
for(i in 0..._keys.length) {
|
||||
keys.push(expr(_keys[i]));
|
||||
values.push(expr(_values[i]));
|
||||
}
|
||||
return a;
|
||||
}
|
||||
var map:IMap<Dynamic, Dynamic> = switch(type) {
|
||||
case IntMap: new haxe.ds.IntMap<Dynamic>();
|
||||
case StringMap: new haxe.ds.StringMap<Dynamic>();
|
||||
case EnumMap: new haxe.ds.EnumValueMap<Dynamic, Dynamic>();
|
||||
case ObjectMap: new haxe.ds.ObjectMap<Dynamic, Dynamic>();
|
||||
default: null;
|
||||
}
|
||||
for (n in 0...keys.length) {
|
||||
map.set(keys[n], values[n]);
|
||||
}
|
||||
return map;
|
||||
case EArrayDecl(arr):
|
||||
return [for (j in 0...arr.length) expr(arr[j])];
|
||||
case EArray(e, index):
|
||||
var arr:Dynamic = expr(e);
|
||||
var index:Dynamic = expr(index);
|
||||
@@ -813,8 +863,8 @@ class Interp {
|
||||
}
|
||||
case ENew(cl, params):
|
||||
var a = new Array();
|
||||
for (e in params)
|
||||
a.push(expr(e));
|
||||
for (i in 0...params.length)
|
||||
a.push(expr(params[i]));
|
||||
return cnew(cl, a);
|
||||
case EThrow(e):
|
||||
throw expr(e);
|
||||
@@ -844,7 +894,8 @@ class Interp {
|
||||
case EObject(fl):
|
||||
var o = {};
|
||||
for (f in fl)
|
||||
set(o, f.name, expr(f.e));
|
||||
UnsafeReflect.setField(o, f.name, expr(f.e));
|
||||
//set(o, f.name, expr(f.e));
|
||||
return o;
|
||||
case ETernary(econd, e1, e2):
|
||||
return if (expr(econd) == true) expr(e1) else expr(e2);
|
||||
@@ -867,9 +918,8 @@ class Interp {
|
||||
return val;
|
||||
case EMeta(a, b, e):
|
||||
var oldAccessor = isBypassAccessor;
|
||||
if(a == ":bypassAccessor") {
|
||||
if(a == ":bypassAccessor")
|
||||
isBypassAccessor = true;
|
||||
}
|
||||
var val = expr(e);
|
||||
|
||||
isBypassAccessor = oldAccessor;
|
||||
@@ -916,17 +966,11 @@ class Interp {
|
||||
restore(old);
|
||||
}
|
||||
|
||||
function makeIterator(v:Dynamic, ?allowKeyValue = false):Iterator<Dynamic> {
|
||||
function makeIterator(v:Dynamic):Iterator<Dynamic> {
|
||||
#if ((flash && !flash9) || (php && !php7 && haxe_ver < '4.0.0'))
|
||||
if (v.iterator != null)
|
||||
v = v.iterator();
|
||||
#else
|
||||
if(allowKeyValue) {
|
||||
try
|
||||
v = v.keyValueIterator()
|
||||
catch (e:Dynamic) {};
|
||||
}
|
||||
|
||||
if(v.hasNext == null || v.next == null) {
|
||||
try
|
||||
v = v.iterator()
|
||||
@@ -938,18 +982,62 @@ class Interp {
|
||||
return v;
|
||||
}
|
||||
|
||||
function forLoop(n, it, e, ?ithv) {
|
||||
var isKeyValue = ithv != null;
|
||||
function makeKeyValueIterator(v:Dynamic):Iterator<Dynamic> {
|
||||
#if ((flash && !flash9) || (php && !php7 && haxe_ver < '4.0.0'))
|
||||
if (v.keyValueIterator != null)
|
||||
v = v.keyValueIterator();
|
||||
|
||||
if (v.iterator != null)
|
||||
v = v.iterator();
|
||||
|
||||
if (v.hasNext == null || v.next == null)
|
||||
error(EInvalidIterator(v));
|
||||
#else
|
||||
try
|
||||
v = v.keyValueIterator()
|
||||
catch (e:Dynamic) {};
|
||||
|
||||
if (v.hasNext == null || v.next == null)
|
||||
v = makeIterator(v);
|
||||
#end
|
||||
return v;
|
||||
}
|
||||
|
||||
function forLoop(n, it, e) {
|
||||
var old = declared.length;
|
||||
if(isKeyValue)
|
||||
declared.push({n: ithv, old: locals.get(ithv), depth: depth});
|
||||
declared.push({n: n, old: locals.get(n), depth: depth});
|
||||
var it = makeIterator(expr(it), isKeyValue);
|
||||
while (it.hasNext()) {
|
||||
var next = it.next();
|
||||
if(isKeyValue)
|
||||
locals.set(ithv, {r: next.key, depth: depth});
|
||||
locals.set(n, {r: isKeyValue ? next.value : next, depth: depth});
|
||||
var it = makeIterator(expr(it));
|
||||
var _hasNext = it.hasNext;
|
||||
var _next = it.next;
|
||||
while (_hasNext()) {
|
||||
var next = _next();
|
||||
locals.set(n, {r: next, depth: depth});
|
||||
try {
|
||||
expr(e);
|
||||
} catch (err:Stop) {
|
||||
switch (err) {
|
||||
case SContinue:
|
||||
case SBreak:
|
||||
break;
|
||||
case SReturn:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
restore(old);
|
||||
}
|
||||
|
||||
function forLoopKeyValue(n, it, e, ithv) {
|
||||
var old = declared.length;
|
||||
declared.push({n: ithv, old: locals.get(ithv), depth: depth});
|
||||
declared.push({n: n, old: locals.get(n), depth: depth});
|
||||
var it = makeKeyValueIterator(expr(it));
|
||||
var _hasNext = it.hasNext;
|
||||
var _next = it.next;
|
||||
while (_hasNext()) {
|
||||
var next = _next();
|
||||
locals.set(ithv, {r: next.key, depth: depth});
|
||||
locals.set(n, {r: next.value, depth: depth});
|
||||
try {
|
||||
expr(e);
|
||||
} catch (err:Stop) {
|
||||
@@ -969,81 +1057,88 @@ class Interp {
|
||||
return (o is IMap);
|
||||
}
|
||||
|
||||
inline function getMap(map:Dynamic):IMap<Dynamic, Dynamic> {
|
||||
return cast(map, IMap<Dynamic, Dynamic>);
|
||||
}
|
||||
|
||||
inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic {
|
||||
return cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).get(key);
|
||||
return cast(map, IMap<Dynamic, Dynamic>).get(key);
|
||||
}
|
||||
|
||||
inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void {
|
||||
cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).set(key, value);
|
||||
cast(map, IMap<Dynamic, Dynamic>).set(key, value);
|
||||
}
|
||||
|
||||
public static var getRedirects:Map<String, Dynamic->String->Dynamic> = [];
|
||||
public static var setRedirects:Map<String, Dynamic->String->Dynamic->Dynamic> = [];
|
||||
|
||||
private static var _getRedirect:Dynamic->String->Dynamic;
|
||||
private static var _setRedirect:Dynamic->String->Dynamic->Dynamic;
|
||||
|
||||
public var useRedirects:Bool = true;
|
||||
|
||||
static function getClassType(o:Dynamic, ?cls:Class<Any>):Null<String> {
|
||||
return switch (Type.typeof(o)) {
|
||||
case TNull: "Null";
|
||||
case TInt: "Int";
|
||||
case TFloat: "Float";
|
||||
case TBool: "Bool";
|
||||
case _:
|
||||
if (cls == null)
|
||||
cls = Type.getClass(o);
|
||||
cls != null ? Type.getClassName(cls) : null;
|
||||
};
|
||||
}
|
||||
|
||||
function get(o:Dynamic, f:String):Dynamic {
|
||||
if (o == null)
|
||||
error(EInvalidAccess(f));
|
||||
return {
|
||||
var redirect:Dynamic->String->Dynamic = null;
|
||||
var cls = Type.getClass(o);
|
||||
var cl:Null<String> = switch (Type.typeof(o)) {
|
||||
case TNull: "Null";
|
||||
case TInt: "Int";
|
||||
case TFloat: "Float";
|
||||
case TBool: "Bool";
|
||||
case _: cls != null ? Type.getClassName(cls) : null;
|
||||
};
|
||||
if (cl != null && getRedirects.exists(cl) && (redirect = getRedirects[cl]) != null) {
|
||||
return redirect(o, f);
|
||||
} else if (o is IHScriptCustomBehaviour) {
|
||||
var obj = cast(o, IHScriptCustomBehaviour);
|
||||
return obj.hget(f);
|
||||
} else {
|
||||
var v = null;
|
||||
if(isBypassAccessor) {
|
||||
if ((v = Reflect.field(o, f)) == null)
|
||||
v = Reflect.field(cls, f);
|
||||
}
|
||||
|
||||
if(v == null) {
|
||||
if ((v = Reflect.getProperty(o, f)) == null)
|
||||
v = Reflect.getProperty(cls, f);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
var cls = Type.getClass(o);
|
||||
var cl:Null<String> = getClassType(o, cls);
|
||||
if (useRedirects && cl != null && getRedirects.exists(cl) && (_getRedirect = getRedirects[cl]) != null) {
|
||||
return _getRedirect(o, f);
|
||||
} else if (o is IHScriptCustomBehaviour) {
|
||||
var obj = cast(o, IHScriptCustomBehaviour);
|
||||
return obj.hget(f);
|
||||
}
|
||||
|
||||
var v = null;
|
||||
if(isBypassAccessor) {
|
||||
if ((v = UnsafeReflect.field(o, f)) == null)
|
||||
v = Reflect.field(cls, f);
|
||||
}
|
||||
|
||||
if(v == null) {
|
||||
if ((v = UnsafeReflect.getProperty(o, f)) == null)
|
||||
v = Reflect.getProperty(cls, f);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function set(o:Dynamic, f:String, v:Dynamic):Dynamic {
|
||||
if (o == null)
|
||||
error(EInvalidAccess(f));
|
||||
|
||||
var redirect:Dynamic->String->Dynamic->Dynamic = null;
|
||||
var cls = Type.getClass(o);
|
||||
var cl:Null<String> = switch (Type.typeof(o)) {
|
||||
case TNull: "Null";
|
||||
case TInt: "Int";
|
||||
case TFloat: "Float";
|
||||
case TBool: "Bool";
|
||||
case _: cls != null ? Type.getClassName(cls) : null;
|
||||
};
|
||||
if (cl != null && setRedirects.exists(cl) && (redirect = setRedirects[cl]) != null)
|
||||
return redirect(o, f, v);
|
||||
var cl:Null<String> = getClassType(o);
|
||||
if (useRedirects && cl != null && setRedirects.exists(cl) && (_setRedirect = setRedirects[cl]) != null)
|
||||
return _setRedirect(o, f, v);
|
||||
else if (o is IHScriptCustomBehaviour) {
|
||||
var obj = cast(o, IHScriptCustomBehaviour);
|
||||
return obj.hset(f, v);
|
||||
}
|
||||
// Can use unsafe reflect here, since we checked for null above
|
||||
if(isBypassAccessor) {
|
||||
Reflect.setField(o, f, v);
|
||||
UnsafeReflect.setField(o, f, v);
|
||||
} else {
|
||||
Reflect.setProperty(o, f, v);
|
||||
UnsafeReflect.setProperty(o, f, v);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function fcall(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
|
||||
if(o == CustomClassHandler.staticHandler && scriptObject != null) {
|
||||
return Reflect.callMethod(scriptObject, Reflect.field(scriptObject, "_HX_SUPER__" + f), args);
|
||||
if(o == CustomClassHandler.staticHandler && _hasScriptObject) {
|
||||
return UnsafeReflect.callMethodUnsafe(scriptObject, UnsafeReflect.field(scriptObject, "_HX_SUPER__" + f), args);
|
||||
}
|
||||
return call(o, get(o, f), args);
|
||||
}
|
||||
@@ -1052,14 +1147,15 @@ class Interp {
|
||||
if(f == CustomClassHandler.staticHandler) {
|
||||
return null;
|
||||
}
|
||||
return Reflect.callMethod(o, f, args);
|
||||
return UnsafeReflect.callMethodSafe(o, f, args);
|
||||
}
|
||||
|
||||
function cnew(cl:String, args:Array<Dynamic>):Dynamic {
|
||||
var cl:String = cast cl;
|
||||
var c:Dynamic = resolve(cl);
|
||||
if (c == null)
|
||||
c = Type.resolveClass(cl);
|
||||
return (c is IHScriptCustomConstructor) ? cast(c, IHScriptCustomConstructor).hnew(args) : Type.createInstance(c, args);
|
||||
}
|
||||
|
||||
static inline function b2i(b:Bool) return b ? 1 : 0;
|
||||
}
|
||||
|
||||
+18
-2
@@ -198,14 +198,24 @@ class Macro {
|
||||
EWhile(convert(c), convert(e), false);
|
||||
case EFor(v, it, efor):
|
||||
#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));
|
||||
#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));
|
||||
#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
|
||||
case EBreak:
|
||||
EBreak;
|
||||
case EContinue:
|
||||
@@ -229,6 +239,12 @@ class Macro {
|
||||
EReturn(e == null ? null : convert(e));
|
||||
case EArray(e, 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):
|
||||
EArrayDecl(map(el,convert));
|
||||
case ENew(cl, params):
|
||||
|
||||
@@ -0,0 +1,640 @@
|
||||
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("!", 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("!", 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 == "+")
|
||||
return mk(Tools.expr(e2), s);
|
||||
if(compareNumber(c1, 1) && op == "*")
|
||||
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 == "+" && compareNumber(c2, 0))
|
||||
return mk(Tools.expr(e1), s);
|
||||
if(op == "/" && compareNumber(c2, 1))
|
||||
return mk(Tools.expr(e1), s);
|
||||
if(op == "*" && 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 "-": return mk(convertConstant(-constant), s);
|
||||
case "!": return mk(convertConstant(!constant), s);
|
||||
case "~":
|
||||
var complement = #if (neko && !haxe3) haxe.Int32.complement(constant) #else ~constant #end;
|
||||
return mk(convertConstant(complement), s);
|
||||
}
|
||||
}
|
||||
|
||||
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, f1:Dynamic, f2:Dynamic):Dynamic {
|
||||
//trace("Optimizing " + f1 + " " + op + " " + f2);
|
||||
return switch(op) {
|
||||
case "+": f1 + f2;
|
||||
case "-": f1 - f2;
|
||||
case "*": f1 * f2;
|
||||
case "/": f1 / f2;
|
||||
case "%": f1 % f2;
|
||||
case "&": f1 & f2;
|
||||
case "|": f1 | f2;
|
||||
case "^": f1 ^ f2;
|
||||
case "<<": f1 << f2;
|
||||
case ">>": f1 >> f2;
|
||||
case ">>>": f1 >>> f2;
|
||||
case "==": f1 == f2;
|
||||
case "!=": f1 != f2;
|
||||
case ">=": f1 >= f2;
|
||||
case "<=": f1 <= f2;
|
||||
case ">": f1 > f2;
|
||||
case "<": f1 < f2;
|
||||
case "||": f1 == true || f2 == true;
|
||||
case "&&": f1 == true && f2 == true;
|
||||
case "??": f1 == null ? f2 : f1;
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ConstType {
|
||||
CTInt;
|
||||
CTFloat;
|
||||
CTBool;
|
||||
CTString;
|
||||
CTNull;
|
||||
}
|
||||
+789
-224
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
+237
-47
@@ -37,6 +37,11 @@ class Printer {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static function convertExprToString( e : Expr ) {
|
||||
var printer = new Printer();
|
||||
return printer.exprToString(e);
|
||||
}
|
||||
|
||||
public function typeToString( t : CType ) {
|
||||
buf = new StringBuf();
|
||||
tabs = "";
|
||||
@@ -44,6 +49,11 @@ class Printer {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static function convertTypeToString( t : CType ) {
|
||||
var printer = new Printer();
|
||||
return printer.typeToString(t);
|
||||
}
|
||||
|
||||
inline function add<T>(s:T) buf.add(s);
|
||||
|
||||
function type( t : CType ) {
|
||||
@@ -108,16 +118,105 @@ class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
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 expr( e : Expr ) {
|
||||
if( e == null ) {
|
||||
add("??NULL??");
|
||||
return;
|
||||
}
|
||||
switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EImport(c, n):
|
||||
|
||||
// TODO: make else if print correctly
|
||||
|
||||
switch( Tools.expr(e) ) {
|
||||
case EImport(c, mode):
|
||||
add("import " + c);
|
||||
if(n != null)
|
||||
add(' as $n');
|
||||
switch(mode) {
|
||||
case IAs(name): add(' as $name');
|
||||
case IAll: add('.*');
|
||||
default:
|
||||
}
|
||||
case EClass(name, fields, extend, interfaces):
|
||||
add('class $name');
|
||||
if (extend != null)
|
||||
@@ -127,6 +226,7 @@ class Printer {
|
||||
}
|
||||
add(' {\n');
|
||||
tabs += "\t";
|
||||
// TODO: Print fields
|
||||
//for(field in fields) {
|
||||
// expr(field);
|
||||
//}
|
||||
@@ -137,13 +237,31 @@ class Printer {
|
||||
switch( c ) {
|
||||
case CInt(i): add(i);
|
||||
case CFloat(f): add(f);
|
||||
case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"');
|
||||
case CString(s):
|
||||
add('"');
|
||||
add(getEscapedString(s));
|
||||
add('"');
|
||||
}
|
||||
case EIdent(v):
|
||||
add(v);
|
||||
case EVar(n, t, e): // TODO: static, public, override
|
||||
add("var " + n);
|
||||
addType(t);
|
||||
if( t != null )
|
||||
addType(t);
|
||||
else
|
||||
switch( Tools.expr(e) ) {
|
||||
case EMapDecl(type, _, _):
|
||||
add(" : Map<");
|
||||
switch( type ) {
|
||||
case ObjectMap: add("Dynamic");
|
||||
case StringMap: add("String");
|
||||
case EnumMap: add("EnumValue");
|
||||
case IntMap: add("Int");
|
||||
case UnknownMap: add("?");
|
||||
}
|
||||
add(", Dynamic>");
|
||||
default:
|
||||
}
|
||||
if( e != null ) {
|
||||
add(" = ");
|
||||
expr(e);
|
||||
@@ -162,15 +280,46 @@ class Printer {
|
||||
add(";\n");
|
||||
}
|
||||
tabs = tabs.substr(1);
|
||||
add(tabs);
|
||||
add("}");
|
||||
}
|
||||
case EField(e, f, s):
|
||||
expr(e);
|
||||
add((s == true ? "?." : ".") + f);
|
||||
case EBinop(op, e1, e2):
|
||||
expr(e1);
|
||||
add(" " + op + " ");
|
||||
expr(e2);
|
||||
var shouldParen = false;
|
||||
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);
|
||||
|
||||
if(paran == 0 || paran == 2) {
|
||||
add("(");
|
||||
expr(e1);
|
||||
add(")");
|
||||
} else {
|
||||
expr(e1);
|
||||
}
|
||||
if(op == "...")
|
||||
add(op);
|
||||
else
|
||||
add(" " + op + " ");
|
||||
if(paran == 1 || paran == 2) {
|
||||
add("(");
|
||||
expr(e2);
|
||||
add(")");
|
||||
} else {
|
||||
expr(e2);
|
||||
}
|
||||
case EUnop(op, pre, e):
|
||||
if( pre ) {
|
||||
add(op);
|
||||
@@ -182,13 +331,13 @@ class Printer {
|
||||
case ECall(e, args):
|
||||
if( e == null )
|
||||
expr(e);
|
||||
else switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EField(_), EIdent(_), EConst(_):
|
||||
expr(e);
|
||||
default:
|
||||
add("(");
|
||||
expr(e);
|
||||
add(")");
|
||||
else switch( Tools.expr(e) ) {
|
||||
case EField(_), EIdent(_), EConst(_):
|
||||
expr(e);
|
||||
default:
|
||||
add("(");
|
||||
expr(e);
|
||||
add(")");
|
||||
}
|
||||
add("(");
|
||||
var first = true;
|
||||
@@ -200,31 +349,35 @@ class Printer {
|
||||
case EIf(cond,e1,e2):
|
||||
add("if( ");
|
||||
expr(cond);
|
||||
add(" ) ");
|
||||
expr(e1);
|
||||
add(" )");
|
||||
var wasBlock = block(e1, true);
|
||||
|
||||
if( e2 != null ) {
|
||||
if(!wasBlock) add("\n" + tabs);
|
||||
add(" else ");
|
||||
expr(e2);
|
||||
block(e2);
|
||||
}
|
||||
case EWhile(cond,e):
|
||||
add("while( ");
|
||||
expr(cond);
|
||||
add(" ) ");
|
||||
expr(e);
|
||||
add(" )");
|
||||
block(e, true);
|
||||
case EDoWhile(cond,e):
|
||||
add("do ");
|
||||
expr(e);
|
||||
add("do");
|
||||
block(e, true);
|
||||
add(" while ( ");
|
||||
expr(cond);
|
||||
add(" )");
|
||||
case EFor(v, it, e, ithv):
|
||||
if(ithv != null)
|
||||
add("for( "+ithv+" => "+v+" in ");
|
||||
else
|
||||
add("for( "+v+" in ");
|
||||
case EFor(v, it, e):
|
||||
add("for( "+v+" in ");
|
||||
expr(it);
|
||||
add(" ) ");
|
||||
expr(e);
|
||||
add(" )");
|
||||
block(e, true);
|
||||
case EForKeyValue(v, it, e, ithv):
|
||||
add("for( "+ithv+" => "+v+" in ");
|
||||
expr(it);
|
||||
add(" )");
|
||||
block(e, true);
|
||||
case EBreak:
|
||||
add("break");
|
||||
case EContinue:
|
||||
@@ -256,7 +409,17 @@ class Printer {
|
||||
add("[");
|
||||
expr(index);
|
||||
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("[");
|
||||
var first = true;
|
||||
for( e in el ) {
|
||||
@@ -276,25 +439,31 @@ class Printer {
|
||||
add("throw ");
|
||||
expr(e);
|
||||
case ETry(e, v, t, ecatch):
|
||||
add("try ");
|
||||
expr(e);
|
||||
add(" catch( " + v);
|
||||
add("try");
|
||||
var wasBlock = block(e);
|
||||
if( !wasBlock ) {
|
||||
add("\n" + tabs);
|
||||
} else add(" ");
|
||||
add("catch( " + v);
|
||||
addType(t);
|
||||
add(") ");
|
||||
expr(ecatch);
|
||||
add(" )");
|
||||
block(ecatch);
|
||||
case EObject(fl):
|
||||
if( fl.length == 0 ) {
|
||||
add("{}");
|
||||
} else {
|
||||
tabs += "\t";
|
||||
add("{\n");
|
||||
for( f in fl ) {
|
||||
for( i=>f in fl ) {
|
||||
add(tabs);
|
||||
add(f.name+" : ");
|
||||
var name = isJson(f.name) ? "\"" + f.name + "\"" : f.name;
|
||||
add(name+" : ");
|
||||
expr(f.e);
|
||||
add(",\n");
|
||||
if( i != fl.length - 1 ) add(",");
|
||||
add("\n");
|
||||
}
|
||||
tabs = tabs.substr(1);
|
||||
add(tabs);
|
||||
add("}");
|
||||
}
|
||||
case ETernary(c,e1,e2):
|
||||
@@ -306,23 +475,28 @@ class Printer {
|
||||
case ESwitch(e, cases, def):
|
||||
add("switch( ");
|
||||
expr(e);
|
||||
add(") {");
|
||||
add(" ) {\n");
|
||||
tabs += "\t";
|
||||
for( c in cases ) {
|
||||
add(tabs);
|
||||
add("case ");
|
||||
var first = true;
|
||||
for( v in c.values ) {
|
||||
if( first ) first = false else add(", ");
|
||||
expr(v);
|
||||
}
|
||||
add(": ");
|
||||
expr(c.expr);
|
||||
add(":");
|
||||
block(c.expr, true);
|
||||
add(";\n");
|
||||
}
|
||||
if( def != null ) {
|
||||
add(tabs);
|
||||
add("default: ");
|
||||
expr(def);
|
||||
block(def, true);
|
||||
add(";\n");
|
||||
}
|
||||
tabs = tabs.substr(1);
|
||||
add(tabs);
|
||||
add("}");
|
||||
case EMeta(name, args, e):
|
||||
add("@");
|
||||
@@ -332,7 +506,7 @@ class Printer {
|
||||
var first = true;
|
||||
for( a in args ) {
|
||||
if( first ) first = false else add(", ");
|
||||
expr(e);
|
||||
expr(a);
|
||||
}
|
||||
add(")");
|
||||
}
|
||||
@@ -347,24 +521,40 @@ class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
public static function toString( e : Expr ) {
|
||||
public inline static function toString( e : Expr ) {
|
||||
return new Printer().exprToString(e);
|
||||
}
|
||||
|
||||
public static function errorToString( e : Expr.Error ) {
|
||||
var message = switch( #if hscriptPos e.e #else e #end ) {
|
||||
public inline static function compareErrors( e1 : Error, e2 : Error ) {
|
||||
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 EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||
case EUnterminatedString: "Unterminated string";
|
||||
case EUnterminatedComment: "Unterminated comment";
|
||||
case EUnterminatedRegex: "Unterminated regular expression";
|
||||
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
|
||||
case EUnknownVariable(v): "Unknown variable: "+v;
|
||||
case EInvalidIterator(v): "Invalid iterator: "+v;
|
||||
case EInvalidType(t): "Invalid type: "+t;
|
||||
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 ECustom(msg): msg;
|
||||
case EPreset(msg): msg.toString();
|
||||
case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
|
||||
case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
|
||||
case EInvalidEscape(s): "Invalid escape sequence: " + s;
|
||||
};
|
||||
#if hscriptPos
|
||||
return e.origin + ":" + e.line + ": " + message;
|
||||
|
||||
+120
-10
@@ -21,13 +21,14 @@
|
||||
*/
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
import hscript.utils.UnsafeReflect;
|
||||
|
||||
class Tools {
|
||||
|
||||
public static function iter( e : Expr, f : Expr -> Void ) {
|
||||
switch( expr(e) ) {
|
||||
case EConst(_), EIdent(_):
|
||||
case EImport(c): f(e);
|
||||
case EImport(c, _): f(e);
|
||||
case EClass(_, e, _, _): for( a in e ) f(a);
|
||||
case EVar(_, _, e): if( e != null ) f(e);
|
||||
case EParent(e): f(e);
|
||||
@@ -40,10 +41,12 @@ class Tools {
|
||||
case EWhile(c, e): f(c); f(e);
|
||||
case EDoWhile(c, e): f(c); f(e);
|
||||
case EFor(_, it, e): f(it); f(e);
|
||||
case EForKeyValue(_, it, e, _): f(it); f(e);
|
||||
case EBreak,EContinue:
|
||||
case EFunction(_, e, _, _): f(e);
|
||||
case EReturn(e): if( e != null ) f(e);
|
||||
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 ENew(_,el): for( e in el ) f(e);
|
||||
case EThrow(e): f(e);
|
||||
@@ -66,10 +69,10 @@ class Tools {
|
||||
public static function map( e : Expr, f : Expr -> Expr ) {
|
||||
var edef = switch( expr(e) ) {
|
||||
case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
|
||||
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null);
|
||||
case EParent(e): EParent(f(e));
|
||||
case EVar(n, t, e, p, s): EVar(n, t, if( e != null ) f(e) else null, p, s);
|
||||
case EParent(e, no): EParent(f(e), no);
|
||||
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 EUnop(op, pre, e): EUnop(op, pre, f(e));
|
||||
case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]);
|
||||
@@ -77,19 +80,21 @@ class Tools {
|
||||
case EWhile(c, e): EWhile(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 EFunction(args, e, name, t): EFunction(args, f(e), name, t);
|
||||
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 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 ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
|
||||
case EThrow(e): EThrow(f(e));
|
||||
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 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 ) new SwitchCase([for( v in c.values ) f(v)], 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 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);
|
||||
}
|
||||
return mk(edef, e);
|
||||
@@ -103,12 +108,117 @@ class Tools {
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function mk( e : ExprDef, p : Expr ) {
|
||||
public static inline function cleanError( e : Error ) {
|
||||
#if hscriptPos
|
||||
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
|
||||
return e.e;
|
||||
#else
|
||||
return e;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function mk( e : ExprDef, p : Expr ):Expr {
|
||||
#if hscriptPos
|
||||
return new Expr(e, p.pmin, p.pmax, p.origin, p.line);
|
||||
#else
|
||||
return e;
|
||||
#end
|
||||
}
|
||||
|
||||
public static function isValidBinOp(op:String):Bool {
|
||||
if(op == ("??"+"=")) return true;
|
||||
return switch(op) {
|
||||
case "+" | "-" | "*" | "/" | "%" | "&" | "|" | "^" | "<<" | ">>" | ">>>" | "==" | "!=" | ">=" | "<=" | ">" | "<" | "||" | "&&" | "is" | "=" | "??" | "..." | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | ">>>=": true;
|
||||
case "=>": true;
|
||||
default: false;
|
||||
}
|
||||
}
|
||||
|
||||
static var priorities = [
|
||||
["%"],
|
||||
["*", "/"],
|
||||
["+", "-"],
|
||||
["<<", ">>", ">>>"],
|
||||
["|", "&", "^"],
|
||||
["==", "!=", ">", "<", ">=", "<="],
|
||||
["..."],
|
||||
["&&"],
|
||||
["||"],
|
||||
["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^=","=>","??"+"="],
|
||||
["->", "??"],
|
||||
["is"]
|
||||
];
|
||||
public static function checkOpPrecedence(mainOp:String, leftOp:String, rightOp:String):Int {
|
||||
var mainOpGroup = getOpGroup(mainOp);
|
||||
var leftOpGroup = getOpGroup(leftOp);
|
||||
var rightOpGroup = getOpGroup(rightOp);
|
||||
|
||||
var leftParam = false;
|
||||
var rightParam = false;
|
||||
|
||||
if(leftOpGroup > mainOpGroup && leftOpGroup != -1) leftParam = true;
|
||||
if(rightOpGroup > mainOpGroup && rightOpGroup != -1) rightParam = true;
|
||||
|
||||
if(!leftParam && !rightParam) {
|
||||
var mainOpIndex = getOpIndex(mainOp);
|
||||
var leftOpIndex = getOpIndex(leftOp);
|
||||
var rightOpIndex = getOpIndex(rightOp);
|
||||
|
||||
if(mainOpGroup == rightOpGroup) {
|
||||
if(rightOpIndex < mainOpIndex) rightParam = true;
|
||||
}
|
||||
if(leftOpGroup == mainOpGroup) {
|
||||
if(leftOpIndex < mainOpIndex) leftParam = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to index
|
||||
if(!leftParam && !rightParam) return -1;
|
||||
if(leftParam && rightParam) return 2;
|
||||
if(leftParam) return 0;
|
||||
if(rightParam) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static function getOpIndex(op:String):Int {
|
||||
if(op == "_") return -1;
|
||||
var i = 0;
|
||||
for(p in priorities) {
|
||||
for(pp in p) {
|
||||
if(op == pp)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static function getOpGroup(op:String):Int {
|
||||
if(op == "_") return -1;
|
||||
var i = 0;
|
||||
for(p in priorities) {
|
||||
for(pp in p) {
|
||||
if(op == pp)
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
public static function getEnum(cl:Enum<Dynamic>):Dynamic {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
package hscript.macros;
|
||||
|
||||
#if macro
|
||||
import Type.ValueType;
|
||||
import haxe.macro.ComplexTypeTools;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Printer;
|
||||
import haxe.macro.Compiler;
|
||||
import haxe.macro.ComplexTypeTools;
|
||||
|
||||
using StringTools;
|
||||
using haxe.macro.PositionTools;
|
||||
|
||||
class AbstractHandler {
|
||||
public static inline final CLASS_SUFFIX = "_HSA";
|
||||
|
||||
public static function init() {
|
||||
#if !display
|
||||
if(Context.defined("display")) return;
|
||||
//for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) {
|
||||
//}
|
||||
Compiler.addGlobalMetadata("", '@:build(hscript.macros.AbstractHandler.build())');
|
||||
|
||||
//var module = cl.module + cl.name;
|
||||
#end
|
||||
}
|
||||
|
||||
static var abstracts = new Map<String, {
|
||||
|
||||
}>();
|
||||
|
||||
/*static function finalizeAbstract(a:AbstractType) {
|
||||
var name = a.name;
|
||||
if(name.endsWith(CLASS_SUFFIX)) return; // !name.endsWith("_Impl_") ||
|
||||
if(name != "NodeListAccess") return;
|
||||
|
||||
Sys.println("");
|
||||
Sys.println("");
|
||||
Sys.println("");
|
||||
Sys.println("");
|
||||
Sys.println("");
|
||||
|
||||
//Sys.println("name: " + a.name);
|
||||
//Sys.println("module: " + a.module);
|
||||
|
||||
Sys.println(MacroPrinter.convertAbstractTypeToString(a));
|
||||
|
||||
//trace("buildAbstract", a);
|
||||
}*/
|
||||
|
||||
static var currentSelf = "";
|
||||
|
||||
/*static function oldTypeToString(t:Type, selfCheck:String = null):String {
|
||||
var str = switch(t) {
|
||||
case TInst(_.get() => t, params):
|
||||
var str = "";
|
||||
if(t.pack.length > 0) {
|
||||
str += t.pack.join(".") + ".";
|
||||
}
|
||||
str += t.name;
|
||||
if(t.params != null && t.params.length > 0) {
|
||||
str += "<" + t.params.map((v)->oldTypeToString(v.t, t.name + "." + v.name)).join(", ") + ">";
|
||||
}
|
||||
//if(t.params.length > 0) {
|
||||
// throw "Params not supported yet " + t.name + " " + t.params;
|
||||
//}
|
||||
str;
|
||||
case TAbstract(_.get() => t, params):
|
||||
var str = "";
|
||||
if(t.pack.length > 0) {
|
||||
str += t.pack.join(".") + ".";
|
||||
}
|
||||
str += t.name;
|
||||
if(t.params != null && t.params.length > 0) {
|
||||
str += "<" + t.params.map((v)->oldTypeToString(v.t, t.name + "." + v.name)).join(", ") + ">";
|
||||
}
|
||||
//if(t.params.length > 0) {
|
||||
// throw "Params not supported yet " + t.name + " " + t.params;
|
||||
//}
|
||||
str;
|
||||
case TEnum(_.get() => t, params):
|
||||
var str = "";
|
||||
if(t.pack.length > 0) {
|
||||
str += t.pack.join(".") + ".";
|
||||
}
|
||||
str += t.name;
|
||||
if(t.params != null && t.params.length > 0) {
|
||||
str += "<" + t.params.map((v)->oldTypeToString(v.t, t.name + "." + v.name)).join(", ") + ">";
|
||||
}
|
||||
//if(t.params.length > 0) {
|
||||
// throw "Params not supported yet " + t.name + " " + t.params;
|
||||
//}
|
||||
str;
|
||||
case TType(_.get() => t, params):
|
||||
var str = "";
|
||||
if(t.pack.length > 0) {
|
||||
str += t.pack.join(".") + ".";
|
||||
}
|
||||
str += t.name;
|
||||
if(t.params != null && t.params.length > 0) {
|
||||
str += "<" + t.params.map((v)->oldTypeToString(v.t, t.name + "." + v.name)).join(", ") + ">";
|
||||
//trace("TType", str);
|
||||
}
|
||||
str;
|
||||
case TAnonymous(_.get() => t):
|
||||
//var str = "Dynamic {"+t.fields.map((f)->f.name).join(", ")+"}";
|
||||
//str += t.name;
|
||||
var str = "{ ";
|
||||
var first = true;
|
||||
for(f in t.fields) {
|
||||
if(!first) str += ", ";
|
||||
first = false;
|
||||
str += f.name + ":" + oldTypeToString(f.type);
|
||||
}
|
||||
str += " }";
|
||||
//trace("TAnonymous", str);
|
||||
str;
|
||||
case TDynamic(t):
|
||||
var str = "Dynamic";
|
||||
if(t != null) {
|
||||
str += "<" + oldTypeToString(t) + ">";
|
||||
}
|
||||
str;
|
||||
default:
|
||||
Sys.println("Unknown type " + Std.string(t));
|
||||
null;
|
||||
}
|
||||
|
||||
if(selfCheck != null) {
|
||||
if(str == selfCheck) {
|
||||
return selfCheck;
|
||||
//return currentSelf;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static function getResolvedType(t:ComplexType):String {
|
||||
if(t == null) return null;
|
||||
|
||||
try {
|
||||
//var type = Context.getType(checkType);
|
||||
var paramFree = switch(t) {
|
||||
case TPath(t):
|
||||
//trace(t);
|
||||
TPath({
|
||||
name: t.name,
|
||||
pack: t.pack,
|
||||
params: t.params,
|
||||
sub: t.sub
|
||||
});
|
||||
case TAnonymous(a):
|
||||
t;
|
||||
case TFunction(args, ret):
|
||||
t;
|
||||
default:
|
||||
trace("");
|
||||
trace(ComplexTypeTools.toString(t));
|
||||
throw "Unknown type " + Std.string(t);
|
||||
}
|
||||
var type = ComplexTypeTools.toType(paramFree);
|
||||
var strType = oldTypeToString(type);
|
||||
if(strType != null) return strType;
|
||||
} catch(e:Dynamic) {
|
||||
Sys.println(e);
|
||||
Sys.println(ComplexTypeTools.toString(t));
|
||||
Sys.println(haxe.CallStack.toString(haxe.CallStack.exceptionStack()));
|
||||
}
|
||||
|
||||
return MacroPrinter.typeToString(t);
|
||||
}*/
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var clRef = Context.getLocalClass();
|
||||
if (clRef == null) return fields;
|
||||
var cl = clRef.get();
|
||||
|
||||
if(cl.isExtern) return fields;
|
||||
|
||||
//if(cl.name != "Access_Impl_") return fields;
|
||||
//if(cl.name != "DrawQuadsView_Impl_") return fields;
|
||||
|
||||
if(cl.name.endsWith("_Impl_") && !cl.name.endsWith(CLASS_SUFFIX)) // && ["Access_Impl_", "AttribAccess_Impl_"].contains(cl.name))
|
||||
{
|
||||
//trace("Processing " + cl);
|
||||
//if(!initialized) {
|
||||
// initialized = true;
|
||||
// Context.onAfterTyping(function(mods) {
|
||||
// for(mod in mods) {
|
||||
// switch(mod) {
|
||||
// case TAbstract(_.get() => a):
|
||||
// //trace("onAfterTyping", a);
|
||||
// finalizeAbstract(a);
|
||||
// //ab = a;
|
||||
// default:
|
||||
// // return;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//}
|
||||
|
||||
currentSelf = cl.name.substr(0, cl.name.length - "_Impl_".length);
|
||||
|
||||
var funcInfos = [];
|
||||
var propsInfo = [];
|
||||
//for(f in fields) {
|
||||
// trace(f.name, f.kind);
|
||||
//}
|
||||
for(f in fields) {
|
||||
if(f.name.startsWith("__abstract_helper"))
|
||||
continue;
|
||||
//if(f.name != "escapes")
|
||||
// continue;
|
||||
//trace(f);
|
||||
//trace(
|
||||
//MacroPrinter.convertFieldToString(f, cl.module + "." + cl.name)
|
||||
//);
|
||||
//trace();
|
||||
//continue;
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if(fun.expr != null) {
|
||||
var obj:Dynamic = {
|
||||
name: f.name,
|
||||
args: [for(a in fun.args) {
|
||||
//name: a.name,
|
||||
//opt: a.opt,
|
||||
//type: MacroPrinter.typeToString(a.type),
|
||||
//value: a.value,
|
||||
//meta: a.meta,
|
||||
var arg = a.type == null ? "_" : MacroPrinter.typeToString(a.type);
|
||||
if(a.opt) {
|
||||
arg = "?" + arg;
|
||||
}
|
||||
arg;
|
||||
}].join("|"),
|
||||
ret: fun.ret,
|
||||
//access: f.access.map(function(a) return Std.string(a)).join(","),
|
||||
access: {
|
||||
var special = 0;
|
||||
var map:Array<Access> = [AStatic, APrivate, APublic, AOverride, AInline, ADynamic, AExtern, AStatic];
|
||||
for(a in f.access) {
|
||||
var index = map.indexOf(a);
|
||||
if(index == -1) {
|
||||
trace("Unknown access " + a);
|
||||
continue;
|
||||
}
|
||||
special |= 1 << index;
|
||||
}
|
||||
special;
|
||||
},
|
||||
special: {
|
||||
var special = 0;//{arrayAccess: false, arrayWrite: false};
|
||||
for(m in f.meta) {
|
||||
if(m.name == ":arrayAccess") {
|
||||
if(fun.args.length == 1) {
|
||||
special |= 1;
|
||||
//special.arrayAccess = true;
|
||||
} else if(fun.args.length == 2) {
|
||||
special |= 2;
|
||||
//special.arrayWrite = true;
|
||||
} else {
|
||||
trace(f.pos);
|
||||
throw "Unknown :arrayAccess meta " + fun.args;
|
||||
}
|
||||
} else if(m.name == ":from") {
|
||||
//throw "Unknown :from meta " + m;
|
||||
special |= 4;
|
||||
} else if(m.name == ":to") {
|
||||
//throw "Unknown :to meta " + m;
|
||||
special |= 8;
|
||||
} else if(m.name == ":op") {
|
||||
special |= 16;
|
||||
}
|
||||
}
|
||||
special;
|
||||
},
|
||||
op: {
|
||||
var op = null;
|
||||
//trace("");
|
||||
//trace("");
|
||||
//trace("");
|
||||
//trace(f.name, f.meta);
|
||||
//trace(f.name, fun);
|
||||
for(m in f.meta) {
|
||||
if(m.name == ":resolve") {
|
||||
if(f.access.contains(AStatic) && fun.args.length == 2) {
|
||||
op = "a.b";
|
||||
} else if(!f.access.contains(AStatic) && fun.args.length == 1) {
|
||||
op = "a.b";
|
||||
} else {
|
||||
throw "Unknown :resolve meta " + fun.args;
|
||||
}
|
||||
}
|
||||
if(m.name == ":op") {
|
||||
switch (m.params[0].expr) {
|
||||
case EField(_.expr => EConst(CIdent(_)), _):
|
||||
op = "a.b";
|
||||
case EArrayDecl([]):
|
||||
op = "[]";
|
||||
case EUnop(o, suffix, _.expr => EConst(CIdent(_))):
|
||||
var opStr = switch(o) {
|
||||
case OpNeg: "-";
|
||||
case OpIncrement: "++";
|
||||
case OpDecrement: "--";
|
||||
case OpNot: "!";
|
||||
case OpNegBits: "~";
|
||||
case OpSpread: "...";
|
||||
}
|
||||
if(suffix) {
|
||||
op = "a" + opStr;
|
||||
} else {
|
||||
op = opStr + "a";
|
||||
}
|
||||
case EBinop(o, _.expr => EConst(CIdent(_)), _.expr => EConst(CIdent(_))):
|
||||
var opStr = getBinopStr(o);
|
||||
op = "a " + opStr + " b";
|
||||
#if (haxe >= "4.3.0")
|
||||
case ECall(_.expr => EConst(CIdent(_)), []):
|
||||
op = "a()";
|
||||
#end
|
||||
default:
|
||||
trace(f.name, cl.module);
|
||||
throw "Unknown op " + MacroPrinter.convertExprToString(m.params[0]) + "\n in " + f.name + " in " + cl.module;
|
||||
}
|
||||
//trace(op, convertExprToString(m.params[0]));
|
||||
//trace(Type.typeof(m.params[0]));
|
||||
//switch (m.params[0].expr) {
|
||||
// case EConst(CIdent(_i)):
|
||||
// id = _i;
|
||||
// default:
|
||||
//}
|
||||
}
|
||||
}
|
||||
op;
|
||||
}
|
||||
}
|
||||
|
||||
if(obj.op == null) {
|
||||
Reflect.deleteField(obj, "op");
|
||||
}
|
||||
|
||||
if(obj.ret == null && f.name.startsWith("get_")) {
|
||||
var v = getVarFromFields(fields, f.name.substr(4));
|
||||
var type = getTypeFromField(v);
|
||||
//Sys.println("getter " + f.name + " " + getResolvedType(type) + " " + type);
|
||||
obj.ret = type;
|
||||
} else if(obj.ret == null && f.name.startsWith("set_")) {
|
||||
var v = getVarFromFields(fields, f.name.substr(4));
|
||||
var type = getTypeFromField(v);
|
||||
//Sys.println("setter " + f.name + " " + getResolvedType(type) + " " + type);
|
||||
obj.ret = type;
|
||||
} else {
|
||||
if(obj.ret != null) {
|
||||
//Sys.println("normal field " + f.name + " " + getResolvedType(obj.ret) + " " + obj.ret);
|
||||
}
|
||||
}
|
||||
|
||||
//if(obj.name == "_new")
|
||||
// obj.name = "new";
|
||||
|
||||
obj.ret = MacroPrinter.typeToString(obj.ret);
|
||||
|
||||
//trace("FFun", obj);
|
||||
var save:Dynamic = null;
|
||||
if(true) {
|
||||
save = [obj.name, obj.args, obj.ret, obj.access, obj.special];
|
||||
if(obj.op != null) {
|
||||
save.push(obj.op);
|
||||
}
|
||||
} else {
|
||||
save = obj;
|
||||
}
|
||||
funcInfos.push(save);
|
||||
//trace(cl.name, obj);
|
||||
}
|
||||
case FProp(get, set, t, e):
|
||||
if(f.access.contains(AStatic)) {
|
||||
continue;
|
||||
}
|
||||
if(get != "get") {
|
||||
trace("Unknown FProp getter " + get, f.pos);
|
||||
continue;
|
||||
}
|
||||
if(e != null) {
|
||||
trace("Unknown FProp expression " + e, f.pos);
|
||||
continue;
|
||||
}
|
||||
//var getsetbits = 0;
|
||||
var canSet = false;
|
||||
// allow transform of getter setter
|
||||
//switch(get) {
|
||||
// case "get": getsetbits |= 1;
|
||||
// case "dynamic": getsetbits |= 1;
|
||||
// //case "null": getsetbits |= 1; // since this cant be called outside of the class
|
||||
//}
|
||||
switch(set) {
|
||||
case "set": canSet = true;
|
||||
case "dynamic": canSet = true;
|
||||
case "null": canSet = false; // since this cant be called outside of the class
|
||||
}
|
||||
|
||||
var obj:Dynamic = {
|
||||
name: f.name,
|
||||
get: "get_" + f.name,
|
||||
set: canSet ? "set_" + f.name : null
|
||||
}
|
||||
|
||||
//var getset = get + "," + set;
|
||||
var save:Dynamic = null;
|
||||
if(true) {
|
||||
save = [obj.name, obj.get, obj.set];
|
||||
} else {
|
||||
save = obj;
|
||||
}
|
||||
propsInfo.push(save);
|
||||
//trace("FProp", getset, t, e);
|
||||
default:
|
||||
}
|
||||
//funcInfos.push([f.name, ]);
|
||||
}
|
||||
|
||||
//trace(funcInfos);
|
||||
|
||||
//trace(cl.pos);
|
||||
|
||||
var shadowClass = macro class {
|
||||
public static var __hx_impl__:String = $v{cl.pack.join(".") + "." + cl.name};
|
||||
|
||||
public static function abstract_funcs():Array<Array<Dynamic>> {
|
||||
return @:fixed $v{funcInfos};
|
||||
}
|
||||
public static function abstract_props():Array<Array<String>> {
|
||||
return @:fixed $v{propsInfo};
|
||||
}
|
||||
}
|
||||
|
||||
shadowClass.kind = TDClass(null, [
|
||||
//{name: "IHScriptAbstractHelper", pack: ["hscript"]},
|
||||
//{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
||||
], false, true, false);
|
||||
shadowClass.name = '${cl.name.substr(0, cl.name.length - "_Impl_".length)}$CLASS_SUFFIX';
|
||||
//trace(shadowClass.name);
|
||||
var imports = Context.getLocalImports().copy();
|
||||
Utils.setupMetas(shadowClass, [], false);
|
||||
|
||||
var moduleName = cl.module;
|
||||
Context.defineModule(moduleName, [shadowClass], imports);
|
||||
|
||||
//var printer = new haxe.macro.Printer();
|
||||
//var code = printer.printTypeDefinition(shadowClass);
|
||||
//trace(code);
|
||||
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
static var initialized = false;
|
||||
|
||||
static function getBinopStr(op:Binop):String {
|
||||
return switch(op) {
|
||||
case OpAdd: "+";
|
||||
case OpSub: "-";
|
||||
case OpMult: "*";
|
||||
case OpDiv: "/";
|
||||
case OpMod: "%";
|
||||
case OpEq: "==";
|
||||
case OpNotEq: "!=";
|
||||
case OpGt: ">";
|
||||
case OpGte: ">=";
|
||||
case OpLt: "<";
|
||||
case OpLte: "<=";
|
||||
case OpAnd: "&";
|
||||
case OpOr: "|";
|
||||
case OpXor: "^";
|
||||
case OpShl: "<<";
|
||||
case OpShr: ">>";
|
||||
case OpUShr: ">>>";
|
||||
case OpBoolAnd: "&&";
|
||||
case OpBoolOr: "||";
|
||||
case OpAssign: "=";
|
||||
case OpArrow: "=>";
|
||||
case OpAssignOp(op):
|
||||
getBinopStr(op) + "=";
|
||||
case OpIn: "in";
|
||||
case OpInterval: "...";
|
||||
}
|
||||
}
|
||||
|
||||
static function getVarFromFields(fields:Array<Field>, name:String):Field {
|
||||
for(f in fields) {
|
||||
if(f.name == name) {
|
||||
switch(f.kind) {
|
||||
case FProp(_, _, _, _):
|
||||
return f;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static function getTypeFromField(field:Field):ComplexType {
|
||||
//if(field == null) return null;
|
||||
return switch(field.kind) {
|
||||
case FProp(_, _, t, _): t;
|
||||
case FVar(t, _): t;
|
||||
case FFun(f): f.ret;
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -184,9 +184,11 @@ class ClassExtendMacro {
|
||||
var name:String = $v{name};
|
||||
|
||||
if (__interp != null) {
|
||||
var v:Dynamic = null;
|
||||
if (__interp.variables.exists(name) && Reflect.isFunction(v = __interp.variables.get(name))) {
|
||||
return v($a{arguments});
|
||||
if (__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
if(Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||
return v($a{arguments});
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.$name($a{arguments});
|
||||
@@ -196,10 +198,12 @@ class ClassExtendMacro {
|
||||
var name:String = $v{name};
|
||||
|
||||
if (__interp != null) {
|
||||
var v:Dynamic = null;
|
||||
if (__interp != null && __interp.variables.exists(name) && Reflect.isFunction(v = __interp.variables.get(name))) {
|
||||
v($a{arguments});
|
||||
return;
|
||||
if (__custom__variables.exists(name)) {
|
||||
var v:Dynamic = null;
|
||||
if(Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||
v($a{arguments});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.$name($a{arguments});
|
||||
@@ -257,21 +261,100 @@ class ClassExtendMacro {
|
||||
pack: cl.pack.copy(),
|
||||
name: cl.name
|
||||
}, [
|
||||
{name: "IHScriptCustomBehaviour", pack: ["hscript"]}
|
||||
{name: "IHScriptCustomBehaviour", pack: ["hscript"]},
|
||||
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
||||
], false, true, false);
|
||||
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
|
||||
var imports = Context.getLocalImports().copy();
|
||||
Utils.setupMetas(shadowClass, imports);
|
||||
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
||||
|
||||
// Adding hscript getters and setters
|
||||
|
||||
// TODO: Remove
|
||||
shadowClass.fields.push({
|
||||
name: "__interp",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(TPath({
|
||||
pack: ['hscript'],
|
||||
name: 'Interp'
|
||||
}))
|
||||
})),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__custom__variables",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(TPath({
|
||||
pack: [],
|
||||
name: 'Map',
|
||||
params: [TPType(TPath({name: "String", pack: []})), TPType(TPath({name: "Dynamic", pack: []}))]
|
||||
})),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__allowSetGet",
|
||||
pos: Context.currentPos(),
|
||||
kind: FVar(TPath({
|
||||
pack: [],
|
||||
name: 'Bool',
|
||||
}), macro true),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__callGetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: TPath({name: 'Dynamic', pack: []}),
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("get_" + name)();
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "String", pack: []})
|
||||
}
|
||||
]
|
||||
}),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
shadowClass.fields.push({
|
||||
name: "__callSetter",
|
||||
pos: Context.currentPos(),
|
||||
kind: FFun({
|
||||
ret: TPath({name: 'Dynamic', pack: []}),
|
||||
params: [],
|
||||
expr: macro {
|
||||
__allowSetGet = false;
|
||||
var v = __custom__variables.get("set_" + name)(val);
|
||||
__allowSetGet = true;
|
||||
return v;
|
||||
},
|
||||
args: [
|
||||
{
|
||||
name: "name",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "String", pack: []})
|
||||
},
|
||||
{
|
||||
name: "val",
|
||||
opt: false,
|
||||
meta: [],
|
||||
type: TPath({name: "Dynamic", pack: []})
|
||||
}
|
||||
]
|
||||
}),
|
||||
access: [APublic]
|
||||
});
|
||||
|
||||
// Todo: make it possible to override
|
||||
@@ -316,44 +399,42 @@ class ClassExtendMacro {
|
||||
|
||||
var hgetField = if(hasHgetInSuper) {
|
||||
macro {
|
||||
if(this.__interp.variables.exists("get_" + name))
|
||||
return this.__interp.variables.get("get_" + name)();
|
||||
if (this.__interp.variables.exists(name))
|
||||
return this.__interp.variables.get(name);
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
return super.hget(name);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
if(this.__interp.variables.exists("get_" + name))
|
||||
return this.__interp.variables.get("get_" + name)();
|
||||
if (this.__interp.variables.exists(name))
|
||||
return this.__interp.variables.get(name);
|
||||
return Reflect.getProperty(this, name);
|
||||
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||
return __callGetter(name);
|
||||
if (__custom__variables.exists(name))
|
||||
return __custom__variables.get(name);
|
||||
return UnsafeReflect.getProperty(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
var hsetField = if(hasHsetInSuper) {
|
||||
macro {
|
||||
if(this.__interp.variables.exists("set_" + name)) {
|
||||
return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function
|
||||
}
|
||||
if (this.__interp.variables.exists(name)) {
|
||||
this.__interp.variables.set(name, val);
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
return super.hset(this, name);
|
||||
}
|
||||
} else {
|
||||
macro {
|
||||
if(this.__interp.variables.exists("set_" + name)) {
|
||||
return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function
|
||||
}
|
||||
if (this.__interp.variables.exists(name)) {
|
||||
this.__interp.variables.set(name, val);
|
||||
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||
return __callSetter(name, val);
|
||||
if (__custom__variables.exists(name)) {
|
||||
__custom__variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
Reflect.setProperty(this, name, val);
|
||||
return Reflect.field(this, name);
|
||||
UnsafeReflect.setProperty(this, name, val);
|
||||
return UnsafeReflect.field(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,10 +25,11 @@ class Utils {
|
||||
return type;
|
||||
}
|
||||
|
||||
public static function setupMetas(shadowClass:TypeDefinition, imports) {
|
||||
public static function setupMetas(shadowClass:TypeDefinition, imports:Array<ImportExpr>, doImports:Bool = true) {
|
||||
shadowClass.meta = [];
|
||||
shadowClass.meta.push({name: ":dox", params: [macro hide], pos: Context.currentPos()});
|
||||
shadowClass.meta.push({name: ":noCompletion", params: [], pos: Context.currentPos()});
|
||||
if(!doImports) return;
|
||||
var module = Context.getModule(Context.getLocalModule());
|
||||
for(t in module) {
|
||||
switch(t) {
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package hscript.utils;
|
||||
|
||||
#if cpp
|
||||
import cpp.ObjectType;
|
||||
#end
|
||||
|
||||
@:analyzer(ignore)
|
||||
class UnsafeReflect {
|
||||
public #if !cpp inline #end static function hasField(o:Dynamic, field:String):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__HasField(field);
|
||||
}
|
||||
#else
|
||||
return Reflect.hasField(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function field(o:Dynamic, field:String):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__Field(field, untyped __cpp__("::hx::paccNever"));
|
||||
}
|
||||
#else
|
||||
return Reflect.field(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function setField(o:Dynamic, field:String, value:Dynamic):Void {
|
||||
#if cpp
|
||||
untyped {
|
||||
o.__SetField(field, value, untyped __cpp__("::hx::paccNever"));
|
||||
}
|
||||
#else
|
||||
return Reflect.setField(o, field, value);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function getProperty(o:Dynamic, field:String):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
return o.__Field(field, untyped __cpp__("::hx::paccAlways"));
|
||||
}
|
||||
#else
|
||||
return Reflect.getProperty(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function setProperty(o:Dynamic, field:String, value:Dynamic):Void {
|
||||
#if cpp
|
||||
untyped {
|
||||
o.__SetField(field, value, untyped __cpp__("::hx::paccAlways"));
|
||||
}
|
||||
#else
|
||||
Reflect.setProperty(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callFieldUnsafe(o:Dynamic, field:String, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
var func:Dynamic = o.__Field(field, untyped __cpp__("::hx::paccDynamic"));
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
#end
|
||||
}
|
||||
|
||||
public inline static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
return Reflect.callMethod(o, func, args);
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callMethodSafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
if (func == null)
|
||||
throw cpp.ErrorConstants.nullFunctionPointer;
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function callMethodUnsafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||
#if cpp
|
||||
untyped {
|
||||
untyped func.__SetThis(o);
|
||||
return untyped func.__Run(args);
|
||||
}
|
||||
#else
|
||||
return Reflect.callMethod(o, func, args);
|
||||
#end
|
||||
}
|
||||
|
||||
public inline static function fields(o:Dynamic):Array<String>
|
||||
return Reflect.fields(o);
|
||||
/*untyped {
|
||||
if (o == null)
|
||||
return new Array();
|
||||
var a:Array<String> = [];
|
||||
o.__GetFields(a);
|
||||
return a;
|
||||
}*/
|
||||
|
||||
public #if !cpp inline #end static function isFunction(f:Dynamic):Bool
|
||||
#if cpp
|
||||
untyped {
|
||||
return f.__GetType() == ObjectType.vtFunction;
|
||||
}
|
||||
#else
|
||||
return Reflect.isFunction(f);
|
||||
#end
|
||||
|
||||
public inline static function compare<T>(a:T, b:T):Int {
|
||||
return Reflect.compare(a, b);
|
||||
//return (a == b) ? 0 : (((a : Dynamic) > (b : Dynamic)) ? 1 : -1);
|
||||
}
|
||||
|
||||
public inline static function compareMethods(f1:Dynamic, f2:Dynamic):Bool {
|
||||
return Reflect.compareMethods(f1, f2);
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function isObject(v:Dynamic):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
var t:Int = v.__GetType();
|
||||
return t == ObjectType.vtObject || t == ObjectType.vtClass || t == ObjectType.vtString || t == ObjectType.vtArray;
|
||||
}
|
||||
#else
|
||||
return Reflect.isObject(v);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function isEnumValue(v:Dynamic):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return v.__GetType() == ObjectType.vtEnum;
|
||||
}
|
||||
#else
|
||||
return Reflect.isEnumValue(v);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function deleteField(o:Dynamic, field:String):Bool {
|
||||
#if cpp
|
||||
untyped {
|
||||
return untyped __global__.__hxcpp_anon_remove(o, field);
|
||||
}
|
||||
#else
|
||||
return Reflect.deleteField(o, field);
|
||||
#end
|
||||
}
|
||||
|
||||
public #if !cpp inline #end static function copy<T>(o:Null<T>):Null<T> {
|
||||
#if cpp
|
||||
if (o == null)
|
||||
return null;
|
||||
var t:Int = untyped o.__GetType();
|
||||
if (t == ObjectType.vtString)
|
||||
return o;
|
||||
if (t == ObjectType.vtArray)
|
||||
return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))();
|
||||
var o2:Dynamic = {};
|
||||
for (f in UnsafeReflect.fields(o))
|
||||
UnsafeReflect.setField(o2, f, UnsafeReflect.field(o, f));
|
||||
return o2;
|
||||
#else
|
||||
return Reflect.copy(o);
|
||||
#end
|
||||
}
|
||||
|
||||
@:overload(function(f:Array<Dynamic>->Void):Dynamic {})
|
||||
public static function makeVarArgs(f:Array<Dynamic>->Dynamic):Dynamic {
|
||||
#if cpp
|
||||
return untyped __global__.__hxcpp_create_var_args(f);
|
||||
#else
|
||||
return inline Reflect.makeVarArgs(f);
|
||||
#end
|
||||
}
|
||||
}
|
||||
@@ -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,30 @@
|
||||
# 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
|
||||
|
||||
--macro keep('IntIterator')
|
||||
# --macro hscript.macros.UsingHandler.init()
|
||||
# --macro hscript.macros.ClassExtendMacro.init()
|
||||
--macro hscript.macros.AbstractHandler.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,35 @@
|
||||
# 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()
|
||||
--macro hscript.macros.AbstractHandler.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,57 @@
|
||||
#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);
|
||||
new TestBenchmark();
|
||||
#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());
|
||||
runTest("Abstract", new AbstractCase());
|
||||
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,329 @@
|
||||
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);
|
||||
}
|
||||
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,71 @@
|
||||
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();
|
||||
|
||||
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,70 @@
|
||||
package tests;
|
||||
|
||||
import haxe.xml.Access;
|
||||
import Xml;
|
||||
|
||||
class AbstractCase extends TestCase {
|
||||
override function setup() {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
override function run() {
|
||||
var access = new Access(Xml.parse("<test test=\"cool\"><hello>world</hello></test>").firstElement());
|
||||
|
||||
trace(access.att.test);
|
||||
|
||||
getNewInterp().execute(Util.parse("
|
||||
|
||||
import haxe.xml.Access;
|
||||
|
||||
var access = new Access(Xml.parse('<test test=\"cool\"><hello>world</hello></test>').firstElement());
|
||||
var a2 = new haxe.xml.Access(Xml.parse('<test test=\"cool\"><hello>world</hello></test>').firstElement());
|
||||
|
||||
trace(access.att.test);
|
||||
"));
|
||||
|
||||
//var a2:Access = Xml.parse('<test test=\"cool\"><hello>world</hello></test>').firstElement();
|
||||
/*getNewInterp().execute(Util.parse("
|
||||
// Converts to
|
||||
import haxe.xml._Access.Access_Impl_;
|
||||
import haxe.xml._Access.AttribAccess_Impl_;
|
||||
|
||||
var access = Xml.parse('<test test=\"cool\"><hello>world</hello></test>').firstElement();
|
||||
|
||||
trace(AttribAccess_Impl_.resolve(Access_Impl_.get_att(access), 'test'));
|
||||
|
||||
|
||||
// trace(Access_Impl_.get_att(access));
|
||||
//abstract import haxe.xml.Access;
|
||||
//import haxe.xml.Access with Abstract;
|
||||
|
||||
|
||||
import haxe.xml.Access;
|
||||
|
||||
var access = new Access(Xml.parse('<test test=\"cool\"><hello>world</hello></test>').firstElement());
|
||||
|
||||
trace(access.att.test);
|
||||
"));*/
|
||||
|
||||
/*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)]);*/
|
||||
}
|
||||
|
||||
override function teardown() {
|
||||
super.teardown();
|
||||
}
|
||||
}
|
||||
|
||||
abstract TestAbstract(Int)
|
||||
{
|
||||
//public static inline var TRANSPARENT:TestAbstract = 0x00000000;
|
||||
//public static inline var WHITE:TestAbstract = 0xFFFFFFFF;
|
||||
//public static inline var GRAY:TestAbstract = 0xFF808080;
|
||||
//public static inline var BLACK:TestAbstract = 0xFF000000;
|
||||
}
|
||||
|
||||
enum abstract TestEnumAbstract(Int) {
|
||||
|
||||
}
|
||||
@@ -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,158 @@
|
||||
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));
|
||||
|
||||
trace(Type.resolveClass("hscript._Error.ErrorMessage_Impl_"));
|
||||
|
||||
// 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,30 @@
|
||||
# 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
|
||||
|
||||
--macro keep('IntIterator')
|
||||
# --macro hscript.macros.UsingHandler.init()
|
||||
# --macro hscript.macros.ClassExtendMacro.init()
|
||||
--macro hscript.macros.AbstractHandler.init()
|
||||
|
||||
# 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