Compare commits

..
74 Commits
Author SHA1 Message Date
Ne_Eo 0ad52138fe Readded int vars 2024-08-23 00:50:58 +02:00
Ne_Eo a616f2affe Revert "s" 2024-08-23 00:36:16 +02:00
Ne_Eo 6721a838ca Merge branch 'codename-dev' into testing 2024-08-23 00:34:26 +02:00
lunarcleint 826f0184bf s 2024-08-22 15:04:06 -07:00
lunarcleint 6c20ad3a8f temp remove int vars 2024-08-22 14:53:05 -07:00
lunarcleint fd5a2d9143 s 2024-08-22 14:44:24 -07:00
lunarcleintandNe_Eo 8da9ed942d benchmark stuff
Co-authored-by: Ne_Eo <NeeEoo@users.noreply.github.com>
2024-08-22 14:43:10 -07:00
lunarcleint f31f380602 change int vars flag 2024-08-22 12:32:38 -07:00
lunarcleint 85ea6c6f44 INT_VARS flag, kinda broken lmao 2024-08-22 12:30:09 -07:00
Ne_Eo 13bdf30055 Classes work more reliably + Optimizations 2024-08-20 04:50:20 +02:00
Ne_Eo 6daea2fb5b Merge branch 'master' into testing 2024-08-13 13:34:56 +02:00
Ne_Eo f18c7a2210 Small optimization 2024-08-02 23:50:16 +02:00
Ne_Eo 123a7de64a Fix Trigraph bug 2024-07-26 04:24:37 +02:00
Ne_Eo bc8a59877e More optimizations 2024-07-11 16:54:37 +02:00
Ne_Eo 4347fe9305 Slight optimization 2024-07-11 16:33:47 +02:00
Ne_Eo e0e1436930 Fix bug oops :3 2024-06-03 19:00:40 +02:00
NeeEoo a41222328a Small optimization 2024-04-29 05:31:21 +02:00
NeeEoo 080c441f24 Cleanup? 2024-04-29 03:52:25 +02:00
lunarcleintandNe_Eo d6d6e7a45e we spent like 5 hours
Co-authored-by: Ne_Eo <NeeEoo@users.noreply.github.com>
2024-04-28 18:11:43 -07:00
lunarcleint cb645d6bb1 get out of here public and static arrays >:(( 2024-04-28 13:10:11 -07:00
lunarcleint 5d58ea56c9 RELIEF 2024-04-28 03:10:06 -07:00
lunarcleint 4170a4aafc i cant wait to break all ur hscripts 2024-04-28 02:15:10 -07:00
NeeEoo 19021058c4 Some fixes and cleanup 2024-04-26 03:47:10 +02:00
NeeEoo 15bd6b038a Fix bug 2024-04-26 03:40:38 +02:00
lunarcleint 4d274954e3 skibidi 2024-04-25 18:34:31 -07:00
lunarcleint aff2d0766d clean up EImport 2024-04-25 18:20:17 -07:00
NeeEoo 38634e1528 Field Imports + Automatic Imports 2024-04-26 01:48:28 +02:00
NeeEoo 5ef937d6d4 Fixed bugs 2024-04-25 22:50:17 +02:00
NeeEoo 22bbe04f77 Oops 2024-04-25 21:51:27 +02:00
NeeEoo 18382c4621 Fixed no hscriptPos errors 2024-04-25 21:49:39 +02:00
NeeEoo ad93c1bc18 Added REGEX!!!!!
Started working on automatic imports
2024-04-25 21:29:49 +02:00
NeeEoo 238c24501b Optimization on string constants 2024-04-25 19:03:49 +02:00
NeeEoo d38ddbb2b4 Some cleanup 2024-04-25 17:38:25 +02:00
NeeEoo fb8f35e2a5 Added String.fromCharCode, "a".code
Added a preprocessor
2024-04-25 17:23:35 +02:00
NeeEoo 6c77566fea Actually 2024-04-25 12:36:11 +02:00
NeeEoo b7c12943fa Updated deepEqual to make it be more accurate 2024-04-25 12:33:45 +02:00
NeeEoo 50e87c504d Fixed bug with string interpolation 2024-04-25 12:15:23 +02:00
NeeEoo aaac98d7b0 Small helper function 2024-04-25 04:43:59 +02:00
NeeEoo b6df96a4b0 EMapDecl + Map Comprehension 2024-04-25 04:14:41 +02:00
NeeEoo c0d4540d4b Fixed bug with val ? true : false optimization 2024-04-25 04:06:53 +02:00
NeeEoo ad05952685 Small optimization 2024-04-25 01:05:33 +02:00
NeeEoo 340d63003f Some cleanup 2024-04-25 00:37:59 +02:00
NeeEoo 177bb9f75b Added String Interpolation 2024-04-25 00:12:20 +02:00
NeeEoo e876db00e4 Added hex, octal, better unicode escape parsing
Fixed printer so it now prints printable strings
2024-04-24 19:33:11 +02:00
NeeEoo cd3bc5b194 Fixed bug with printing 2024-04-24 04:07:34 +02:00
NeeEoo b13d449b78 Fixed Enums 2024-04-24 03:57:07 +02:00
NeeEoo 006b0e1f32 Fixed bug 2024-04-24 00:49:36 +02:00
NeeEoo 0ca6eab16e Optimize val + 0 -> val, val ? true : false -> val
Bug fixes with printing
Cleaner print output
2024-04-24 00:15:46 +02:00
NeeEoo df1c5ad97c Fixed Printer + Added more optimizations 2024-04-23 22:40:07 +02:00
NeeEoo 8c1aaf5454 Added a optimizer + bug fixes with benchmark 2024-04-23 19:41:46 +02:00
NeeEoo 9288a9df10 Bug fix on custom classes + Better performance with custom classes
Also made the custom class generated code use less code
2024-04-23 17:47:30 +02:00
NeeEoo a727148447 More Optimizations 2024-04-23 16:52:26 +02:00
NeeEoo a16e74dee6 Added UnsafeReflect 2024-04-23 16:17:17 +02:00
NeeEoo fdde089908 Optimized more code 2024-04-23 15:18:42 +02:00
NeeEoo 6346f82edf Turned stuffs to classes to reduce reflection 2024-04-23 15:02:15 +02:00
NeeEoo 10dd51ebe8 Small cleanup + bug fix 2024-04-23 14:17:17 +02:00
lunarcleint e97c79d14c s 2024-04-23 00:42:20 -07:00
lunarcleint 418843c606 fix build on windows 2024-04-23 00:41:45 -07:00
NeeEoo 049daf4b0d Added more tests + benchmark works 2024-04-23 06:28:28 +02:00
NeeEoo b105ac0453 Added a lot more tests 2024-04-23 05:24:52 +02:00
NeeEoo d2aa99eff7 Made it show how many skipped known bugs 2024-04-23 04:09:25 +02:00
NeeEoo 934848ff8d Added some more tests 2024-04-23 04:04:25 +02:00
NeeEoo f512ac4cde WIP TESTING + BENCHMARK CODE 2024-04-23 03:25:17 +02:00
b96e6c8149 Fix import extend class (#4)
* Fix import extend class

yea

* Update Interp.hx

* Fix issues with compiling

* Improved extend Errors

---------

Co-authored-by: NeeEoo <NeeEoo@users.noreply.github.com>
2024-04-23 00:01:43 +02:00
NeeEoo f6402ac638 Improve Auto Complete Performance 2024-04-13 18:32:13 +02:00
Ne_EoandGitHub 1e55d0200b Update README.md 2024-02-15 19:43:11 +01:00
NeeEoo 1725dfd1ea Merge branch 'master' into custom-classes 2024-02-15 19:35:31 +01:00
NeeEoo a8cc0ccc86 Restructuring + KeyValueItherators + is + fixes 2023-09-23 21:23:31 +02:00
LeerandGitHub 7dd868aac7 fix compilation errors and make setVar public (#1)
* fix 1

* fix 2

* make setVar public
2023-09-23 20:32:31 +02:00
NeeEoo 220e5ce007 Merge branch 'custom-classes' 2023-08-17 09:55:12 +02:00
NeeEoo 80f6ea4502 Oops 2023-08-17 09:54:56 +02:00
NeeEoo eb820aaf6a Improved ?. 2023-08-17 09:51:43 +02:00
NeeEoo a754c162d9 Fix private class bug 2023-08-13 19:29:08 +02:00
NeeEoo de18ceb887 Fixed Type not found error 2023-08-13 19:24:08 +02:00
53 changed files with 7368 additions and 1040 deletions
+2
View File
@@ -1,2 +1,4 @@
/hscript.swf /hscript.swf
/release.zip /release.zip
tests/bin/*
+14 -4
View File
@@ -1,8 +1,18 @@
hscript hscript-improved
======= =======
[![TravisCI Build Status](https://travis-ci.org/HaxeFoundation/hscript.svg?branch=master)](https://travis-ci.org/HaxeFoundation/hscript) How to install
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/HaxeFoundation/hscript?branch=master&svg=true)](https://ci.appveyor.com/project/HaxeFoundation/hscript) ```
haxelib git hscript-improved https://github.com/FNF-CNE-Devs/hscript-improved.git
```
To enable custom classes support you have to do this in project.xml
```xml
<define name="CUSTOM_CLASSES" />
```
Warning: custom classes are sometimes broken, would like help to fix them. You can only override functions from the current class, not from the extended part, like you cant override update in FlxText because FlxText doesnt have a update function overriden
-----------
Parse and evalutate Haxe expressions. Parse and evalutate Haxe expressions.
@@ -100,4 +110,4 @@ Some other optional files :
- `hscript.Macro` : convert Haxe macro into hscript Expr - `hscript.Macro` : convert Haxe macro into hscript Expr
- `hscript.Printer` : convert hscript Expr to String - `hscript.Printer` : convert hscript Expr to String
- `hscript.Tools` : utility functions (map/iter) - `hscript.Tools` : utility functions (map/iter)
+4 -1
View File
@@ -1 +1,4 @@
--macro keep('IntIterator') --macro keep('IntIterator')
--macro hscript.macros.UsingHandler.init()
--macro hscript.macros.ClassExtendMacro.init()
--define HSCRIPT_NEW
+2 -1
View File
@@ -1233,9 +1233,10 @@ class Checker {
mergeType( typeExpr(defaultExpr, withType), defaultExpr); mergeType( typeExpr(defaultExpr, withType), defaultExpr);
return withType == NoValue ? TVoid : tmin == null ? makeMono() : tmin; return withType == NoValue ? TVoid : tmin == null ? makeMono() : tmin;
case ENew(cl, params): case ENew(cl, params):
default:
} }
error("Don't know how to type "+edef(expr).getName(), expr); error("Don't know how to type "+edef(expr).getName(), expr);
return TDynamic; return TDynamic;
} }
} }
+24
View File
@@ -0,0 +1,24 @@
package hscript;
class Config {
// Runs support for custom classes in these
public static final ALLOWED_CUSTOM_CLASSES = [
"flixel",
];
// Runs support for abstract support in these
public static final ALLOWED_ABSTRACT_AND_ENUM = [
"flixel",
"openfl.display.BlendMode",
];
// Incase any of your files fail
// These are the module names
public static final DISALLOW_CUSTOM_CLASSES = [
];
public static final DISALLOW_ABSTRACT_AND_ENUM = [
];
}
+83 -28
View File
@@ -1,5 +1,8 @@
package hscript; package hscript;
import hscript.Interp.DeclaredVar;
import hscript.utils.UnsafeReflect;
using StringTools; using StringTools;
class CustomClassHandler implements IHScriptCustomConstructor { class CustomClassHandler implements IHScriptCustomConstructor {
@@ -11,45 +14,58 @@ class CustomClassHandler implements IHScriptCustomConstructor {
public var extend:String; public var extend:String;
public var interfaces:Array<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>) { public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
this.ogInterp = ogInterp; this.ogInterp = ogInterp;
this.name = name; this.name = name;
this.fields = fields; this.fields = fields;
this.extend = extend; this.extend = extend;
this.interfaces = interfaces; 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 { public function hnew(args:Array<Dynamic>):Dynamic {
#if !HSCRIPT_NO_INT_VARS
throw "todo: do all this lmao -lunar";
return null;
#else
var interp = new Interp(); var interp = new Interp();
interp.errorHandler = ogInterp.errorHandler; interp.errorHandler = ogInterp.errorHandler;
var cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX'); var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
var _class = Type.createInstance(cl, args);
var __capturedLocals = ogInterp.duplicate(ogInterp.locals); var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
var capturedLocals:Map<String, {r:Dynamic, depth:Int}> = []; var capturedLocals:Map<String, DeclaredVar> = [];
for(k=>e in __capturedLocals) for(k=>e in __capturedLocals)
if (e != null && e.depth <= 0) if (e != null && e.depth <= 0)
capturedLocals.set(k, e); capturedLocals.set(k, e);
var disallowCopy = Type.getInstanceFields(cl); var disallowCopy = Type.getInstanceFields(cl);
//trace("Locals");
for (key => value in capturedLocals) { for (key => value in capturedLocals) {
if(!disallowCopy.contains(key)) { if(!disallowCopy.contains(key)) {
interp.locals.set(key, {r: value, depth: -1}); interp.locals.set(key, {r: value, depth: -1});
//trace(key, value);
} }
} }
//trace("Variables");
for (key => value in ogInterp.variables) { for (key => value in ogInterp.variables) {
if(!disallowCopy.contains(key)) { if(!disallowCopy.contains(key)) {
interp.variables.set(key, value); interp.variables.set(key, value);
//trace(key, value);
} }
} }
var comparisonMap = new Map();
for(key => value in interp.variables) {
comparisonMap.set(key, value);
}
_class.__custom__variables = interp.variables;
//trace(fields);
for(expr in fields) { for(expr in fields) {
@:privateAccess @:privateAccess
interp.exprReturn(expr); interp.exprReturn(expr);
@@ -57,19 +73,43 @@ class CustomClassHandler implements IHScriptCustomConstructor {
interp.variables.set("super", staticHandler); interp.variables.set("super", staticHandler);
_class.__interp = interp; _class.__interp = interp; // TODO: Remove
interp.scriptObject = _class; interp.scriptObject = _class;
// get only variables that were not set before
var newFunc = interp.variables.get("new"); var classVariables = [for(key => value in interp.variables) if(!comparisonMap.exists(key) || comparisonMap[key] != value) key => value];
if(newFunc != null) { for(variable => value in classVariables) {
Reflect.callMethod(null, newFunc, args); if(variable == "this" || variable == "super") continue;
@:privateAccess
if(!interp.__instanceFields.contains(variable)) {
interp.__instanceFields.push(variable);
}
} }
//trace([for(key => value in classVariables) key]);
//@:privateAccess
//trace(interp.__instanceFields);
_class.__allowSetGet = false;
for(variable => value in interp.variables) { for(variable => value in interp.variables) {
if(variable == "this") continue; if(variable == "this") continue;
if(variable.startsWith("set_") || variable.startsWith("get_")) {
_class.__allowSetGet = true;
}
} }
var newFunc = interp.variables.get("new");
if(newFunc != null) {
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
}
var newFunc = interp.variables.get("new");
if(newFunc != null) {
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
}
return _class; return _class;
#end
} }
public function toString():String { public function toString():String {
@@ -77,29 +117,44 @@ class CustomClassHandler implements IHScriptCustomConstructor {
} }
} }
class TemplateClass implements IHScriptCustomBehaviour { class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour {
public var __interp:Interp; 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 { public function hset(name:String, val:Dynamic):Dynamic {
if(this.__interp.variables.exists("set_" + name)) { if(__allowSetGet && __custom__variables.exists("set_" + name))
return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function return __callSetter(name, val);
} if (__custom__variables.exists(name)) {
if (this.__interp.variables.exists(name)) { __custom__variables.set(name, val);
this.__interp.variables.set(name, val);
return val; return val;
} }
Reflect.setProperty(this, name, val); UnsafeReflect.setProperty(this, name, val);
return Reflect.field(this, name); return UnsafeReflect.field(this, name);
} }
public function hget(name:String):Dynamic { public function hget(name:String):Dynamic {
if(this.__interp.variables.exists("get_" + name)) if(__allowSetGet && __custom__variables.exists("get_" + name))
return this.__interp.variables.get("get_" + name)(); return __callGetter(name);
if (this.__interp.variables.exists(name)) if (__custom__variables.exists(name))
return this.__interp.variables.get(name); return __custom__variables.get(name);
return Reflect.getProperty(this, 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;
} }
} }
class StaticHandler { final class StaticHandler {
public function new() {} public function new() {}
} }
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
#if hscriptPos
class Error_ {
public var e : ErrorDef;
public var pmin : Int;
public var pmax : Int;
public var origin : String;
public var line : Int;
public function new(e, pmin, pmax, origin, line) {
this.e = e;
this.pmin = pmin;
this.pmax = pmax;
this.origin = origin;
this.line = line;
}
public function toString(): String {
return Printer.errorToString(this);
}
}
enum ErrorDef
#else
enum Error_
#end
{
EInvalidChar( c : Int );
EUnexpected( s : String );
EUnterminatedString;
EUnterminatedComment;
EUnterminatedRegex;
EInvalidPreprocessor( msg : String );
EUnknownVariable( v : String );
EInvalidIterator( v : String );
EInvalidType( t : String );
EInvalidOp( op : String );
EInvalidAccess( f : String, ?on : String );
ECustom( msg : String );
EPreset( msg : ErrorMessage );
EInvalidClass( className : String);
EAlreadyExistingClass( className : String);
EInvalidEscape( s : String );
}
enum abstract ErrorMessage(Expr.UInt8) from Expr.UInt8 to Expr.UInt8 {
final INVALID_CHAR_CODE_MULTI;
final FROM_CHAR_CODE_NON_INT;
final EMPTY_INTERPOLATION;
final UNKNOWN_MAP_TYPE;
final UNKNOWN_MAP_TYPE_RUNTIME;
final EXPECT_KEY_VALUE_SYNTAX;
public function toString():String {
return switch(cast this) {
case INVALID_CHAR_CODE_MULTI: "'char'.code only works on single characters";
case FROM_CHAR_CODE_NON_INT: "String.fromCharCode only works on integers";
case EMPTY_INTERPOLATION: "Invalid interpolation: Expression cannot be empty";
case UNKNOWN_MAP_TYPE: "Unknown Map Type";
case UNKNOWN_MAP_TYPE_RUNTIME: "Unknown Map Type, while parsing at runtime";
case EXPECT_KEY_VALUE_SYNTAX: "Expected a => b";
}
}
}
+293 -81
View File
@@ -21,6 +21,16 @@
*/ */
package hscript; package hscript;
typedef Int8 = #if cpp cpp.Int8 #else Int #end;
typedef Int16 = #if cpp cpp.Int16 #else Int #end;
typedef Int32 = #if cpp cpp.Int32 #else Int #end;
typedef Int64 = #if cpp cpp.Int64 #else Int #end;
typedef UInt8 = #if cpp cpp.UInt8 #else Int #end;
typedef UInt16 = #if cpp cpp.UInt16 #else Int #end;
typedef UInt32 = #if cpp cpp.UInt32 #else Int #end;
typedef UInt64 = #if cpp cpp.UInt64 #else Int #end;
enum Const { enum Const {
CInt( v : Int ); CInt( v : Int );
CFloat( f : Float ); CFloat( f : Float );
@@ -30,67 +40,20 @@ enum Const {
#end #end
} }
#if hscriptPos enum abstract MapType(UInt8) {
typedef Expr = { var Null;
var e : ExprDef; var IntMap;
var pmin : Int; var StringMap;
var pmax : Int; var EnumMap;
var origin : String; var ObjectMap;
var line : Int; var UnknownMap;
}
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 );
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> );
} }
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr }; typedef Error = hscript.Error.Error_;
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 );
}
#if hscriptPos #if hscriptPos
class Error { class Expr {
public var e : ErrorDef; public var e : ExprDef;
public var pmin : Int; public var pmin : Int;
public var pmax : Int; public var pmax : Int;
public var origin : String; public var origin : String;
@@ -102,28 +65,271 @@ class Error {
this.origin = origin; this.origin = origin;
this.line = line; this.line = line;
} }
public function toString(): String {
return Printer.errorToString(this);
}
} }
enum ErrorDef { enum ExprDef
#else #else
enum Error { typedef ExprDef = Expr;
enum Expr
#end #end
EInvalidChar( c : Int ); {
EUnexpected( s : String ); EConst( c : Const );
EUnterminatedString; EIdent( v : VarN );
EUnterminatedComment; EVar( n : VarN, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool );
EInvalidPreprocessor( msg : String ); EParent( e : Expr, ?noOptimize : Bool );
EUnknownVariable( v : String ); EBlock( e : Array<Expr> );
EInvalidIterator( v : String ); EField( e : Expr, f : String , ?safe : Bool );
EInvalidOp( op : String ); EBinop( op : Binop, e1 : Expr, e2 : Expr );
EInvalidAccess( f : String ); EUnop( op : Unop, prefix : Bool, e : Expr );
ECustom( msg : String ); ECall( e : Expr, params : Array<Expr> );
EInvalidClass( className : String); EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
EAlreadyExistingClass( className : String); EWhile( cond : Expr, e : Expr );
EFor( v : VarN, it : Expr, e : Expr);
EForKeyValue( v : VarN, it : Expr, e : Expr, ithv: VarN);
EBreak;
EContinue;
EFunction( args : Array<Argument>, e : Expr, ?name : VarN, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool );
EReturn( ?e : Expr );
EArray( e : Expr, index : Expr );
EMapDecl( type: MapType, keys: Array<Expr>, values: Array<Expr> );
EArrayDecl( e : Array<Expr> );
ENew( cl : VarN, params : Array<Expr> );
EThrow( e : Expr );
ETry( e : Expr, v : VarN, t : Null<CType>, ecatch : Expr );
EObject( fl : Array<ObjectField> );
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
ESwitch( e : Expr, cases : Array<SwitchCase>, ?defaultExpr : Expr );
EDoWhile( cond : Expr, e : Expr);
EMeta( name : String, args : Array<Expr>, e : Expr );
ECheckType( e : Expr, t : CType );
EImport( c : String, mode: KImportMode );
EClass( name:VarN, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
#if !HSCRIPT_NO_INT_VARS
EInfo( info: InfoClass, e: Expr );
#end
} }
enum Binop {
/**
`+`
**/
OpAdd;
/**
`*`
**/
OpMult;
/**
`/`
**/
OpDiv;
/**
`-`
**/
OpSub;
/**
`=`
**/
OpAssign;
/**
`==`
**/
OpEq;
/**
`!=`
**/
OpNotEq;
/**
`>`
**/
OpGt;
/**
`>=`
**/
OpGte;
/**
`<`
**/
OpLt;
/**
`<=`
**/
OpLte;
/**
`&`
**/
OpAnd;
/**
`|`
**/
OpOr;
/**
`^`
**/
OpXor;
/**
`&&`
**/
OpBoolAnd;
/**
`||`
**/
OpBoolOr;
/**
`<<`
**/
OpShl;
/**
`>>`
**/
OpShr;
/**
`>>>`
**/
OpUShr;
/**
`%`
**/
OpMod;
/**
`+=` `-=` `/=` `*=` `<<=` `>>=` `>>>=` `|=` `&=` `^=` `%=`
**/
OpAssignOp(op:Binop);
/**
`...`
**/
OpInterval;
/**
`=>`
**/
OpArrow;
/**
`is`
**/
OpIs; // used to be OpIn, but our system treats that differently
/**
`??`
**/
OpNullCoal;
}
enum abstract Unop(UInt8) {
/**
`++`
**/
var OpIncrement;
/**
`--`
**/
var OpDecrement;
/**
`!`
**/
var OpNot;
/**
`-`
**/
var OpNeg;
/**
`~`
**/
var OpNegBits;
/**
`...`
**/
var OpSpread;
}
#if !HSCRIPT_NO_INT_VARS
class InfoClass {
public var variables:Array<String>;
public function new(variables:Array<String>) {
this.variables = variables;
}
}
#end
enum KImportMode {
INormal;
IAs( name : String );
IAsReturn;
IAll;
}
class ObjectField {
public var name:String;
public var e:Expr;
public function new(name, e) {
this.name = name;
this.e = e;
}
}
@:structInit
final class SwitchCase {
public var values : Array<Expr>;
public var expr : Expr;
}
//typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
class Argument {
public var name : VarN;
public var t : Null<CType>;
public var opt : Bool;
public var value : Null<Expr>;
public function new(name, ?t, ?opt, ?value) {
this.name = name;
this.t = t;
this.opt = opt;
this.value = value;
}
public function toString() {
return (opt ? "?" : "") + name + (t != null ? ":" + Printer.convertTypeToString(t) : "") + (value != null ? "=" + Printer.convertExprToString(value) : "");
}
}
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
enum CType {
CTPath( path : Array<String>, ?params : Array<CType> );
CTFun( args : Array<CType>, ret : CType );
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
CTParent( t : CType );
CTOpt( t : CType );
CTNamed( n : String, t : CType );
}
enum ModuleDecl { enum ModuleDecl {
DPackage( path : Array<String> ); DPackage( path : Array<String> );
@@ -157,13 +363,13 @@ typedef FieldDecl = {
var access : Array<FieldAccess>; var access : Array<FieldAccess>;
} }
enum FieldAccess { enum abstract FieldAccess(UInt8) {
APublic; var APublic;
APrivate; var APrivate;
AInline; var AInline;
AOverride; var AOverride;
AStatic; var AStatic;
AMacro; var AMacro;
} }
enum FieldKind { enum FieldKind {
@@ -183,3 +389,9 @@ typedef VarDecl = {
var expr : Null<Expr>; var expr : Null<Expr>;
var type : Null<CType>; var type : Null<CType>;
} }
#if !HSCRIPT_NO_INT_VARS
abstract VarN(Dynamic) from Int from String to Int to String {}
#else
typedef VarN = String;
#end
+16
View File
@@ -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;
}
+912 -303
View File
File diff suppressed because it is too large Load Diff
+20 -2
View File
@@ -198,14 +198,24 @@ class Macro {
EWhile(convert(c), convert(e), false); EWhile(convert(c), convert(e), false);
case EFor(v, it, efor): case EFor(v, it, efor):
#if (haxe_ver >= 4) #if (haxe_ver >= 4)
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end; var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor)); EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
#elseif (haxe_211 || haxe3) #elseif (haxe_211 || haxe3)
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end; var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor)); EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
#else #else
EFor(v, convert(it), convert(efor)); EFor(v, convert(it), convert(efor));
#end #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: case EBreak:
EBreak; EBreak;
case EContinue: case EContinue:
@@ -229,6 +239,12 @@ class Macro {
EReturn(e == null ? null : convert(e)); EReturn(e == null ? null : convert(e));
case EArray(e, index): case EArray(e, index):
EArray(convert(e), convert(index)); EArray(convert(e), convert(index));
case EMapDecl(type, keys, values): // port to array decl, haxe still uses EArrayDecl but with => as a binop
var el = [];
for( i in 0...keys.length ) {
el.push( hscript.Expr.EBinop("=>", keys[i], values[i]) );
}
EArrayDecl(map(el,convert));
case EArrayDecl(el): case EArrayDecl(el):
EArrayDecl(map(el,convert)); EArrayDecl(map(el,convert));
case ENew(cl, params): case ENew(cl, params):
@@ -252,6 +268,8 @@ class Macro {
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub)); EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
case ECheckType(e, t): case ECheckType(e, t):
ECheckType(convert(e), convertType(t)); ECheckType(convert(e), convertType(t));
default:
null;
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end } }, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
} }
+643
View File
@@ -0,0 +1,643 @@
package hscript;
import hscript.Expr;
import hscript.Tools;
import hscript.Parser;
@:access(hscript.Parser)
class Optimizer {
static inline function expr(e:Expr) return Tools.expr(e);
public static function optimize(s:Expr):Expr {
if(s == null)
return null;
var e = Tools.expr(s);
// TODO: Convert EBinOp(+, a, EUnop(-, b)) to EBinOp(-, a, b) aka (a - b)
// TODO: Make it so if EFor & EForKeyValue iterator variable isnt used its replaced with a $, which the code handles as doesnt exist
// TODO: Make it so <PURE_VAR|CONST> == ?; gets removed, IF it isnt stored
// TODO: Optimize ?. and ??
// TODO: Optimize Std.string(CONSTANT) to "CONSTANT"
// TODO: Remove Std.string if its the right side of a binop of a string
// TODO: Add EForComprehension, EWhileComprehension, EDoWhileComprehension for better optimization
// TODO: Optimize EFor(v, it, EBlock([e1])) to EFor(v, it, e1)
// TODO: Optimize EIf(cond, e1, e2) to EBlock([cond, e1]) if e1 == e2, if cond is constant or side effect free then convert to EParent(e1)
switch(e) {
// Parse all expressions and recreate the AST
case EBlock(exprs):
var exprs = exprs.map((v) -> optimize(v)).filter((v) -> v != null).filter((v) -> {
if(Tools.expr(v).match(EBlock([]))) return false; // Remove empty blocks
return true;
});
var newExprs = [];
for(e in exprs) {
switch(Tools.expr(e)) {
case EBlock(iex):
var declares = Lambda.exists(iex, ie -> hasDecl(ie));
if(!declares) {
for(ex in iex)
newExprs.push(ex);
} else {
newExprs.push(e);
}
case EReturn(_): // remove stuff that are after a return
newExprs.push(e);
break;
default:
newExprs.push(e);
}
}
return mk(EBlock(newExprs), s);
case EIf(econd, e1, e2):
var econd = optimize(econd);
var e1 = optimize(e1);
var e2 = optimize(e2);
if(isBool(econd)) {
var econd = getBool(econd);
if(econd == true) {
if(e1 == null)
return mk(EBlock([]), s);
return mk(EBlock([e1]), s);
} else {
if(e2 == null)
return mk(EBlock([]), s);
return mk(EBlock([e2]), s);
}
}
if(e2 != null && isBool(e1) && isBool(e2)) {
var c1 = getBool(e1);
var c2 = getBool(e2);
if(c1 == false && c2 == true) { // (VAR ? false : true)
return optimize(mk(EUnop(OpNot, true, econd), s));
}
if(c1 == true && c2 == false) { // (VAR ? true : false)
return optimize(mk(Tools.expr(econd), s));
}
if(isConstant(econd)) { // Side effect free
if(c1 == true && c2 == true) { // (CONST ? true : true)
return mk(convertConstant(true), s);
}
if(c1 == false && c2 == false) { // (CONST ? false : false)
return mk(convertConstant(false), s);
}
// TODO: Check if local variables are used in the condition
// Since they cant have side effects, they can be optimized
}
}
return mk(EIf(econd, e1, e2), s);
case ETernary(econd, e1, e2):
var econd = optimize(econd);
var e1 = optimize(e1);
var e2 = optimize(e2);
if(isConstant(econd)) {
var econd = getConstant(econd);
if(econd == true) {
if(e1 == null)
return mk(EBlock([]), s);
return mk(EParent(e1), s);
} else {
if(e2 == null)
return mk(EBlock([]), s);
return mk(EParent(e2), s);
}
}
if(isBool(e1) && isBool(e2)) {
var c1 = getBool(e1);
var c2 = getBool(e2);
if(c1 == false && c2 == true) { // (VAR ? false : true)
return optimize(mk(EUnop(OpNot, true, econd), s));
}
if(c1 == true && c2 == false) { // (VAR ? true : false)
return optimize(mk(Tools.expr(econd), s));
}
if(isConstant(econd)) { // Side effect free
if(c1 == true && c2 == true) { // (CONST ? true : true)
return mk(convertConstant(true), s);
}
if(c1 == false && c2 == false) { // (CONST ? false : false)
return mk(convertConstant(false), s);
}
// TODO: Check if local variables are used in the condition
// Since they cant have side effects, they can be optimized
}
}
return mk(ETernary(econd, e1, e2), s);
case EWhile(econd, e):
var econd = optimize(econd);
var e = optimize(e);
return mk(EWhile(econd, e), s);
case EDoWhile(econd, e):
var econd = optimize(econd);
var e = optimize(e);
return mk(EDoWhile(econd, e), s);
case EFor(v, it, e):
var it = optimize(it);
var e = optimize(e);
return mk(EFor(v, it, e), s);
case EForKeyValue(v, it, e, ithv):
var it = optimize(it);
var e = optimize(e);
return mk(EForKeyValue(v, it, e, ithv), s);
case EBreak:
return mk(EBreak, s);
case EContinue:
return mk(EContinue, s);
case EReturn(e):
var e = optimize(e);
return mk(EReturn(e), s);
case ETry(e, v, t, ecatch):
var e = optimize(e);
var ecatch = optimize(ecatch);
return mk(ETry(e, v, t, ecatch), s);
case EThrow(e):
var e = optimize(e);
return mk(EThrow(e), s);
case EVar(n, t, e, isPublic, isStatic):
e = optimize(e);
return mk(EVar(n, t, e, isPublic, isStatic), s);
case ECall(e, params):
e = optimize(e);
params = params.map((v) -> optimize(v));
function p(i:Int, t:ConstType, opt:Bool = false):Dynamic {
var p = params[i];
if(t != getConstType(p))
if(opt)
return null;
else
throw Parser.getBaseError(EInvalidType(getTypeName(p)));
return switch(t) {
case CTInt: getInt(p);
case CTFloat: getFloat(p);
case CTBool: getBool(p);
case CTString: getStringConstant(p);
case CTNull: null;
}
}
switch(Tools.expr(e)) {
case EField(expr(_) => EConst(CString(str)), field):
switch(field) {
case "toString":
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toString() takes no arguments"));
return mk(convertConstant(str.toString()), s);
case "toUpperCase":
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toUpperCase() takes no arguments"));
return mk(convertConstant(str.toUpperCase()), s);
case "toLowerCase":
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toLowerCase() takes no arguments"));
return mk(convertConstant(str.toLowerCase()), s);
case "charAt":
var index = p(0, CTInt);
return mk(convertConstant(str.charAt(index)), s);
case "charCodeAt":
var index = p(0, CTInt);
return mk(convertConstant(str.charCodeAt(index)), s);
case "indexOf":
var value = p(0, CTString);
var startIndex = p(1, CTInt, true);
return mk(convertConstant(str.indexOf(value, startIndex)), s);
case "lastIndexOf":
var value = p(0, CTString);
var startIndex = p(1, CTInt, true);
return mk(convertConstant(str.lastIndexOf(value, startIndex)), s);
case "split":
var delimiter = p(0, CTString);
var strArr = str == "" ? [] : str.split(delimiter); // fix platform dependent behavior
return mk(EArrayDecl([for(st in strArr) mk(EConst(CString(st)), s)]), s);
case "substr":
var pos = p(0, CTInt);
var len = p(1, CTInt, true);
return mk(convertConstant(str.substr(pos, len)), s);
case "substring":
var startIndex = p(0, CTInt);
var endIndex = p(1, CTInt, true);
return mk(convertConstant(str.substring(startIndex, endIndex)), s);
}
default:
}
switch(Tools.expr(e)) {
default:
}
return mk(ECall(e, params), s);
case EField(e, f, safe):
e = optimize(e);
switch(Tools.expr(e)) {
case EConst(CString(str)) if(f == "length"):
return mk(convertConstant(str.length), s);
default:
}
return mk(EField(e, f, safe), s);
case EIdent(_) | EConst(_):
return s;
case EParent(e, noOptimize):
e = optimize(e);
return mk(noOptimize ? EParent(e) : Tools.expr(e), s);
case ECheckType(e, t):
e = optimize(e);
return mk(ECheckType(e, t), s);
case EMeta(name, args, e):
e = optimize(e);
return mk(EMeta(name, args, e), s);
case ENew(cl, args):
args = args.map((v) -> optimize(v));
return mk(ENew(cl, args), s);
case EObject(fl):
fl.map((v) -> {
v.e = optimize(v.e);
});
return mk(EObject(fl), s);
case EImport(c, n):
return mk(EImport(c, n), s);
case EClass(name, fields, extend, interfaces): // Possible code not working
fields = fields.map((v) -> optimize(v));
return mk(EClass(name, fields, extend, interfaces), s);
case EFunction(args, e, name, ret, isPublic, isStatic, isOverride):
args.map((v) -> {
v.name = v.name;
v.value = optimize(v.value);
});
e = optimize(e);
return mk(EFunction(args, e, name, ret, isPublic, isStatic, isOverride), s);
case ESwitch(e, cases, def):
e = optimize(e);
cases.map((v) -> {
var values = v.values.map((v) -> optimize(v));
v.expr = optimize(v.expr);
});
def = optimize(def);
if(isConstant(e)) {
var econd = getConstant(e);
for(c in cases) {
for(v in c.values) {
if(isConstant(v)) {
var value = getConstant(v);
if(value == econd)
return mk(Tools.expr(c.expr), s);
}
}
}
// Maybe convert this to a Lambda.foreach?
var isAllCasesConstant = true;
for(c in cases) {
for(v in c.values) {
if(!isConstant(v)) {
isAllCasesConstant = false;
break;
}
}
}
if(isAllCasesConstant) {
#if debug
//trace("Didnt find any cases that match");
#end
if(def != null)
return mk(Tools.expr(def), s);
else
return mk(EBlock([]), s);
}
}
return mk(ESwitch(e, cases, def), s);
case EMapDecl(type, keys, values):
keys = keys.map((v) -> optimize(v));
values = values.map((v) -> optimize(v));
return mk(EMapDecl(type, keys, values), s);
case EArrayDecl(arr):
arr = arr.map((v) -> optimize(v));
return mk(EArrayDecl(arr), s);
case EArray(e, index):
e = optimize(e);
index = optimize(index);
if(isConstant(index)) {
if(Tools.expr(e).match(EArrayDecl(_))) {
var arr = switch(Tools.expr(e)) {
case EArrayDecl(arr): arr;
default: null;
};
var constant = Lambda.exists(arr, isConstant);
var index = getInt(index);
if(constant && index != null) {
return mk(Tools.expr(arr[index]), s);
}
}
if(Tools.expr(e).match(EMapDecl(_))) {
var map = switch(Tools.expr(e)) {
case EMapDecl(type, keys, vals): [for(i in 0...keys.length) [keys[i], vals[i]]];
default: null;
};
var constant = Lambda.exists(map, (v) -> isConstant(v[0]) && isConstant(v[1]));
if(constant) {
var idx = Lambda.findIndex(map, (v) -> Type.enumEq(Tools.expr(v[0]), Tools.expr(index)));
if(idx == -1)
return mk(EIdent("null"), s);
return mk(Tools.expr(map[idx][1]), s);
}
}
}
return mk(EArray(e, index), s);
case EBinop(op, e1, e2):
e1 = optimize(e1);
e2 = optimize(e2);
if(isConstant(e1) && isConstant(e2)) {
var optimized:Dynamic = optimizeOp(op, getConstant(e1), getConstant(e2));
if(optimized != null)
return mk(convertConstant(optimized), s);
}
// Possible bugs here
if(isNumber(e1) && !isConstant(e2)) {
var c1 = getNumber(e1);
if(compareNumber(c1, 0) && op == OpAdd)
return mk(Tools.expr(e2), s);
if(compareNumber(c1, 1) && op == OpMult)
return mk(Tools.expr(e2), s);
//if(compareNumber(c1, 0) && op == "*")
// return mk(convertConstant(0), s);
}
if(!isConstant(e1) && isNumber(e2)) {
var c2 = getNumber(e2);
if(op == OpAdd && compareNumber(c2, 0))
return mk(Tools.expr(e1), s);
if(op == OpDiv && compareNumber(c2, 1))
return mk(Tools.expr(e1), s);
if(op == OpMult && compareNumber(c2, 1))
return mk(Tools.expr(e1), s);
//if(op == "*" && compareNumber(c2, 0))
// return mk(convertConstant(0), s);
}
return mk(EBinop(op, e1, e2), s);
case EUnop(op, prefix, e):
e = optimize(e);
if(isConstant(e) && prefix) {
var constant:Dynamic = getConstant(e);
switch(op) {
case OpNeg: return mk(convertConstant(-constant), s);
case OpNot: return mk(convertConstant(!constant), s);
case OpNegBits:
var complement = #if (neko && !haxe3) haxe.Int32.complement(constant) #else ~constant #end;
return mk(convertConstant(complement), s);
default:
}
}
return mk(EUnop(op, prefix, e), s);
default:
Sys.println("Unknown expr: " + e);
}
return s;
}
static function compareNumber(a:Dynamic, b:Dynamic):Bool {
return switch(Type.typeof(a)) {
case TInt:
var a:Int = cast a;
switch(Type.typeof(b)) {
case TInt: a == cast(b, Int);
case TFloat: a == cast(b, Float);
default: false;
}
case TFloat:
var a:Float = cast a;
switch(Type.typeof(b)) {
case TInt: a == cast(b, Int);
case TFloat: a == cast(b, Float);
default: false;
}
default: false;
}
}
static function hasDecl(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EVar(_): true;// has a declaration
case EFunction(_): true;// has a declaration
default: false;
}
}
static function getTypeName(e:Expr):String {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CInt(_)): "Int";
case EConst(CFloat(_)): "Float";
case EConst(CString(_)): "String";
case EIdent("true") | EIdent("false"): "Bool";
case EIdent("null"): "Null";
case EIdent(_): "Dynamic";
case EParent(e): getTypeName(e);
default: Std.string(Tools.expr(e));
}
}
static function getConstType(e:Expr):ConstType {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CInt(_)): CTInt;
case EConst(CFloat(_)): CTFloat;
case EConst(CString(_)): CTString;
case EIdent("true") | EIdent("false"): CTBool;
case EIdent("null"): CTNull;
case EParent(e): getConstType(e);
default: throw "Unknown type " + Tools.expr(e);
}
}
static function isConstant(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EIdent("true") | EIdent("false") | EIdent("null"): true;
case EConst(_): true;
case EParent(e): isConstant(e);
default: false;
}
}
static function isBool(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EIdent("true") | EIdent("false"): true;
case EParent(e): isBool(e);
default: false;
}
}
static function isNumber(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EConst(CInt(_)): true;
case EConst(CFloat(_)): true;
case EParent(e): isNumber(e);
default: false;
}
}
static function getNumber(e:Expr):Dynamic {
return switch(Tools.expr(e)) {
case EConst(CInt(value)): value;
case EConst(CFloat(value)): value;
case EParent(e): getNumber(e);
default: throw "Unknown type " + Tools.expr(e);
}
}
static function getBool(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EIdent("true"): true;
case EIdent("false"): false;
case EParent(e): getBool(e);
default: throw "Unknown type " + Tools.expr(e);
}
}
static function getInt(e:Expr):Null<Int> {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CInt(value)): value;
case EParent(e): getInt(e);
default: null;
}
}
static function getFloat(e:Expr):Null<Float> {
if(e == null)
return null;
return switch(Tools.expr(e)) {
case EConst(CFloat(value)): value;
case EParent(e): getFloat(e);
default: null;
}
}
static function isString(e:Expr):Bool {
return switch(Tools.expr(e)) {
case EConst(CString(_)): true;
case EParent(e): isString(e);
default: false;
}
}
static function getStringConstant(e:Expr):String {
return switch(Tools.expr(e)) {
case EConst(CString(value)): value;
case EParent(e): getStringConstant(e);
default: null;
}
}
static function getConstant(e:Expr):Dynamic {
return switch(Tools.expr(e)) {
case EParent(e): getConstant(e);
case EIdent("true"): true;
case EIdent("false"): false;
case EIdent("null"): null;
case EConst(CInt(value)): value;
case EConst(CFloat(value)): value;
case EConst(CString(value)): value;
default: throw "Unknown constant " + Tools.expr(e);
}
}
static function convertConstant(value:Dynamic):ExprDef {
return switch(Type.typeof(value)) {
case TInt: EConst(CInt(value));
case TFloat: EConst(CFloat(value));
case TBool: EIdent(value == true ? "true" : "false");
//case TString: EConst(CString(value));
case TNull: EIdent("null");
case TClass(String): EConst(CString(value));
default: throw "Unknown type " + Type.typeof(value);
}
}
static function mk(e:ExprDef, s:Expr):Expr {
#if hscriptPos
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
#else
return e;
#end
}
static function optimizeOp(op:Binop, f1:Dynamic, f2:Dynamic):Dynamic {
//trace("Optimizing " + f1 + " " + op + " " + f2);
return switch(op) {
case OpAdd: f1 + f2;
case OpSub: f1 - f2;
case OpMult: f1 * f2;
case OpDiv: f1 / f2;
case OpMod: f1 % f2;
case OpAnd: f1 & f2;
case OpOr: f1 | f2;
case OpXor: f1 ^ f2;
case OpShl: f1 << f2;
case OpShr: f1 >> f2;
case OpUShr: f1 >>> f2;
case OpEq: f1 == f2;
case OpNotEq: f1 != f2;
case OpGte: f1 >= f2;
case OpLte: f1 <= f2;
case OpGt: f1 > f2;
case OpLt: f1 < f2;
case OpBoolOr: f1 == true || f2 == true;
case OpBoolAnd: f1 == true && f2 == true;
case OpNullCoal: f1 == null ? f2 : f1;
default: null;
}
}
}
enum ConstType {
CTInt;
CTFloat;
CTBool;
CTString;
CTNull;
}
+796 -228
View File
File diff suppressed because it is too large Load Diff
+107
View File
@@ -0,0 +1,107 @@
package hscript;
import hscript.Expr;
import hscript.Error;
import hscript.Tools;
import hscript.Parser;
@:access(hscript.Parser)
class Postprocessor {
static inline function expr(e:Expr) return Tools.expr(e);
static function mk(e:ExprDef, s:Expr):Expr {
#if hscriptPos
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
#else
return e;
#end
}
#if !HSCRIPT_NO_INT_VARS
public static function processvars(e:Expr, ?vars:Array<String>) {
if(vars == null) vars = [];
for(v in getvars(e)) vars.push(v);
e = _processvars(e, vars);
return mk(EInfo(new InfoClass(vars), e), e);
}
private static function _processvars(e:Expr, vars:Array<String>):Expr {
if(e == null) return null;
var ge = e;
var doExtra = true;
var e = switch (expr(e)) {
case EIdent(v) if (v is String):
mk(EIdent(vars.indexOf(v)), e);
case EVar(n, t, _e, p, s) if (n is String):
mk(EVar(vars.indexOf(n), t, _e, p, s), e);
case EImport(c, IAs(n)) if (n is String):
mk(EVar(vars.indexOf(n), null, mk(EImport(c, IAsReturn), e), false, false), e);
case EFunction(args, _e, n, r, p, s, o) if (n is String):
var _args:Array<Argument> = [
for (arg in args)
new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value)
];
mk(EFunction(_args, _e, vars.indexOf(n), r, p, s, o), e);
case EFunction(args, _e, null, r, p, s, o):
var _args:Array<Argument> = [
for (arg in args)
new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value)
];
mk(EFunction(_args, _e, null, r, p, s, o), e);
case ENew(cl, p) if (cl is String):
var p = [for(arg in p) arg];
mk(ENew(vars.indexOf(cl), p), e);
case EClass(n, fls, extnd, i) if (n is String):
var fls:Array<Expr> = [for(f in fls) f];
mk(EClass(vars.indexOf(n), fls, extnd, i), e);
case ETry(_e, v, t, ec) if (v is String):
mk(ETry(_e, vars.indexOf(v), t, ec), e);
case EFor(v, it, _e) if (v is String):
mk(EFor(vars.indexOf(v), it, _e), e);
case EForKeyValue(v, it, _e, ithv) if (v is String && ithv is String):
mk(EForKeyValue(vars.indexOf(v), it, _e, vars.indexOf(ithv)), e);
default: {
doExtra = false;
e;
}
};
e = Tools.map(e, function(e) {
return _processvars(e, vars);
});
return e;
}
public static function getvars(se:Expr, ?vars:Array<String>):Array<String> {
inline function pushVar(v:VarN) {
if(v is String) {
var v:String = cast v;
if (!vars.contains(v))
vars.push(v);
}
};
if(vars == null) vars = [];
Tools.iterExprRecursive(se, (e) -> {
switch (expr(e)) {
case EVar(n, t, e, _,_): pushVar(n);
case EIdent(v): pushVar(v);
case EFunction(args,e,name,_,_,_,_):
pushVar(name);
for (a in args) {
pushVar(a.name);
}
case EFor(v,it,e): pushVar(v);
case EForKeyValue(v,it,e,ithv): pushVar(ithv); pushVar(v);
case ETry(e, v, _, ec): pushVar(v);
case ENew(cl, params): pushVar(cl);
case EClass(name, fls,_,_): pushVar(name);
default:
}
});
return vars;
}
#end
}
+147
View File
@@ -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;
}
}
+476 -218
View File
@@ -37,6 +37,11 @@ class Printer {
return buf.toString(); return buf.toString();
} }
public static function convertExprToString( e : Expr ) {
var printer = new Printer();
return printer.exprToString(e);
}
public function typeToString( t : CType ) { public function typeToString( t : CType ) {
buf = new StringBuf(); buf = new StringBuf();
tabs = ""; tabs = "";
@@ -44,6 +49,11 @@ class Printer {
return buf.toString(); 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); inline function add<T>(s:T) buf.add(s);
function type( t : CType ) { function type( t : CType ) {
@@ -68,11 +78,14 @@ class Printer {
type(t); type(t);
case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))): case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))):
add('('); add('(');
for (a in args) var first = true;
for (i=>a in args) {
switch a { switch a {
case CTNamed(_, _): type(a); case CTNamed(_, _): type(a);
default: type(CTNamed('_', a)); default: type(CTNamed('_', a));
} }
if( i != args.length - 1 ) add(", ");
}
add(')->'); add(')->');
type(ret); type(ret);
case CTFun(args, ret): case CTFun(args, ret):
@@ -108,260 +121,505 @@ 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 getVar(v:VarN) {
if( v == null ) {
return ("??NULL??");
}
if(v is Int) {
return ("$" + v);
} else {
return (v);
}
}
public static function getBinaryOp(op:Binop) {
return switch(op) {
case OpAdd: "+";
case OpSub: "-";
case OpMult: "*";
case OpDiv: "/";
case OpMod: "%";
case OpAnd: "&";
case OpOr: "|";
case OpXor: "^";
case OpShl: "<<";
case OpShr: ">>";
case OpUShr: ">>>";
case OpEq: "==";
case OpNotEq: "!=";
case OpGt: ">";
case OpGte: ">=";
case OpLt: "<";
case OpLte: "<=";
case OpBoolAnd: "&&";
case OpBoolOr: "||";
case OpIs: "is";
case OpNullCoal: "??";
case OpAssign: "=";
case OpArrow: "=>";
case OpInterval: "...";
case OpAssignOp(op): getBinaryOp(op) + "=";
}
}
public static function getUnaryOp(op:Unop) {
return switch(op) {
case OpIncrement: "++";
case OpDecrement: "--";
case OpNot: "!";
case OpNeg: "-";
case OpNegBits: "~";
case OpSpread: "...";
}
}
function expr( e : Expr ) { function expr( e : Expr ) {
if( e == null ) { if( e == null ) {
add("??NULL??"); add("??NULL??");
return; return;
} }
switch( #if hscriptPos e.e #else e #end ) {
case EImport(c, n):
add("import " + c);
if(n != null)
add(' as $n');
case EClass(name, fields, extend, interfaces):
add('class $name');
if (extend != null)
add(' extends $extend');
for(_interface in interfaces) {
add(' implements $_interface');
}
add(' {\n');
tabs += "\t";
//for(field in fields) {
// expr(field);
//}
tabs = tabs.substr(1); // TODO: make else if print correctly
add("}");
case EConst(c): switch( Tools.expr(e) ) {
switch( c ) { case EImport(c, mode):
case CInt(i): add(i); add("import " + c);
case CFloat(f): add(f); switch(mode) {
case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"'); case IAs(name): add(' as $name');
} case IAll: add('.*');
case EIdent(v): default:
add(v); }
case EVar(n, t, e): // TODO: static, public, override case EClass(name, fields, extend, interfaces):
add("var " + n); add('class ${getVar(name)}');
addType(t); if (extend != null)
if( e != null ) { add(' extends $extend');
add(" = "); for(_interface in interfaces) {
expr(e); add(' implements $_interface');
} }
case EParent(e): add(' {\n');
add("("); expr(e); add(")");
case EBlock(el):
if( el.length == 0 ) {
add("{}");
} else {
tabs += "\t"; tabs += "\t";
add("{\n"); // TODO: Print fields
for( e in el ) { //for(field in fields) {
add(tabs); // expr(field);
//}
tabs = tabs.substr(1);
add("}");
case EConst(c):
switch( c ) {
case CInt(i): add(i);
case CFloat(f): add(f);
case CString(s):
add('"');
add(getEscapedString(s));
add('"');
}
case EIdent(v):
add(getVar(v));
case EVar(n, t, e): // TODO: static, public, override
add("var " + getVar(n));
if( t != null )
addType(t);
else
switch( Tools.expr(e) ) {
case EMapDecl(type, _, _):
add(" : Map<");
switch( type ) {
case ObjectMap: add("Dynamic");
case StringMap: add("String");
case EnumMap: add("EnumValue");
case IntMap: add("Int");
case UnknownMap: add("?");
case Null: add("Null");
}
add(", Dynamic>");
default:
}
if( e != null ) {
add(" = ");
expr(e); expr(e);
add(";\n");
} }
tabs = tabs.substr(1); case EParent(e):
add("}"); add("("); expr(e); add(")");
} case EBlock(el):
case EField(e, f, s): if( el.length == 0 ) {
expr(e); add("{}");
add((s == true ? "?." : ".") + f); } else {
case EBinop(op, e1, e2): tabs += "\t";
expr(e1); add("{\n");
add(" " + op + " "); for( e in el ) {
expr(e2); add(tabs);
case EUnop(op, pre, e): expr(e);
if( pre ) { add(";\n");
add(op); }
expr(e); tabs = tabs.substr(1);
} else {
expr(e);
add(op);
}
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(")");
}
add("(");
var first = true;
for( a in args ) {
if( first ) first = false else add(", ");
expr(a);
}
add(")");
case EIf(cond,e1,e2):
add("if( ");
expr(cond);
add(" ) ");
expr(e1);
if( e2 != null ) {
add(" else ");
expr(e2);
}
case EWhile(cond,e):
add("while( ");
expr(cond);
add(" ) ");
expr(e);
case EDoWhile(cond,e):
add("do ");
expr(e);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e):
add("for( "+v+" in ");
expr(it);
add(" ) ");
expr(e);
case EBreak:
add("break");
case EContinue:
add("continue");
case EFunction(params, e, name, ret): // TODO: static, public, override
add("function");
if( name != null )
add(" " + name);
add("(");
var first = true;
for( a in params ) {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(a.name);
addType(a.t);
}
add(")");
addType(ret);
add(" ");
expr(e);
case EReturn(e):
add("return");
if( e != null ) {
add(" ");
expr(e);
}
case EArray(e,index):
expr(e);
add("[");
expr(index);
add("]");
case EArrayDecl(el, _):
add("[");
var first = true;
for( e in el ) {
if( first ) first = false else add(", ");
expr(e);
}
add("]");
case ENew(cl, args):
add("new " + cl + "(");
var first = true;
for( e in args ) {
if( first ) first = false else add(", ");
expr(e);
}
add(")");
case EThrow(e):
add("throw ");
expr(e);
case ETry(e, v, t, ecatch):
add("try ");
expr(e);
add(" catch( " + v);
addType(t);
add(") ");
expr(ecatch);
case EObject(fl):
if( fl.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( f in fl ) {
add(tabs); add(tabs);
add(f.name+" : "); add("}");
expr(f.e);
add(",\n");
} }
tabs = tabs.substr(1); case EField(e, f, s):
add("}"); expr(e);
} add((s == true ? "?." : ".") + f);
case ETernary(c,e1,e2): case EBinop(op, e1, e2):
expr(c); /*var op1 = switch(Tools.expr(e1)) {
add(" ? "); case EBinop(op, _, _): op;
expr(e1); case EConst(_): "_";
add(" : "); case EIdent(_): "_";
expr(e2); default: null;
case ESwitch(e, cases, def): }
add("switch( "); var op2 = switch(Tools.expr(e2)) {
expr(e); case EBinop(op, _, _): op;
add(") {"); case EConst(_): "_";
for( c in cases ) { case EIdent(_): "_";
add("case "); default: null;
var first = true; }
for( v in c.values ) { var paran = Tools.checkOpPrecedence(op, op1, op2);*/
if( first ) first = false else add(", "); var paran = 2;
expr(v);
var op = getBinaryOp(op);
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):
var op = getUnaryOp(op);
if( pre ) {
add(op);
expr(e);
} else {
expr(e);
add(op);
}
case ECall(e, args):
if( e == null )
expr(e);
else switch( Tools.expr(e) ) {
case EField(_), EIdent(_), EConst(_):
expr(e);
default:
add("(");
expr(e);
add(")");
} }
add(": ");
expr(c.expr);
add(";\n");
}
if( def != null ) {
add("default: ");
expr(def);
add(";\n");
}
add("}");
case EMeta(name, args, e):
add("@");
add(name);
if( args != null && args.length > 0 ) {
add("("); add("(");
var first = true; var first = true;
for( a in args ) { for( a in args ) {
if( first ) first = false else add(", ");
expr(a);
}
add(")");
case EIf(cond,e1,e2):
add("if( ");
expr(cond);
add(" )");
var wasBlock = block(e1, true);
if( e2 != null ) {
if(!wasBlock) add("\n" + tabs);
add(" else ");
block(e2);
}
case EWhile(cond,e):
add("while( ");
expr(cond);
add(" )");
block(e, true);
case EDoWhile(cond,e):
add("do");
block(e, true);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e):
add("for( "+getVar(v)+" in ");
expr(it);
add(" )");
block(e, true);
case EForKeyValue(v, it, e, ithv):
add("for( "+getVar(ithv)+" => "+getVar(v)+" in ");
expr(it);
add(" )");
block(e, true);
case EBreak:
add("break");
case EContinue:
add("continue");
case EFunction(params, e, name, ret): // TODO: static, public, override
add("function");
if( name != null )
add(" " + getVar(name));
add("(");
var first = true;
for( a in params ) {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(getVar(a.name));
addType(a.t);
}
add(")");
addType(ret);
add(" ");
expr(e);
case EReturn(e):
add("return");
if( e != null ) {
add(" ");
expr(e);
}
case EArray(e,index):
expr(e);
add("[");
expr(index);
add("]");
case EMapDecl(type, keys, values):
add("[");
var first = true;
for( i in 0...keys.length ) {
if( first ) first = false else add(", ");
expr(keys[i]);
add(" => ");
expr(values[i]);
}
add("]");
case EArrayDecl(el):
add("[");
var first = true;
for( e in el ) {
if( first ) first = false else add(", ");
expr(e);
}
add("]");
case ENew(cl, args):
add("new " + getVar(cl) + "(");
var first = true;
for( e in args ) {
if( first ) first = false else add(", "); if( first ) first = false else add(", ");
expr(e); expr(e);
} }
add(")"); add(")");
} case EThrow(e):
add(" "); add("throw ");
expr(e); expr(e);
case ECheckType(e, t): case ETry(e, v, t, ecatch):
add("("); add("try");
expr(e); var wasBlock = block(e);
add(" : "); if( !wasBlock ) {
addType(t); add("\n" + tabs);
add(")"); } else add(" ");
add("catch( " + v);
addType(t);
add(" )");
block(ecatch);
case EObject(fl):
if( fl.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( i=>f in fl ) {
add(tabs);
var name = isJson(f.name) ? "\"" + f.name + "\"" : f.name;
add(name+" : ");
expr(f.e);
if( i != fl.length - 1 ) add(",");
add("\n");
}
tabs = tabs.substr(1);
add(tabs);
add("}");
}
case ETernary(c,e1,e2):
expr(c);
add(" ? ");
expr(e1);
add(" : ");
expr(e2);
case ESwitch(e, cases, def):
add("switch( ");
expr(e);
add(" ) {\n");
tabs += "\t";
for( c in cases ) {
add(tabs);
add("case ");
var first = true;
for( v in c.values ) {
if( first ) first = false else add(", ");
expr(v);
}
add(":");
block(c.expr, true);
add(";\n");
}
if( def != null ) {
add(tabs);
add("default: ");
block(def, true);
add(";\n");
}
tabs = tabs.substr(1);
add(tabs);
add("}");
case EMeta(name, args, e):
add("@");
add(name);
if( args != null && args.length > 0 ) {
add("(");
var first = true;
for( a in args ) {
if( first ) first = false else add(", ");
expr(e);
}
add(")");
}
add(" ");
expr(e);
case ECheckType(e, t):
add("(");
expr(e);
add(" : ");
addType(t);
add(")");
#if !HSCRIPT_NO_INT_VARS
case EInfo(_, e):
expr(e);
#end
} }
} }
public static function toString( e : Expr ) { public inline static function toString( e : Expr ) {
return new Printer().exprToString(e); return new Printer().exprToString(e);
} }
public static function errorToString( e : Expr.Error ) { public inline static function compareErrors( e1 : Error, e2 : Error ) {
var message = switch( #if hscriptPos e.e #else e #end ) { return Type.enumEq(Tools.cleanError(e1), Tools.cleanError(e2));
}
public inline static function getPrintableError( e : Error ) {
return Tools.cleanError(switch( Tools.cleanError(e) ) {
case EPreset(msg): Parser.getBaseError(ECustom(msg.toString()));
default: e;
});
}
public static function errorToString( e : Error ) {
var message = switch( Tools.cleanError(e) ) {
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")"; case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
case EUnexpected(s): "Unexpected token: \""+s+"\""; case EUnexpected(s): "Unexpected token: \""+s+"\"";
case EUnterminatedString: "Unterminated string"; case EUnterminatedString: "Unterminated string";
case EUnterminatedComment: "Unterminated comment"; case EUnterminatedComment: "Unterminated comment";
case EUnterminatedRegex: "Unterminated regular expression";
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")"; case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
case EUnknownVariable(v): "Unknown variable: "+v; case EUnknownVariable(v): "Unknown variable: "+v;
case EInvalidIterator(v): "Invalid iterator: "+v; case EInvalidIterator(v): "Invalid iterator: "+v;
case EInvalidType(t): "Invalid type: "+t;
case EInvalidOp(op): "Invalid operator: "+op; case EInvalidOp(op): "Invalid operator: "+op;
case EInvalidAccess(f, on) if (on != null): "Invalid access to field " + f + " on " + on;
case EInvalidAccess(f): "Invalid access to field " + f; case EInvalidAccess(f): "Invalid access to field " + f;
case ECustom(msg): msg; case ECustom(msg): msg;
case EPreset(msg): msg.toString();
case EInvalidClass(cla): "Invalid class: " + cla + " was not found."; case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.'; case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
case EInvalidEscape(s): "Invalid escape sequence: " + s;
}; };
#if hscriptPos #if hscriptPos
return e.origin + ":" + e.line + ": " + message; return e.origin + ":" + e.line + ": " + message;
+269 -61
View File
@@ -21,76 +21,131 @@
*/ */
package hscript; package hscript;
import hscript.Expr; import hscript.Expr;
import hscript.utils.UnsafeReflect;
class Tools { class Tools {
public static function iter( e : Expr, f : Expr -> Void ) { public static function iter( e : Expr, f : Expr -> Void ) {
switch( expr(e) ) { switch( expr(e) ) {
case EConst(_), EIdent(_): case EConst(_), EIdent(_): f(e);
case EImport(c): f(e); case EImport(c, _): f(e);
case EClass(_, e, _, _): for( a in e ) f(a); case EClass(_, e, _, _): for( a in e ) f(a);
case EVar(_, _, e): if( e != null ) f(e); case EVar(_, _, e): if( e != null ) f(e);
case EParent(e): f(e); case EParent(e, _): f(e);
case EBlock(el): for( e in el ) f(e); case EBlock(el): for( e in el ) f(e);
case EField(e, _): f(e); case EField(e, _): f(e);
case EBinop(_, e1, e2): f(e1); f(e2); case EBinop(_, e1, e2): f(e1); f(e2);
case EUnop(_, _, e): f(e); case EUnop(_, _, e): f(e);
case ECall(e, args): f(e); for( a in args ) f(a); case ECall(e, args): f(e); for( a in args ) f(a);
case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2); case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2);
case EWhile(c, e): f(c); f(e); case EWhile(c, e): f(c); f(e);
case EDoWhile(c, e): f(c); f(e); case EDoWhile(c, e): f(c); f(e);
case EFor(_, it, e): f(it); f(e); case EFor(_, it, e): f(it); f(e);
case EBreak,EContinue: case EForKeyValue(_, it, e, _): f(it); f(e);
case EFunction(_, e, _, _): f(e); case EBreak,EContinue:
case EReturn(e): if( e != null ) f(e); case EFunction(_, e, _, _): f(e);
case EArray(e, i): f(e); f(i); case EReturn(e): if( e != null ) f(e);
case EArrayDecl(el): for( e in el ) f(e); case EArray(e, i): f(e); f(i);
case ENew(_,el): for( e in el ) f(e); case EMapDecl(type, keys, values): for( e in keys ) f(e); for( e in values ) f(e);
case EThrow(e): f(e); case EArrayDecl(el): for( e in el ) f(e);
case ETry(e, _, _, c): f(e); f(c); case ENew(_,el): for( e in el ) f(e);
case EObject(fl): for( fi in fl ) f(fi.e); case EThrow(e): f(e);
case ETernary(c, e1, e2): f(c); f(e1); f(e2); case ETry(e, _, _, c): f(e); f(c);
case ESwitch(e, cases, def): case EObject(fl): for( fi in fl ) f(fi.e);
f(e); case ETernary(c, e1, e2): f(c); f(e1); f(e2);
for( c in cases ) { case ESwitch(e, cases, def):
for( v in c.values ) f(v); f(e);
f(c.expr); for( c in cases ) {
} for( v in c.values ) f(v);
if( def != null ) f(def); f(c.expr);
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e); }
case ECheckType(e,_): f(e); if( def != null ) f(def);
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
case ECheckType(e,_): f(e);
#if !HSCRIPT_NO_INT_VARS
case EInfo(info, e): f(e);
#end
}
}
public static function iterExprRecursive( ve : Expr, f : Expr -> Void ) {
var rec = (e:Expr) -> iterExprRecursive(e, f);
f(ve);
switch( expr(ve) ) {
case EConst(_), EIdent(_): {}
case EImport(c, _): {}
case EClass(_, e, _, _): for( a in e ) rec(a);
case EVar(_, _, e): if( e != null ) rec(e);
case EParent(e, _): rec(e);
case EBlock(el): for( e in el ) rec(e);
case EField(e, _): rec(e);
case EBinop(_, e1, e2): rec(e1); rec(e2);
case EUnop(_, _, e): rec(e);
case ECall(e, args): rec(e); for( a in args ) rec(a);
case EIf(c, e1, e2): rec(c); rec(e1); if( e2 != null ) rec(e2);
case EWhile(c, e): rec(c); rec(e);
case EDoWhile(c, e): rec(c); rec(e);
case EFor(_, it, e): rec(it); rec(e);
case EForKeyValue(_, it, e, _): rec(it); rec(e);
case EBreak,EContinue: {}
case EFunction(_, e, _, _): rec(e);
case EReturn(e): if( e != null ) rec(e);
case EArray(e, i): rec(e); rec(i);
case EMapDecl(type, keys, values): for( e in keys ) rec(e); for( e in values ) rec(e);
case EArrayDecl(el): for( e in el ) rec(e);
case ENew(_,el): for( e in el ) rec(e);
case EThrow(e): rec(e);
case ETry(e, _, _, c): rec(e); rec(c);
case EObject(fl): for( fi in fl ) rec(fi.e);
case ETernary(c, e1, e2): rec(c); rec(e1); rec(e2);
case ESwitch(e, cases, def):
rec(e);
for( c in cases ) {
for( v in c.values ) rec(v);
rec(c.expr);
}
if( def != null ) rec(def);
case EMeta(name, args, e): if( args != null ) for( a in args ) rec(a); rec(e);
case ECheckType(e,_): rec(e);
#if !HSCRIPT_NO_INT_VARS
case EInfo(info, e): rec(e);
#end
} }
} }
public static function map( e : Expr, f : Expr -> Expr ) { public static function map( e : Expr, f : Expr -> Expr ) {
var edef = switch( expr(e) ) { var edef = switch( expr(e) ) {
case EConst(_), EIdent(_), EBreak, EContinue: expr(e); case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null); case EVar(n, t, e, p, s): EVar(n, t, if( e != null ) f(e) else null, p, s);
case EParent(e): EParent(f(e)); case EParent(e, no): EParent(f(e), no);
case EBlock(el): EBlock([for( e in el ) f(e)]); case EBlock(el): EBlock([for( e in el ) f(e)]);
case EField(e, fi): EField(f(e),fi); case EField(e, fi, s): EField(f(e),fi,s);
case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2)); case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
case EUnop(op, pre, e): EUnop(op, pre, f(e)); case EUnop(op, pre, e): EUnop(op, pre, f(e));
case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]); case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]);
case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null); case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null);
case EWhile(c, e): EWhile(f(c),f(e)); case EWhile(c, e): EWhile(f(c),f(e));
case EDoWhile(c, e): EDoWhile(f(c),f(e)); case EDoWhile(c, e): EDoWhile(f(c),f(e));
case EFor(v, it, e): EFor(v, f(it), f(e)); case EFor(v, it, e): EFor(v, f(it), f(e));
case 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 EReturn(e): EReturn(if( e != null ) f(e) else null); case EFunction(args, e, name, t, p, s, o): EFunction(args, f(e), name, t, p, s, o);
case EArray(e, i): EArray(f(e),f(i)); case EReturn(e): EReturn(if( e != null ) f(e) else null);
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]); case EArray(e, i): EArray(f(e),f(i));
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]); case EMapDecl(type, keys, values): EMapDecl(type, [for( e in keys ) f(e)], [for( e in values ) f(e)]);
case EThrow(e): EThrow(f(e)); case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
case ETry(e, v, t, c): ETry(f(e), v, t, f(c)); case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]); case EThrow(e): EThrow(f(e));
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2)); case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
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 EObject(fl): EObject([for( fi in fl ) new ObjectField(fi.name, f(fi.e))]);
case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e)); case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
case ECheckType(e,t): ECheckType(f(e), t); 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 EImport(c): EImport(c); case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces); case ECheckType(e,t): ECheckType(f(e), t);
case EImport(c, m): EImport(c, m);
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
#if !HSCRIPT_NO_INT_VARS
case EInfo(info, e): EInfo(info, f(e));
#end
} }
return mk(edef, e); return mk(edef, e);
} }
@@ -103,12 +158,165 @@ class Tools {
#end #end
} }
public static inline function mk( e : ExprDef, p : Expr ) { public static inline function cleanError( e : Error ) {
#if hscriptPos #if hscriptPos
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line }; return e.e;
#else #else
return e; return e;
#end #end
} }
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;
}
}
public static function getOpEnum(op:String):Binop {
return switch(op) {
case "+": OpAdd;
case "-": OpSub;
case "*": OpMult;
case "/": OpDiv;
case "%": OpMod;
case "&": OpAnd;
case "|": OpOr;
case "^": OpXor;
case "<<": OpShl;
case ">>": OpShr;
case ">>>": OpUShr;
case "==": OpEq;
case "!=": OpNotEq;
case ">=": OpGte;
case "<=": OpLte;
case ">": OpGt;
case "<": OpLt;
case "||": OpBoolOr;
case "&&": OpBoolAnd;
case "is": OpIs;
case "=": OpAssign;
case "=>": OpArrow;
case "??": OpNullCoal;
case "...": OpInterval;
default: {
var op2 = op.substr(0, op.length - 1);
if(isValidBinOp(op2)) {
return OpAssignOp(getOpEnum(op2));
}
throw "Unknown binary operator: " + op;
}
}
}
public static function getUnopEnum(op:String):Unop {
return switch(op) {
case "++": OpIncrement;
case "--": OpDecrement;
case "!": OpNot;
case "-": OpNeg;
case "~": OpNegBits;
case "...": OpSpread;
default: throw "Unknown unary operator: " + op;
}
}
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;
}
} }
+518
View File
@@ -0,0 +1,518 @@
package hscript.macros;
#if macro
import haxe.macro.Type.ClassType;
import Type.ValueType;
import haxe.macro.Expr.Function;
import haxe.macro.Expr;
import haxe.macro.Type.MetaAccess;
import haxe.macro.Type.FieldKind;
import haxe.macro.Type.ClassField;
import haxe.macro.Type.VarAccess;
import haxe.macro.*;
import Sys;
using StringTools;
class ClassExtendMacro {
public static inline final FUNC_PREFIX = "_HX_SUPER__";
public static inline final CLASS_SUFFIX = "_HSX";
public static var unallowedMetas:Array<String> = [":bitmap", ":noCustomClass", ":generic"];
public static var modifiedClasses:Array<String> = [];
public static function init() {
#if !display
#if CUSTOM_CLASSES
if(Context.defined("display")) return;
for(apply in Config.ALLOWED_CUSTOM_CLASSES) {
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.ClassExtendMacro.build())");
}
#end
#end
}
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.isAbstract || cl.isExtern || cl.isFinal || cl.isInterface) return fields;
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith(CLASS_SUFFIX) && !cl.name.endsWith("_HSC")) {
var metas = cl.meta.get();
for(m in metas)
if (unallowedMetas.contains(m.name))
return fields;
if(cl.params.length > 0)
return fields;
var key = cl.module;
var fkey = cl.module + "." + cl.name;
if(key == "sys.thread.FixedThreadPool") return fields; // Error: Type name sys.thread.Worker_HSX is redefined from module sys.thread.FixedThreadPool
if(key == "StdTypes") return fields; // Error: Cant extend basic class
if(key == "Xml") return fields; // Error: Cant extend basic class
if(key == "Date") return fields; // Error: Cant extend basic class
if(key == "away3d.tools.commands.Mirror") return fields; // Error: Unknown identifier
if(key == "away3d.tools.commands.SphereMaker") return fields; // Error: Unknown identifier
if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier
if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined
if(key == "sys.thread.EventLoop") return fields; // Error: cant override force inlined
if(Config.DISALLOW_CUSTOM_CLASSES.contains(cl.module) || Config.DISALLOW_CUSTOM_CLASSES.contains(fkey)) return fields;
if(cl.module.contains("_")) return fields; // Weird issue, sorry
var superFields = [];
if(cl.superClass != null) {
var _superFields = cl.superClass.t.get().fields.get();
_superFields = []; // Comment to enable super support, (broken)
for(field in _superFields) {
if(!field.kind.match(FMethod(_))) // only catch methods
continue;
try {
var nfield = @:privateAccess TypeTools.toField(field);
switch ([field.kind, field.type]) {
case [FMethod(kind), TFun(args, ret)]:
if(kind == MethInline)
nfield.access.push(AInline);
if(kind == MethDynamic)
nfield.access.push(ADynamic);
default:
}
switch(nfield.kind) {
case FFun(fun):
if (fun.params != null && fun.params.length > 0)
continue;
fun.ret = Utils.fixStdTypes(fun.ret);
var metas = nfield.meta;
var defaultValues:Map<String, Dynamic> = [];
var defaultEntry = null;
var isGeneric = false;
for(m in metas) {
if(m.name == ":value") {
defaultEntry = m;
switch(m.params[0].expr) {
case EObjectDecl(fields):
for(fil in fields)
defaultValues[fil.field] = fil.expr;
default:
}
}
if(m.name == ":generic")
isGeneric = true;
}
if(isGeneric) continue;
if(defaultEntry != null)
metas.remove(defaultEntry);
for(arg in fun.args) {
var opt = false;
if(defaultValues.exists(arg.name)) {
arg.value = defaultValues[arg.name];
arg.opt = false;
}
arg.type = Utils.fixStdTypes(arg.type);
if(arg.opt) {
if(arg.type.getParameters()[0].name != "Null")
arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null<Dynamic>};
}
}
default:
}
superFields.push(nfield);
} catch(e) {
}
}
//superFields = [];
}
var shadowClass = macro class {
};
var definedFields:Array<String> = [];
//trace(getModuleName(cl));
var hasNew = false;
for(_field in [fields.copy(), superFields.copy()])
for(f in _field) {
if (f == null)
continue;
if (f.name == "new") {
hasNew = true;
continue;
}
if (f.name.startsWith(FUNC_PREFIX))
continue;
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline))
continue;
if(f.name == "hget" || f.name == "hset") continue; // sorry, no overwriting the hget and hset in custom classes, yet
if(definedFields.contains(f.name)) continue; // no duplicate fields
for(m in f.meta)
if (unallowedMetas.contains(m.name))
continue;
switch(f.kind) {
case FFun(fun):
if (fun == null)
continue;
if (fun.params != null && fun.params.length > 0) // TODO: Support for this maybe?
continue;
if(fun.params == null)
fun.params = [];
var overrideExpr:Expr;
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
var name = f.name;
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
if (returns) {
overrideExpr = macro {
var name:String = $v{name};
if (__custom__variables != null) {
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});
};
} else {
overrideExpr = macro {
var name:String = $v{name};
if (__custom__variables != null) {
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});
};
}
var superFuncExpr:Expr = returns ? {
macro return super.$name($a{arguments});
} : {
macro super.$name($a{arguments});
};
var func:Function = {
ret: fun.ret,
params: fun.params.copy(),
expr: overrideExpr,
args: fun.args.copy()
};
var overrideField:Field = {
name: f.name,
access: f.access.copy(),
kind: FFun(func),
pos: Context.currentPos(),
doc: f.doc,
meta: f.meta.copy()
};
if (!overrideField.access.contains(AOverride))
overrideField.access.push(AOverride);
var superField:Field = {
name: '$FUNC_PREFIX${f.name}',
pos: Context.currentPos(),
kind: FFun({
ret: fun.ret,
params: fun.params.copy(),
expr: superFuncExpr,
args: fun.args.copy()
}),
access: f.access.copy()
};
if (superField.access.contains(AOverride))
superField.access.remove(AOverride);
shadowClass.fields.push(overrideField);
shadowClass.fields.push(superField);
definedFields.push(f.name);
default:
// fuck off >:(
}
}
var totalFields = definedFields.length;
if(totalFields == 0 && !hasNew) {
//Sys.println(cl.pack.join(".") + "." + cl.name + ", " + totalFields);
return fields;
}
shadowClass.kind = TDClass({
pack: cl.pack.copy(),
name: cl.name
}, [
{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
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
Context.defineModule(cl.module, [shadowClass], imports);
return fields;
}
var hasHgetInSuper = false;
var hasHsetInSuper = false;
if(cl.name == "CustomShader") {
hasHgetInSuper = hasHsetInSuper = true;
}
// TODO: somehow check the super super class
for(_field in [fields.copy(), superFields.copy()])
for(f in _field) {
if (f.name == "new")
continue;
if (f.name.startsWith(FUNC_PREFIX))
continue;
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern))
continue;
switch(f.kind) {
case FFun(fun):
if (fun.params != null && fun.params.length > 0)
continue;
if(!hasHgetInSuper)
hasHgetInSuper = f.name == "hget";
if(!hasHsetInSuper)
hasHsetInSuper = f.name == "hset";
if(hasHgetInSuper && hasHsetInSuper)
break;
default:
}
}
var hgetField = if(hasHgetInSuper) {
macro {
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(__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(__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(__allowSetGet && __custom__variables.exists("set_" + name))
return __callSetter(name, val);
if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
return val;
}
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
}
//if(hasHsetInSuper || hasHgetInSuper) return fields;
//trace(cl.name);
shadowClass.fields.push({
name: "hset",
pos: Context.currentPos(),
access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
kind: FFun({
ret: TPath({name: 'Dynamic', pack: []}),
params: [],
expr: hsetField,
args: [
{
name: "name",
opt: false,
meta: [],
type: TPath({name: "String", pack: []})
},
{
name: "val",
opt: false,
meta: [],
type: TPath({name: "Dynamic", pack: []})
}
]
})
});
shadowClass.fields.push({
name: "hget",
pos: Context.currentPos(),
access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
kind: FFun({
ret: TPath({name: 'Dynamic', pack: []}),
params: [],
expr: hgetField,
args: [
{
name: "name",
opt: false,
meta: [],
type: TPath({name: "String", pack: []})
}
]
})
});
/*var p = new Printer();
var aa = p.printTypeDefinition(shadowClass);
if(aa.length < 5024)
trace(aa);
if(aa.indexOf("pack") >= 0)
if(cl.name == "FunkinShader")*/
Context.defineModule(cl.module, [shadowClass], imports);
}
return fields;
}
}
#else
class ClassExtendMacro {
public var usedClass:Class<Dynamic>;
public var className:String;
}
#end
+160
View File
@@ -0,0 +1,160 @@
package hscript.macros;
#if macro
import Type.ValueType;
import haxe.macro.ComplexTypeTools;
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Printer;
import haxe.macro.Compiler;
using StringTools;
class UsingHandler {
public static function init() {
#if !display
if(Context.defined("display")) return;
for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) {
Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.UsingHandler.build())');
}
#end
}
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.name.startsWith("Flx") && */ cl.name.endsWith("_Impl_") && cl.params.length <= 0 && !cl.meta.has(":multiType") && !cl.name.contains("_HSC")) {
var metas = cl.meta.get();
var trimEnum = cl.name.substr(0, cl.name.length - 6);
var key = cl.module;
var fkey = cl.module + "." + trimEnum;
if(key == "lime.system.Locale") return fields; // Error: Unknown identifier : currentLocale, Due to Func
if(key == "cpp.Function") return fields; // Error: Unknown identifier : nativeGetProcAddress, Due to Func
if(key == "haxe.ds.Vector") return fields; // Error: haxe.ds._Vector.VectorData<blit.T> has no field blit, Due to Func
if(key == "haxe.display.Display") return fields; // Error: haxe.display.DisplayItemKind<haxe.display.DisplayLiteral<Dynamic>> has no field Null, Due to Func
if(key == "cpp.Callable") return fields; // Error: cpp.Function.fromStaticFunction must be called on static function, Due to Func
if(key == "haxe.display.JsonAnonStatusKind") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
if(key == "cpp.CharStar") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
if(Config.DISALLOW_ABSTRACT_AND_ENUM.contains(cl.module) || Config.DISALLOW_ABSTRACT_AND_ENUM.contains(fkey)) return fields;
if(cl.module.contains("_")) return fields; // Weird issue, sorry
var shadowClass = macro class {
};
shadowClass.kind = TDClass();
shadowClass.params = switch(cl.params.length) {
case 0:
null;
case 1:
[{
name: "T",
}];
default:
[for(k=>e in cl.params) {
name: "T" + Std.int(k+1)
}];
};
shadowClass.name = '${cl.name.substr(0, cl.name.length - 6)}_HSC';
var imports = Context.getLocalImports().copy();
Utils.setupMetas(shadowClass, imports);
//trace(cl.module);
for(f in fields)
switch(f.kind) {
case FFun(fun):
if (f.access.contains(AStatic)) {
if (fun.expr != null) {
fun.expr = macro @:privateAccess $e{fun.expr};
shadowClass.fields.push(f);
/*var trimEnum = cl.name.substr(0, cl.name.length - 6);
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
var name = f.name;
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
var expr:Expr = Context.parse('${returns?"return " : ""} $trimEnum.$name(${[for(a in fun.args) a.name].join(", ")})', f.pos);
var func:Function = {
ret: fun.ret,
params: fun.params.copy(),
expr: expr,
args: fun.args.copy()
};
var field:Field = {
pos: f.pos,
name: f.name,
meta: f.meta,
kind: FFun(func),
doc: null,//f.doc,
access: [APublic, AStatic]
}
shadowClass.fields.push(field);*/
}
}
case FProp(get, set, t, e):
if (get == "default" && (set == "never" || set == "null")) {
shadowClass.fields.push(f);
}
case FVar(t, e):
if (f.access.contains(AStatic) || cl.meta.has(":enum") || f.name.toUpperCase() == f.name) {
var name:String = f.name;
var enumType:String = cl.name;
var pack = cl.module.split(".");
//trace(pack, cl.name, name, cl.module);
if(pack[pack.length - 1] == trimEnum)
pack.pop();
var complexType:ComplexType = t;
if(complexType == null && e != null) {
complexType = switch(e.expr) {
case EConst(CRegexp(_)): TPath({ name: "EReg", pack: [] });
default: null;
}
}
if(complexType == null) {
complexType = TPath({
name: trimEnum,
pack: [],//pack
});
}
var code = Context.parse('@:privateAccess ($trimEnum.$name)', f.pos); // '${pack.join(".")}.${trimEnum}.$name'
var field:Field = {
pos: f.pos,
name: f.name,
meta: f.meta,
kind: FVar(null, code),
doc: f.doc,
access: [APublic, AStatic]
}
shadowClass.fields.push(field);
}
default:
}
//var printer = new Printer();
//for(field in shadowClass.fields)
// trace(printer.printField(field));
Context.defineModule(cl.module, [shadowClass], imports);
}
return fields;
}
}
#end
@@ -1,107 +1,20 @@
package hscript; package hscript.macros;
import Type.ValueType;
import haxe.macro.ComplexTypeTools;
#if macro #if macro
import haxe.macro.Type.ClassType;
import Type.ValueType;
import haxe.macro.Expr.Function;
import haxe.macro.Expr; import haxe.macro.Expr;
import haxe.macro.Context; import haxe.macro.Type.MetaAccess;
import haxe.macro.Printer; import haxe.macro.Type.FieldKind;
import haxe.macro.Compiler; import haxe.macro.Type.ClassField;
import haxe.macro.Type.VarAccess;
import haxe.macro.*;
using StringTools; using StringTools;
class UsingHandler { class Utils {
public var usedClass:Class<Dynamic>; public static function fixStdTypes(type:ComplexType) {
public var className:String;
public function new(className:String, usedClass:Class<Dynamic>) {
this.className = className;
this.usedClass = usedClass;
}
public static function init() {
Compiler.addGlobalMetadata('flixel', '@:build(hscript.UsingHandler.build())');
Compiler.addGlobalMetadata('openfl.display.BlendMode', '@:build(hscript.UsingHandler.build())');
}
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.name.startsWith("Flx") && */ cl.name.endsWith("_Impl_") && cl.params.length <= 0 && !cl.meta.has(":multiType")) {
var metas = cl.meta.get();
var shadowClass = macro class {
};
shadowClass.kind = TDClass();
shadowClass.params = switch(cl.params.length) {
case 0:
null;
case 1:
[
{
name: "T",
}
];
default:
[for(k=>e in cl.params) {
name: "T" + Std.int(k+1)
}];
};
shadowClass.name = '${cl.name.substr(0, cl.name.length - 6)}_HSC';
var imports = Context.getLocalImports().copy();
setupMetas(shadowClass, imports);
for(f in fields)
switch(f.kind) {
case FFun(fun):
if (f.access.contains(AStatic)) {
if (fun.expr != null)
shadowClass.fields.push(f);
}
case FProp(get, set, t, e):
if (get == "default" && (set == "never" || set == "null"))
shadowClass.fields.push(f);
case FVar(t, e):
if (f.access.contains(AStatic) || cl.meta.has(":enum") || f.name.toUpperCase() == f.name) {
var name:String = f.name;
var enumType:String = cl.name;
var pack = cl.module.split(".");
pack.pop();
var complexType:ComplexType = t != null ? t : (name.contains("REGEX") ? TPath({
name: "EReg",
pack: []
}) : TPath({
name: cl.name.substr(0, cl.name.length - 6),
pack: pack}));
var field:Field = {
pos: f.pos,
name: f.name,
meta: f.meta,
kind: FVar(complexType, {
pos: Context.currentPos(),
expr: ECast(e, complexType)
}),
doc: f.doc,
access: [APublic, AStatic]
}
shadowClass.fields.push(field);
}
default:
}
Context.defineModule(cl.module + "_HSC", [shadowClass], imports);
}
return fields;
}
static function fixStdTypes(type:ComplexType) {
switch(type) { switch(type) {
case TPath({name: "StdTypes"}): case TPath({name: "StdTypes"}):
var a:TypePath = type.getParameters()[0]; var a:TypePath = type.getParameters()[0];
@@ -113,16 +26,9 @@ class UsingHandler {
} }
public static function setupMetas(shadowClass:TypeDefinition, imports) { public static function setupMetas(shadowClass:TypeDefinition, imports) {
shadowClass.meta = [{ shadowClass.meta = [];
name: ':dox', shadowClass.meta.push({name: ":dox", params: [macro hide], pos: Context.currentPos()});
pos: Context.currentPos(), shadowClass.meta.push({name: ":noCompletion", params: [], pos: Context.currentPos()});
params: [
{
expr: EConst(CIdent("hide")),
pos: Context.currentPos()
}
]
}];
var module = Context.getModule(Context.getLocalModule()); var module = Context.getModule(Context.getLocalModule());
for(t in module) { for(t in module) {
switch(t) { switch(t) {
@@ -300,9 +206,4 @@ class UsingHandler {
} }
} }
} }
#else
class UsingHandler {
public var usedClass:Class<Dynamic>;
public var className:String;
}
#end #end
+184
View File
@@ -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
}
}
+1
View File
@@ -0,0 +1 @@
bin/*
+9
View File
@@ -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
+25
View File
@@ -0,0 +1,25 @@
# Generic Haxe stuffs to make the project compile
-cp src
-cp ../
-D analyzer-optimize
-main Main
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
# --library hxcpp-debug-server
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
--define hscriptPos
--define HXCPP_CHECK_POINTER
--define HXCPP_STACK_LINE
--define HXCPP_DEBUG_LINK
--dce no
# File Specific Defines
--define linux
# Copying the assets from source code to bin folder
# --cmd rm -rf bin/cpp/assets
# --cmd cp -r assets bin/cpp/assets
--cpp bin/cpp
--cmd cd bin/cpp
--cmd ./Main
+34
View File
@@ -0,0 +1,34 @@
# Generic Haxe stuffs to make the project compile
-cp src
-cp ../
-D analyzer-optimize
-main Main
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
# --library hscript-improved
# --library hxcpp-debug-server
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
--define hscriptPos
--define HXCPP_CHECK_POINTER
--define HXCPP_STACK_LINE
--define HXCPP_DEBUG_LINK
# -D HXCPP_M64
# -D HXCPP_ARM64
--dce no
# File Specific Defines
--define macos
--define mac
--macro keep('IntIterator')
# --macro hscript.macros.UsingHandler.init()
# --macro hscript.macros.ClassExtendMacro.init()
# Copying the assets from source code to bin folder
# --cmd rm -rf bin/cpp/assets
# --cmd cp -r assets bin/cpp/assets
--cpp bin/cpp
--cmd cd bin/cpp
--cmd ./Main
+74
View File
@@ -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);
}
}
+84
View File
@@ -0,0 +1,84 @@
#if BENCHMARK
import benchmark.*;
#else
import tests.*;
#end
class Main {
public static var SHOW_KNOWN_BUGS:Bool = false;
static function main() {
#if BENCHMARK
Sys.println("Running benchmark");
//new Benchmark("Test", 1000);
var haxeTotalTime:Float = 0;
var hscriptTotalTime:Float = 0;
var haxeTimes:Array<Float> = [];
var hscriptTimes:Array<Float> = [];
var iterations:Int = 10;
for (i in 0...iterations) {
var benchmark:TestBenchmark = new TestBenchmark();
haxeTimes.push(benchmark.haxeTotalTime);
hscriptTimes.push(benchmark.hscriptTotalTime);
haxeTotalTime += benchmark.haxeTotalTime;
hscriptTotalTime += benchmark.hscriptTotalTime;
}
haxeTimes.sort((a, b) -> return a > b ? 1 : -1);
hscriptTimes.sort((a, b) -> return a > b ? 1 : -1);
var slowestHaxeTime:Float = haxeTimes[haxeTimes.length-1];
var slowestHscriptTime:Float = hscriptTimes[hscriptTimes.length-1];
var fastestHaxeTime:Float = haxeTimes[0];
var fastestHscriptTime:Float = hscriptTimes[0];
var haxeWon = hscriptTotalTime > haxeTotalTime;
Sys.println('${haxeWon ? "Haxe" : "Hscript"} was faster overall (Faster by: ${Util.roundWith((haxeWon ? hscriptTotalTime/haxeTotalTime : haxeTotalTime/hscriptTotalTime), 100)}x)');
Sys.println('Haxe average time: ${Util.convertToReadableTime(haxeTotalTime/iterations)} (Highest: ${Util.convertToReadableTime(slowestHaxeTime)}) (Lowest: ${Util.convertToReadableTime(fastestHaxeTime)})');
Sys.println('Hscript average time: ${Util.convertToReadableTime(hscriptTotalTime/iterations)} (Highest: ${Util.convertToReadableTime(slowestHscriptTime)}) (Lowest: ${Util.convertToReadableTime(fastestHscriptTime)})');
#else
Sys.println("Beginning tests");
if(!Main.SHOW_KNOWN_BUGS) {
Sys.println("Hiding known bugs [TEMPORARY]");
}
runTest("Array", new ArrayCase());
runTest("BinOp", new BinOpCase());
runTest("Enum", new EnumCase());
runTest("EvalOrder", new EvalOrderCase());
runTest("Error", new ErrorCase());
runTest("Float", new FloatCase());
runTest("IntIterator", new IntIteratorCase());
runTest("Lambda", new LambdaCase());
runTest("List", new ListCase());
runTest("Math", new MathCase());
runTest("Map", new MapCase());
runTest("Misc", new MiscCase());
runTest("Reflect", new ReflectCase());
runTest("Regex", new RegexCase());
runTest("Std", new StdCase());
runTest("String", new StringCase());
runTest("StringBuf", new StringBufCase());
runTest("StringTools", new StringToolsCase());
runTest("SwitchStatement", new SwitchCase());
// TODO: UnicodeCase.hx?
// TODO: UnicodeStringCase.hx?
runTest("Final", new FinalCase());
Util.printTestResults();
#end
}
#if !BENCHMARK
static function runTest(name:String, test:TestCase) {
Util.startUnitTest(name);
test.setup();
test.run();
test.teardown();
Util.endUnitTest();
}
#end
}
+330
View File
@@ -0,0 +1,330 @@
import hscript.Expr.Error;
import hscript.Printer;
import haxe.Constraints.IMap;
import haxe.EnumTools.EnumValueTools;
using StringTools;
class Util {
static inline function isNull(a:Dynamic):Bool {
return Type.enumEq(Type.typeof(a), TNull);
}
static inline function isFunction(a:Dynamic):Bool {
return Type.enumEq(Type.typeof(a), TFunction);
}
// TODO: check this for bugs
// Code from https://github.com/elnabo/equals/blob/master/src/equals/Equal.hx, (MIT License), but updated to work with haxe 4
public static function deepEqual<T> (a:T, b:T) : Bool {
if (a == b) { return true; } // if physical equality
if (isNull(a) || isNull(b)) {
return false;
}
switch (Type.typeof(a)) {
case TNull, TInt, TBool, TUnknown:
return a == b;
case TFloat:
return Math.isNaN(cast a) && Math.isNaN(cast b); // only valid true result remaining
case TFunction:
return Reflect.compareMethods(a, b); // only physical equality can be tested for function
case TEnum(_):
if (EnumValueTools.getIndex(cast a) != EnumValueTools.getIndex(cast b)) {
return false;
}
var a_args = EnumValueTools.getParameters(cast a);
var b_args = EnumValueTools.getParameters(cast b);
return deepEqual(a_args, b_args);
case TClass(_):
if ((a is String) && (b is String)) {
return a == b;
}
if ((a is Array) && (b is Array)) {
var a = cast(a, Array<Dynamic>);
var b = cast(b, Array<Dynamic>);
if (a.length != b.length) { return false; }
for (i in 0...a.length) {
if (!deepEqual(a[i], b[i])) {
return false;
}
}
return true;
}
if ((a is IMap) && (b is IMap)) {
var a = cast(a, IMap<Dynamic, Dynamic>);
var b = cast(b, IMap<Dynamic, Dynamic>);
var a_keys = [ for (key in a.keys()) key ];
var b_keys = [ for (key in b.keys()) key ];
a_keys.sort(Reflect.compare);
b_keys.sort(Reflect.compare);
if (!deepEqual(a_keys, b_keys)) { return false; }
for (key in a_keys) {
if (!deepEqual(a.get(key), b.get(key))) {
return false;
}
}
return true;
}
if ((a is Date) && (b is Date)) {
return cast(a, Date).getTime() == cast(b, Date).getTime();
}
if ((a is haxe.io.Bytes) && (b is haxe.io.Bytes)) {
return deepEqual(cast(a, haxe.io.Bytes).getData(), cast(b, haxe.io.Bytes).getData());
}
case TObject:
}
for (field in Reflect.fields(a)) {
var pa = Reflect.field(a, field);
var pb = Reflect.field(b, field);
if (isFunction(pa)) {
// ignore function as only physical equality can be tested, unless null
if (isNull(pa) != isNull(pb)) {
return false;
}
continue;
}
if (!deepEqual(pa, pb)) {
return false;
}
}
return true;
}
public static inline function passed() {
passedTestUnits++;
return true;
}
public static inline function failed() {
failedTestUnits++;
return false;
}
public static function assert(value:Bool, message:String, ?pos:haxe.PosInfos) {
if (value) {
return passed();
} else {
Sys.println("Assertion failed: " + message);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
public static function assertEq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
var equals = deepEqual(value, expected);
var _passed = equals;
if (_passed) {
return passed();
} else {
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
public static function assertEqPrintable(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
var equals = deepEqual(value, expected);
var _passed = equals;
if (_passed) {
return passed();
} else {
Sys.println("Assertion failed: " + message + " Expected: " + Printer.getEscapedString(expected) + " Got: " + Printer.getEscapedString(value));
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
public static function assertNeq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
var equals = deepEqual(value, expected);
var _passed = !equals;
// WARNING THIS MIGHT NOT WORK
if (_passed) {
return passed();
} else {
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
return failed();
}
}
static var currentTestPath:Array<String> = [];
static var totalTestUnits:Int = 0;
static var passedTestUnits:Int = 0;
static var failedTestUnits:Int = 0;
static var skippedKnownBugs:Int = 0;
public static function runKnownBug(name:String, func:Void->Void) {
if (Main.SHOW_KNOWN_BUGS) {
Sys.println("Running known bug: " + name);
func();
} else {
skippedKnownBugs++;
}
}
public static function startUnitTest(path:String) {
Sys.println("Running unit tests in " + path);
currentTestPath.push(path);
totalTestUnits++;
}
public static function endUnitTest() {
var path = currentTestPath.pop();
//Sys.println("Finished unit tests " + path);
}
public static function printTestResults() {
Sys.println(Util.getTitle('RESULTS'));
Sys.println("Total Units: " + totalTestUnits);
Sys.println("Total tests passed: " + passedTestUnits);
Sys.println("Total tests failed: " + failedTestUnits);
if(skippedKnownBugs > 0)
Sys.println("Skipped known bugs: " + skippedKnownBugs);
//Sys.println("Finished unit tests " + currentTestPath.pop());
Sys.println(Util.getTitle('RESULTS'));
}
public static function parse(str:String) {
var p = new Parser();
p.allowTypes = true;
p.allowMetadata = true;
p.allowJSON = true;
var result = null;
try {
result = p.parseString(str);
} catch (e:Dynamic) {
Sys.println("## Error parsing: " + str);
Sys.println("## Error: " + e);
// Print stack trace
var stack = haxe.CallStack.toString(haxe.CallStack.exceptionStack());
Sys.println("## Stack trace: " + stack);
Sys.exit(1);
}
return result;
}
/**
* Parses a string without checking for errors
*/
public static function parseUnsafe(str:String) {
var p = new Parser();
p.allowTypes = true;
p.allowMetadata = true;
p.allowJSON = true;
return p.parseString(str);
}
public static var defaultImport = true;
public static function getInterp() {
var interp = new Interp();
//interp.importEnabled = true;
//interp.allowStaticVariables = true;
//interp.allowPublicVariables = true;
if(defaultImport) {
interp.variables.set("Math", Math);
interp.variables.set("Std", Std);
interp.variables.set("Type", Type);
interp.variables.set("StringTools", StringTools);
}
return interp;
}
public static inline function getTime():Float {
return untyped __global__.__time_stamp();
}
// expandScientificNotation but its WAY too long to write out
public static function exScienceN(value:Float):String {
var parts = Std.string(value).split("e");
var coefficient = Std.parseFloat(parts[0]);
var exponent = parts.length > 1 ? Std.parseInt(parts[1]) : 0;
var result = "";
if (exponent > 0) {
result += StringTools.replace(Std.string(coefficient), ".", "");
var decimalLength = Std.string(coefficient).split(".")[1].length;
var additionalZeros:Int = Std.int(Math.abs(exponent - decimalLength));
result += StringTools.lpad("", "0", additionalZeros); // repeat
} else {
result += "0.";
var leadingZeros:Int = Std.int(Math.abs(exponent) - 1);
result += StringTools.lpad("", "0", leadingZeros); // repeat
result += StringTools.replace(Std.string(coefficient), ".", "");
}
return result;
}
public static function convertToReadableTime(seconds:Float) {
if (seconds >= 1) return seconds + " s";
var milliseconds = seconds * 1000; // 1 second = 1,000 ms
if (milliseconds >= 1) return milliseconds + " ms";
var microseconds = seconds * 1000000; // 1 second = 1,000,000 μs
if (microseconds >= 1) return microseconds + " μs";
var nanoseconds = seconds * 1000000000; // 1 second = 1,000,000,000 ns
return nanoseconds + " ns";
}
public static function errorHandler(error:Error) {
#if hscriptPos
var fileName = error.origin;
var fn = '$fileName:${error.line}: ';
#else
var fn = '';
#end
var err = Std.string(error);
if (err.startsWith(fn)) err = err.substr(fn.length);
Sys.println("ERROR: " + fn + err);
//Logs.traceColored([
// Logs.logText(fn, GREEN),
// Logs.logText(err, RED)
//], ERROR);
}
public static function deepCompareArrays(a1: Array<Dynamic>, a2: Array<Dynamic>): Bool {
if (a1.length != a2.length)
return false;
for (i in 0...a1.length) {
var item1 = a1[i];
var item2 = a2[i];
if (Std.isOfType(item1, Array) && Std.isOfType(item2, Array)) {
if (!deepCompareArrays(item1, item2))
return false;
} else if (item1 != item2)
return false;
}
return true;
}
public static function roundDecimal(Value:Float, Precision:Int):Float {
var mult:Float = 1;
for (i in 0...Precision)
mult *= 10;
return Math.fround(Value * mult) / mult;
}
public inline static function roundWith(Value:Float, Mult:Int):Float {
return Math.fround(Value * Mult) / Mult;
}
public static function getTitle(title:String, ?dashsLen:Int = 46) {
var l = StringTools.lpad("", "-", Std.int((dashsLen - title.length - 2)/2));
return l + ' $title ' + l;
}
}
+74
View File
@@ -0,0 +1,74 @@
package benchmark;
import hscript.Expr;
class Benchmark extends HScriptRunner {
public var haxeTimes:Array<Float> = [];
public var haxeTotalTime:Float = 0;
public var hscriptTimes:Array<Float> = [];
public var hscriptTotalTime:Float = 0;
public function new(name:String, iterations:Int) {
super();
doBenchmark(name, iterations);
}
public function doBenchmark(name:String, iterations:Int) {
haxeTimes = cast new haxe.ds.Vector<Float>(iterations);
hscriptTimes = cast new haxe.ds.Vector<Float>(iterations);
for (i in 0...iterations) {
reset();
var haxeStartTime:Float = Util.getTime();
haxeBenchmark();
var haxeEndTime:Float = Util.getTime();
haxeTimes[i] = haxeEndTime-haxeStartTime;
reset();
var hscriptStartTime:Float = Util.getTime();
hscriptBenchmark();
var hscriptEndTime:Float = Util.getTime();
hscriptTimes[i] = hscriptEndTime-hscriptStartTime;
}
haxeTimes.sort(Reflect.compare);
hscriptTimes.sort(Reflect.compare);
for (time in haxeTimes) haxeTotalTime += time;
for (time in hscriptTimes) hscriptTotalTime += time;
Sys.println(Util.getTitle('$name BENCHMARK'));
if ((haxeTotalTime == 0 || hscriptTotalTime == 0) || (haxeTimes.length <= 0 || hscriptTimes.length <= 0)) {
Sys.println('BENCHMARK UNNOTICABLE, TOOK 0 SECONDS');
} else {
var haxeWon = hscriptTotalTime > haxeTotalTime;
Sys.println('${haxeWon ? "Haxe" : "Hscript"} was faster overall (Faster by: ${Util.roundWith((haxeWon ? hscriptTotalTime/haxeTotalTime : haxeTotalTime/hscriptTotalTime), 100)}x)');
Sys.println('Haxe average time: ${Util.convertToReadableTime(haxeTotalTime/iterations)} (Highest: ${haxeTimes[haxeTimes.length-1]})');
Sys.println('Hscript average time: ${Util.convertToReadableTime(hscriptTotalTime/iterations)} (Highest: ${hscriptTimes[haxeTimes.length-1]})');
}
Sys.println(Util.getTitle('$name BENCHMARK'));
}
public function reset() {}
public var expr:Expr;
public inline function cacheExpr(script:String) {
interp = getNewInterp();
interp.variables.set("self", this);
interp.scriptObject = this;
expr = Util.parse(headerCode + script + tailCode);
}
public override function execute(script:String):Dynamic {
if (expr == null)
cacheExpr(script);
return interp.execute(expr);
}
public function haxeBenchmark() {}
public function hscriptBenchmark() {}
}
+57
View File
@@ -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);
}
}
+4
View File
@@ -0,0 +1,4 @@
import hscript.Interp;
import hscript.Parser;
import Util;
+19
View File
@@ -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();
}
}
+161
View File
@@ -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();
}
}
+68
View File
@@ -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);
}
+20
View File
@@ -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();
}
}
+280
View File
@@ -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();
}
}
+157
View File
@@ -0,0 +1,157 @@
package tests;
class FinalCase extends TestCase {
override function setup() {
super.setup();
}
override function getNewInterp() {
Util.defaultImport = false;
var interp = super.getNewInterp();
Util.defaultImport = true;
//interp.variables.set("Std", Std);
//interp.variables.set("String", String);
//interp.variables.set("Bool", Bool);
//interp.variables.set("Float", Float);
//interp.variables.set("Array", Array);
//interp.variables.set("Int", Int);
//interp.variables.set("IntIterator", IntIterator);
return interp;
}
override function run() {
headerCode = "var i = 5; var x = 10;";
var i = 5; var x = 10;
assertDisplay("(i - x) * 30 + 90", ((i-x) * 30) + 90);
assertDisplay("i - x * 30 + 90", i-x * 30 + 90);
/*
// Test operator precedence
assertDisplay("i / x * 30 + 90", i / x * 30 + 90);
assertDisplay("i / (x * 30) + 90", i / (x * 30) + 90);
assertDisplay("x * 30 / i + 90", x * 30 / i + 90);
//assertEq("(x * 30) / i + 90", x * 30 / i + 90);
// (_) -> {a = false;}
// Shouldnt be converted to (_) -> return {a = false;}
Util.parse("openCodesList(false, codesOpened ? false : true, previousOpen != open ? true : false);");
Util.parse("openCodesList(false, 'HELLO' ? false : false, 'WORLD' ? true : true);");
Util.parse('FlxTween.tween(newSprite, {"scale.x": 1, "scale.y": 1, alpha: 1, angle: 0}, 0.3, {ease: FlxEase.qaudInOut});');
Util.parse('
bloomShader.dim = dim = .8 + (.3 * FlxMath.fastSin(__totalTime));
bloomShader.size = size = 18 + (8 * FlxMath.fastSin(__totalTime));
');
*/
Util.parse('sprite.setPosition(codesList.x + (sprite.ID % 2 == 1 ? 240 : 67),codesList.y + (15 * sprite.ID) + (sprite.ID%2 == 1 ? 35 : 54));');
headerCode = "import tests.FinalCase.TestEnum;";
assertEq("TestEnum.A", TestEnum.A);
assertEq("TestEnum.B", TestEnum.B);
assertEq("TestEnum.C(1, 2)", TestEnum.C(1, 2));
assertEq("TestEnum.D(1, 2, 3)", TestEnum.D(1, 2, 3));
Util.parse('test ? 0xFF343434 : 0xFF92A2FF');
Util.parse('test ? 1 : 0.75;');
Util.runKnownBug("Function then array causes error, unless the function ends with ;", function() {
headerCode = "";
function isEven(i:Int):Bool { return i % 2 == 0; }
assertEq("function isEven(i:Int):Bool { return i % 2 == 0; }[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]);
});
assertEq("[for(i in 0...10) i]", [for(i in 0...10) i]);
assertEq("[for(i in 0...10) i => null]", [for(i in 0...10) i => null]);
assertEq("[for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']", [for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']);
assertEq("[for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']", [for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']);
assertEq("[for(i in 0...10) for(j in 0...10) i * j]", [for(i in 0...10) for(j in 0...10) i * j]);
headerCode = "function isEven(i:Int):Bool { return i % 2 == 0; };";
function isEven(i:Int):Bool { return i % 2 == 0; }
assertEq("[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]);
assertEq("[for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]", [for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]);
headerCode = "function area(a:Int, b:Int):Int { return a * b; };";
function area(a:Int, b:Int):Int { return a * b; }
assertEq("[for(i in 0...10) for(j in 0...10) area(i, j)]", [for(i in 0...10) for(j in 0...10) area(i, j)]);
headerCode = "";
assertEq("[0=>'hello', 1=>'world']", [0=>'hello', 1=>'world']);
assertEq("[0=>'hello', 1=>'world'][1]", [0=>'hello', 1=>'world'][1]);
assertEq("[0=>'hello', 5=>'world'][1]", [0=>'hello', 5=>'world'][1]);
assertEq("
import tests.FinalCase;
FinalCase.test1();
", FinalCase.test1());
assertEq("
import tests.FinalCase as FC;
FC.test1();
", FinalCase.test1());
assertEq("
import tests.FinalCase as FC;
import tests.FinalCase.test;
FC.test1() + test();
", FinalCase.test1() + test());
assertEq("
//import Std.isOfType;
Std.isOfType('', String);
", Std.isOfType('', String));
assertEq("
import Std.isOfType;
isOfType('', String);
", Std.isOfType('', String));
assertEq("
import Math.round as F;
F(2.9);", Math.round(2.9));
assertEq("
import Math.round;
round(1.9);", Math.round(1.9));
// Test EOF with preprocessor
}
static function test1() {
return "hello";
}
static function test() {
return "world";
}
function assertDisplay(script:String, expected:Dynamic) {
var _script = script;
script = '"${script}";\n' + script;
assertEq(script, expected, _script);
}
override function teardown() {
super.teardown();
}
}
enum TestEnum {
A;
B;
C(a:Int, b:Int);
D(a:Int, b:Int, c:Int);
}
+74
View File
@@ -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();
}
}
+59
View File
@@ -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();
}
}
+35
View File
@@ -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();
}
}
+39
View File
@@ -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();
}
}
+23
View File
@@ -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();
}
}
+23
View File
@@ -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();
}
}
+164
View File
@@ -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();
}
}
+58
View File
@@ -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();
}
}
+120
View File
@@ -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();
}
}
+214
View File
@@ -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();
}
}
+34
View File
@@ -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();
}
}
+180
View File
@@ -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();
}
}
+25
View File
@@ -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();
}
}
+38
View File
@@ -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();
}
}
+93
View File
@@ -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;
}
}
+25
View File
@@ -0,0 +1,25 @@
# Generic Haxe stuffs to make the project compile
-cp src
-cp ../
-D analyzer-optimize
-main Main
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
# --library hscript-improved
# --library hxcpp-debug-server
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
--define hscriptPos
--define HXCPP_CHECK_POINTER
--define HXCPP_STACK_LINE
--define HXCPP_DEBUG_LINK
--dce no
# File Specific Defines
--define windows
# Copying the assets from source code to bin folder
# --cmd Xcopy assets\ bin\cpp\assets\ /E /H /C /I /Y
--cpp bin/cpp
--cmd cd bin/cpp
--cmd Main.exe