Compare commits

..
Author SHA1 Message Date
Jaime Humberto Macias Bustamante d344ede669 Regular Expression support (regex) 2025-09-03 20:13:39 -07:00
Jaime Humberto Macias Bustamante 85f51df7eb fixed accessing class fields from objects
this shouldn't happen, unless "useRedirects" is on.
2025-08-19 19:24:00 -07:00
FrakitsandGitHub 5dce82eaf6 Update haxelib.json 2025-08-15 15:07:28 +03:00
Jaime Humberto Macias Bustamante 0b4675b4ad static property accessor call 2025-08-14 03:07:44 -07:00
Jaime Humberto Macias Bustamante e4861a606a Features Documentation 2025-08-04 13:40:48 -07:00
Jaime Humberto Macias Bustamante bbe9fd255d fixed package parsing issue 2025-08-03 21:46:04 -07:00
Jaime Humberto Macias Bustamante 6bd3f26746 package syntax support
For now it's just sugar syntax.
2025-08-03 21:28:59 -07:00
Jaime Humberto Macias Bustamante 83aa0f3856 fixed variable set bug 2025-08-03 01:08:06 -07:00
Jaime Humberto Macias Bustamante f97a7b8a55 Merge branch 'codename-dev' of github.com:CodenameCrew/hscript-improved into codename-dev 2025-08-03 00:08:46 -07:00
Jaime Humberto Macias Bustamante f936877339 MyCustomClass.new + Optimization
You can now pass the custom class constructor as a field.
2025-08-03 00:08:43 -07:00
Jamextreme140andGitHub 9fbe8a6316 Update README.md 2025-08-02 02:45:30 -07:00
Jaime Humberto Macias Bustamante 7f087eab03 fixed getting property fields on superclass
Basically "Type.getInstanceFields" doesn't take into account property fields, so it uses "Reflect.fields" to fetch the rest of fields.
2025-08-01 16:55:13 -07:00
Jaime Humberto Macias Bustamante 056a2bd791 fixed calling superclass functions 2025-07-29 13:23:13 -07:00
Jaime Humberto Macias Bustamante 902133a8ba goodbye legacy custom class, you will never be missed :3 2025-07-26 21:22:16 -07:00
Jaime Humberto Macias Bustamante 1791187808 Update ClassExtendMacro.hx 2025-07-26 16:09:02 -07:00
Jaime Humberto Macias Bustamante cacb0cdbd6 Cleanup + loopRun
- added loopRun to define custom loops (https://github.com/HaxeFoundation/hscript/commit/20c8bc3f3c72a4dedabf9a647940b9c4f43eedf8)
2025-07-26 15:34:35 -07:00
Jaime Humberto Macias Bustamante 0b1fbf66dc "cast" syntax support
- Now it parses unsafe/safe cast. For now it's just sugar syntax.
2025-07-26 01:59:57 -07:00
Jaime Humberto Macias Bustamante 2c6f48b1bc Update Parser.hx
now enum parses correctly
2025-07-24 14:18:45 -07:00
Jaime Humberto Macias Bustamante ed78ed79d9 Scripted Enums 2025-07-23 19:50:36 -07:00
Jaime Humberto Macias Bustamante 98b97bef66 Scripted Enums WIP 2025-07-21 22:39:08 -07:00
Jaime Humberto Macias Bustamante 003d22286c super call fixes 2025-07-21 16:47:01 -07:00
Jaime Humberto Macias Bustamante 5232f069fb final classes + improved custom class aliases 2025-07-17 14:46:12 -07:00
Jaime Humberto Macias Bustamante 4f13f07071 better custom class import alias 2025-07-17 13:20:03 -07:00
Jaime Humberto Macias Bustamante 70efab47d1 Documentation + custom classes import alias 2025-07-14 12:59:54 -07:00
Jaime Humberto Macias Bustamante c00c0c3257 Create IHScriptAbstractBehaviour.hx 2025-07-12 16:49:04 -07:00
Jaime Humberto Macias Bustamante f8a930cd48 upcasting 2025-07-12 16:13:59 -07:00
Jaime Humberto Macias Bustamante 1d753731e0 better default static extension fetch 2025-07-12 13:44:06 -07:00
Jaime Humberto Macias Bustamante b239ad2790 Update Interp.hx
oops :3
2025-07-11 09:03:29 -07:00
Jaime Humberto Macias Bustamante 97c3f8bb01 small optimization
since Reflect.hasField(cls, name) is quite expensive, we cache the fields, so it gets the field directly without issues.
2025-07-11 08:45:33 -07:00
Jaime Humberto Macias Bustamante 6b54479211 Static Extension support + small fixes
Static extension for real/custom classes. aka. the `using` keyword

Warning: UNTESTED
2025-07-11 04:35:03 -07:00
Jaime Humberto Macias Bustamante fd88d78544 Custom Class Type Check + Getting custom fields
- Now it checks for the custom class type (partial).

- Custom class fields get for generated classes.
2025-07-08 17:06:33 -07:00
Jaime Humberto Macias Bustamante 434ded6503 "null" value fix
CLOSES #9
2025-07-06 21:50:24 -07:00
Jaime Humberto Macias Bustamante a2cdefb369 static fixes :3 2025-07-03 02:44:53 -07:00
Jaime Humberto Macias Bustamante 46285a800e Update CustomClass.hx 2025-07-01 14:34:41 -07:00
Jaime Humberto Macias Bustamante 305ede38cc Update Parser.hx 2025-06-26 16:56:10 -07:00
Jaime Humberto Macias Bustamante f773589a37 Improved "is" operator
Official implementation of "is" operator.
2025-06-26 16:37:04 -07:00
Jaime Humberto Macias Bustamante b9a0706d6d performance fix for js + cleanup 2025-06-26 14:42:53 -07:00
Jaime Humberto Macias Bustamante 85f3830d71 bruh :'3 2025-06-23 18:23:51 -07:00
Jaime Humberto Macias Bustamante 2c6db18d06 Type Parameters parsing for "ENew"
it now parses type parameters on "ENew", ignored by the interpreter.

Example: "var a = new Object<Float>();
2025-06-23 15:12:15 -07:00
Jaime Humberto Macias Bustamante c165cdfc0a another bit of documentation 2025-06-20 15:59:31 -07:00
Jaime Humberto Macias Bustamante b48a3ee9ed Allow for setting fields before super
also improved fetching superclass fields
2025-06-20 14:45:06 -07:00
Jaime Humberto Macias Bustamante 1af4a64b10 Update CustomClassHandler.hx
i forgot to delete this
2025-06-20 01:28:15 -07:00
Jaime Humberto Macias Bustamante 64f287fd6d Better "EVar" pretty print + fix 2025-06-19 20:20:01 -07:00
Jaime Humberto Macias Bustamante c927d324dc Allow accessing other custom classes 2025-06-19 18:45:51 -07:00
Jaime Humberto Macias Bustamante bcd212a450 "null" fix 2025-06-19 15:55:00 -07:00
Jaime Humberto Macias Bustamante d05270d413 extending typed classes (WIP) 2025-06-19 13:14:02 -07:00
Jaime Humberto Macias Bustamante e40d18b8c4 Update AbstractHandler.hx
oops
2025-06-19 12:50:40 -07:00
Jaime Humberto Macias Bustamante 0e977b071c cleanup + optimization 2025-06-19 01:07:48 -07:00
Jaime Humberto Macias Bustamante c79882b75e Implemented Override
untested, still in progress tho
2025-06-18 10:08:59 -07:00
Jaime Humberto Macias Bustamante 251306a3d3 A bit if documentation + cleanup 2025-06-17 23:50:00 -07:00
Jaime Humberto Macias Bustamante 0bd95d402a Update CustomClass.hx
bruh
2025-06-17 22:49:54 -07:00
Jaime Humberto Macias Bustamante 6441acc93d fixes :3 2025-06-17 15:27:21 -07:00
Jaime Humberto Macias Bustamante 04a50580c6 Disallow static fields access from outside instances 2025-06-17 12:33:32 -07:00
Jaime Humberto Macias Bustamante a4d2d4c788 Custom Classes rework progress - 2 2025-06-16 22:24:22 -07:00
Jaime Humberto Macias Bustamante 54ab2fc509 Custom Classes rework progress 2025-06-16 12:44:40 -07:00
Jaime Humberto Macias Bustamante c16a5e99d5 Custom Classes rework (WIP) 2025-06-16 02:56:10 -07:00
Jaime Humberto Macias Bustamante e28b5a6de5 Property Variables
Now property variables fully works.
2025-06-15 23:03:46 -07:00
Jaime Humberto Macias Bustamante 6ac8fc7fe5 Fixed property access check 2025-06-12 14:50:31 -07:00
Jaime Humberto Macias Bustamante 43bd1001d2 "@:isVar" parses correctly now 2025-06-12 14:41:14 -07:00
Jaime Humberto Macias Bustamante 8bdc07c91b oops :3 2025-06-12 14:27:30 -07:00
Jaime Humberto Macias Bustamante a3a8f602ea Mandatory "allowTypes" for Property + "isVar" parsing 2025-06-12 14:24:51 -07:00
Jaime Humberto Macias Bustamante 1a9c0c68ac Update Parser.hx 2025-06-12 13:35:20 -07:00
Jaime Humberto Macias Bustamante 27b26eb2a0 Property Parsing + Simplified Interfaces - Part 2
- Now it parses "(get, set)". For now it's just sugar syntax.
2025-06-12 00:33:20 -07:00
Jaime Humberto Macias Bustamante 1bfe81cfac Simplified Interfaces - Part 1
here we go again...
2025-05-30 22:05:54 -07:00
33 changed files with 1866 additions and 3093 deletions
+14 -1
View File
@@ -1,16 +1,29 @@
hscript-improved
=======
- [Features](docs/FEATURES.md)
How to install
```
haxelib git hscript-improved https://github.com/CodenameCrew/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
or set this in build.hxml
```hxml
-D CUSTOM_CLASSES
```
Current Custom Class Limitations :
- For now, 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.
- You cannot create custom classes that extends a typed class (those ones that has `<T>`), this will be implemented in the future.
-----------
-5
View File
@@ -33,10 +33,6 @@ class TestHScript extends TestCase {
assertScript("- 123",-123);
assertScript("1.546",1.546);
assertScript(".545",.545);
assertScript("1e5",100000);
assertScript("1.2e2",120);
assertScript("100e-2",1);
assertScript("1.2e-1",0.12);
assertScript("'bla'","bla");
assertScript("null",null);
assertScript("true",true);
@@ -57,7 +53,6 @@ class TestHScript extends TestCase {
assertScript("3 * 2 // + 5 \n + 6",12);
assertScript("3 /* 2\n */ + 5",8);
assertScript("[55,66,77][1]",66);
assertScript("[11,22,33,][2]", 33);
assertScript("var a = [55]; a[0] *= 2; a[0]",110);
assertScript("x",55,{ x : 55 });
assertScript("var y = 33; y",33);
+83
View File
@@ -0,0 +1,83 @@
# FEATURES
- Custom Classes
- Final Classes
- Static Classes
- Allow for type check (`is`)
- Allow extending other custom classes
- Enums
```haxe
enum TypeValue {
NUMBER(n:Int);
DECIMAL(d:Float, ?p:Int);
CHARACTER(s:String);
BOOLEAN(b:Bool);
}
var type = TypeValue.DECIMAL(10.1234, 2);
// You need to type the full enum field for each case
// i.e. you can't type the enum field directly (limitation for now)
switch(type) {
case TypeValue.NUMBER(number):
trace("number: " + number);
case TypeValue.DECIMAL(decimal, precision):
if(precision != null)
trace("decimal: " + decimal + " | rounded decimal: " + roundDecimal(decimal, precision));
else
trace("decimal: " + decimal);
case TypeValue.CHARACTER(char):
trace("character: " + char);
default:
trace("unknown type");
}
function roundDecimal(Value:Float, Precision:Int) {
var mult:Float = Math.pow(10, Precision);
return Math.fround(Value * mult) / mult;
}
```
- Enum matching with arguments for switch statements (for real and scripted enums)
- Property Fields (`(get, set)` variables)
```haxe
public var myvar(get, set):Int;
var _myvar:Int = 10;
function get_myvar():Int {
return _myvar;
}
function set_myvar(val:Int):Int {
if(val > 10) return _myvar = val;
return val;
}
```
- `@:isVar` metadata support
- Static extension (`using`)
```haxe
using StringTools;
class IntExtender {
static public function triple(i:Int) {
return i * 3;
}
}
// need to create/import the custom class
// before setting the extension (limitation for now)
using IntExtender;
var str = " Hello World! ";
trace(str.trim()); // "Hello World!"
trace(12.triple()); // 36
```
- Support for real and custom classes
- Misc.
- Allow using type parameters for creating objects (i.e. `var a = new TypedObject<Int>();`)
- Allow `package` declaration. Ignored by the interpreter.
-1
View File
@@ -1,4 +1,3 @@
--macro keep('IntIterator')
--macro hscript.macros.AbstractHandler.init()
--macro hscript.macros.UsingMacro.init()
--macro hscript.macros.ClassExtendMacro.init()
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "hscript",
"name": "hscript-improved",
"url": "https://github.com/HaxeFoundation/hscript",
"license": "MIT",
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
+5 -4
View File
@@ -124,16 +124,17 @@ class Bytes {
#end
bout.addByte(Type.enumIndex(e));
switch( e ) {
case EIgnore(_):
case EPackage(n):
// TODO
case EImport(c):
// TODO
case EClass(_, _, _, _):
// TODO
case ERedirect(_, _):
case EEnum(en):
// TODO
case EUsing(name):
case ECast(e, _):
// TODO
case EEnum(name, fields):
case ERegex(e, f):
// TODO
case EConst(c):
doEncodeConst(c);
+3
View File
@@ -422,6 +422,9 @@ class Checker {
return makeType(t,e);
case CTOpt(t):
return makeType(t,e);
case CTExpr(_):
error("Unsupported expr type parameter", e);
return null;
}
}
-9
View File
@@ -12,11 +12,6 @@ class Config {
"openfl.display.BlendMode",
];
// Runs support for using in specific classes.
public static final ALLOWED_USING = [
];
// Incase any of your files fail
// These are the module names
public static final DISALLOW_CUSTOM_CLASSES = [
@@ -26,8 +21,4 @@ class Config {
public static final DISALLOW_ABSTRACT_AND_ENUM = [
];
public static final DISALLOW_USING = [
];
}
+355
View File
@@ -0,0 +1,355 @@
package hscript;
import hscript.utils.UnsafeReflect;
import haxe.Constraints.Function;
using Lambda;
/**
* The Custom Class core.
*
* Provides handlers for custom classes.
*
* @author Jamextreme140
*/
@:access(hscript.CustomClassHandler)
@:access(hscript.Property)
class CustomClass implements IHScriptCustomClassBehaviour {
public var className(get, never):String;
private function get_className():String
return __class.name;
public var __interp:Interp;
public var __real_fields:Array<String> = []; // UNUSED
public var __class__fields:Array<String> = []; // Declared fields
public var __allowSetGet:Bool = true;
var __class:CustomClassHandler;
var __superClass:IHScriptCustomClassBehaviour;
var __upperClass:IHScriptCustomClassBehaviour;
var __constructor:Function;
var __overrideFields:Array<String> = [];
var __cachedFieldSet:Map<String, Dynamic> = null;
var initializing:Bool = false;
public function new(__class:CustomClassHandler, ?args:Array<Dynamic>, ?cachedFieldSet:Map<String, Dynamic>) {
this.__class = __class;
__interp = new Interp();
__interp.errorHandler = __class.__interp.errorHandler;
__interp.importFailedCallback = __class.__interp.importFailedCallback;
// __interp.variables = __class.staticInterp.variables;
@:privateAccess __interp.usingHandler.usingEntries = __class.ogInterp.usingHandler.usingEntries;
__interp.publicVariables = __class.ogInterp.publicVariables;
__interp.staticVariables = __class.ogInterp.staticVariables;
__interp.customClasses = __class.ogInterp.customClasses;
for(f => v in __class.__interp.variables) {
if(f == 'new') continue;
if (!__interp.variables.exists(f))
__interp.variables.set(f, v);
}
for (f in __class.fields) {
switch (Tools.expr(f)) {
case EVar(n): __class__fields.push(n);
case EFunction(_, _, n, _, _, _, isOverride):
if(isOverride) __overrideFields.push(n);
__class__fields.push(n);
default: continue;
}
@:privateAccess __interp.exprReturn(f);
}
__interp.scriptObject = this;
initializing = true;
if(cachedFieldSet != null)
for(f => v in cachedFieldSet)
this.hset(f, v);
if (hasField('new')) {
buildConstructor();
call('new', args);
if(__cachedFieldSet != null) {
__cachedFieldSet.clear();
__cachedFieldSet = null;
}
if (this.__superClass == null && __class.extend != null)
__interp.error(ECustom("super() not called"));
} else if (__class.extend != null) {
buildSuperClass(args);
}
initializing = false;
}
function cacheFieldSet(name:String, val:Dynamic) {
if(!initializing) return;
if(__cachedFieldSet == null) __cachedFieldSet = [];
__cachedFieldSet.set(name, val);
}
function buildConstructor() {
__constructor = Reflect.makeVarArgs(buildSuperClass);
}
function buildSuperClass(?args:Array<Dynamic>) {
if (args == null)
args = [];
if (__class.cl == null) {
__interp.error(ECustom('Current class does not have a super'));
return;
}
if (__class.cl is CustomClassHandler) {
var customClass = new CustomClass(__class.cl, args, __cachedFieldSet);
if(__overrideFields.length > 0) {
for (field in __overrideFields) {
var func = __interp.variables.get(field);
customClass.overrideField(field, func);
}
}
customClass.__upperClass = this;
__superClass = customClass;
@:privateAccess __interp.__instanceFields = __interp.__instanceFields.concat(getSuperFields());
} else {
if(__cachedFieldSet != null)
UnsafeReflect.setField(__class.cl, "__cachedFieldSet", __cachedFieldSet);
__superClass = Type.createInstance(__class.cl, args);
var disallowCopy:Array<String> = {
var fieldMap:Map<String, String> = []; // Prevent duplicate values
for(f in Reflect.fields(__superClass).concat(Type.getInstanceFields(Type.getClass(__superClass))))
fieldMap.set(f, f);
fieldMap.array();
}
this.__real_fields = disallowCopy;
@:privateAccess __interp.__instanceFields = __interp.__instanceFields.concat(disallowCopy);
__superClass.__real_fields = this.__real_fields;
__superClass.__class__fields = this.__class__fields;
__superClass.__interp = this.__interp;
}
}
public function call(name:String, ?args:Array<Dynamic>, ?toSuper:Bool = false):Dynamic {
var superFnName:Null<String> = toSuper ? '_HX_SUPER__$name' : null;
var fn:Dynamic = {
if(toSuper && __interp.variables.exists(superFnName)) {
__interp.variables.get(superFnName);
}
else
__interp.variables.get(name);
};
if (fn != null && Reflect.isFunction(fn))
return UnsafeReflect.callMethodUnsafe(null, fn, (args == null) ? [] : args);
else
__interp.error(ECustom('$name doesn\'t exists or is not a function'));
return null;
}
function hasField(name:String) {
return __class__fields.contains(name);
}
function hasStaticField(name:String):Bool {
return __class.hasField(name);
}
function getField(name:String, allowProperty:Bool = true):Dynamic {
var f = __interp.variables.get(name);
if (f != null && allowProperty && f is Property) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callGetter(name);
prop.__allowSetGet = null;
return r;
}
return f;
}
function setField(name:String, val:Dynamic):Dynamic {
var f = getField(name, false);
if (f != null && f is Property) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callSetter(name, val);
prop.__allowSetGet = null;
return r;
}
__interp.variables.set(name, val);
return val;
}
/**
* Overrides (replaces) the declared function.
* @param name
* @param func
*/
function overrideField(name:String, func:Function) {
var f = getField(name, false);
if(f != null && Reflect.isFunction(f)) {
__interp.variables.set(name, func);
__interp.variables.set('_HX_SUPER__$name', f);
}
else if(__superClass != null && __superClass is CustomClass) {
cast(__superClass, CustomClass).overrideField(name, func);
}
}
function superHasField(name:String) {
if (__superClass == null)
return false;
var realFieldExists = __superClass.__real_fields != null && __superClass.__real_fields.contains(name);
var classFieldExists = __superClass.__class__fields != null && __superClass.__class__fields.contains(name);
if(!realFieldExists && !classFieldExists && __superClass is CustomClass)
return cast(__superClass, CustomClass).superHasField(name);
return realFieldExists || classFieldExists;
}
function getSuperFields():Array<String> {
if(__superClass == null) return [];
var classFields:Map<String, String> = []; // Prevents duplicated values
var cls:Null<IHScriptCustomClassBehaviour> = __superClass;
while (cls != null) {
for(fieldSet in [cls.__class__fields, cls.__real_fields])
for(f in fieldSet)
classFields.set(f, f);
var next:IHScriptCustomClassBehaviour = null;
if(cls is CustomClass)
next = cast(cls, CustomClass).__superClass;
if (next == null)
break;
cls = next;
}
return [for(f in classFields) f];
}
public function hget(name:String):Dynamic {
switch (name) {
case 'superClass': return __superClass;
case 'superConstructor': return __constructor;
default:
if (hasField(name))
return getField(name);
if (hasStaticField(name)) {
__interp.error(ECustom('The field ${name} should be accessed in a static way.'));
return null;
}
if (__superClass != null) {
if (superHasField(name)) {
__superClass.__allowSetGet = this.__allowSetGet;
return __superClass.hget(name);
}
}
throw "field '"
+ name
+ "' does not exist in custom class '"
+ this.className
+ "'"
+ (__superClass != null ? "' or super class '" + Type.getClassName(Type.getClass(this.__superClass)) + "'" : "");
}
return null;
}
public function hset(name:String, val:Dynamic):Dynamic {
if (hasField(name))
return setField(name, val);
if (hasStaticField(name)) {
__interp.error(ECustom('The field ${name} should be accessed in a static way.'));
return null;
}
if (__superClass != null) {
if (superHasField(name)) {
__superClass.__allowSetGet = this.__allowSetGet;
return __superClass.hset(name, val);
}
}
else if(__class.extend != null && initializing) {
cacheFieldSet(name, val);
return val;
}
throw "field '"
+ name
+ "' does not exist in custom class '"
+ this.className
+ "'"
+ (__superClass != null ? "' or super class '" + Type.getClassName(Type.getClass(this.__superClass)) + "'" : "");
return null;
}
// UNUSED
public function __callGetter(name:String):Dynamic {
return null;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
return null;
}
/**
* Returns the real superClass if the Custom Class
* extends another Custom Class, and so on until
* it reaches a real class, otherwise it will
* return the last fetched Custom Class
* @return Null<Dynamic>
*/
public function getSuperclass():IHScriptCustomClassBehaviour {
if(__superClass == null) return null;
var cls:Null<IHScriptCustomClassBehaviour> = __superClass;
// Check if the superClass is another custom class,
// so it will find for a real class, otherwise
// returns the last super CustomClass parent.
while (cls != null && cls is CustomClass) {
var next = cast(cls, CustomClass).__superClass;
if (next == null)
break; // Return the Custom Class itself
cls = next;
}
return cls is CustomClass ? this : cls;
}
// TODO: scripted safe cast for custom classes
public function getUpperclass():IHScriptCustomClassBehaviour {
if(__upperClass == null) return this;
var cls:CustomClass = cast __upperClass;
while (cls != null) {
var prev:CustomClass = cast cls.__upperClass;
if(prev == null)
break;
cls = prev;
}
return cls;
}
public function toString():String
return className;
}
+125 -181
View File
@@ -1,219 +1,168 @@
package hscript;
import hscript.Interp.DeclaredVar;
import hscript.utils.UnsafeReflect;
using StringTools;
@:deprecated('Switch to new Custom Class instead')
class CustomClassHandler implements IHScriptCustomConstructor {
public static var staticHandler = new StaticHandler();
/**
* Provides handlers for static custom class fields and instantiation.
*/
@:access(hscript.Property)
class CustomClassHandler implements IHScriptCustomConstructor implements IHScriptCustomAccessBehaviour{
public var ogInterp:Interp;
public var name:String;
public var fields:Array<Expr>;
public var extend:String;
public var extend:Null<String>;
public var interfaces:Array<String>;
public final isFinal:Bool;
public var cl:Class<Dynamic>;
public var cl:Dynamic;
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
private var __interp:Interp;
private var __staticFields:Array<String> = [];
public var __allowSetGet:Bool = true;
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>, ?isFinal:Bool) {
this.ogInterp = ogInterp;
this.name = name;
this.fields = fields;
this.extend = extend;
this.interfaces = interfaces;
this.isFinal = isFinal != null ? isFinal : false;
//this.cl = extend == null ? CustomTemplateClass : Type.resolveClass('${extend}_HSX');
if(cl == null)
ogInterp.error(EInvalidClass(extend));
if(extend != null) {
if(ogInterp.customClasses.exists(extend)) {
var customCls:CustomClassHandler = ogInterp.customClasses.get(extend);
if(customCls.isFinal)
ogInterp.error(ECustom('Cannot extend a final class'));
this.cl = customCls;
}
else
this.cl = Type.resolveClass('${extend}_HSX');
if(cl == null)
ogInterp.error(EInvalidClass(extend));
}
initStatic();
}
public function hnew(args:Array<Dynamic>):Dynamic {
// TODO: clean this up, it sucks, i hate it
// TODO: make static vars work correctly
var interp = new Interp();
@:access(hscript.Interp)
function initStatic() {
__interp = new Interp();
__interp.errorHandler = ogInterp.errorHandler;
__interp.importFailedCallback = ogInterp.importFailedCallback;
interp.errorHandler = ogInterp.errorHandler;
//__interp.variables = ogInterp.variables;
__interp.usingHandler.usingEntries = ogInterp.usingHandler.usingEntries;
__interp.publicVariables = ogInterp.publicVariables;
__interp.staticVariables = ogInterp.staticVariables;
__interp.customClasses = ogInterp.customClasses;
var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
for(f => v in ogInterp.variables)
if(!__interp.variables.exists(f))
__interp.variables.set(f, v);
//var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
//var capturedLocals:Map<String, DeclaredVar> = [];
//for(k=>e in __capturedLocals)
// if (e != null && e.depth <= 0)
// capturedLocals.set(k, e);
var disallowCopy = Type.getInstanceFields(cl);
_class.__real_fields = disallowCopy;
// todo: make it so you can use variables from the same scope as where the class was defined
//for (key => value in capturedLocals) {
// if(!disallowCopy.contains(key)) {
// interp.locals.set(key, {r: value, depth: -1});
// }
//}
for (key => value in ogInterp.variables) {
if(!disallowCopy.contains(key)) {
interp.variables.set(key, value);
}
}
for(key => value in ogInterp.customClasses) {
if(!disallowCopy.contains(key)) {
interp.customClasses.set(key, value);
}
}
// todo: clone static vars, but make it so setting it only sets it on the class
// todo: clone public vars
//trace("Before: " + [for(key => value in interp.variables) key]);
interp.variables.set("super", staticHandler);
var comparisonMap:Map<String, Dynamic> = [];
for(key => value in interp.variables) {
comparisonMap.set(key, value);
}
//_class.__custom__variables = interp.variables;
//trace(fields);
for(expr in fields) {
@:privateAccess
interp.exprReturn(expr);
}
//trace("After: " + [for(key => value in interp.variables) key]);
// get only variables that were not set before
var classVariables = [
for(key => value in interp.variables)
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
key => value
];
//for(variable => value in classVariables) {
// if(variable == "this" || variable == "super" || variable == "new") continue;
// @:privateAccess
// if(!interp.__instanceFields.contains(variable)) {
// interp.__instanceFields.push(variable);
// }
//}
//_class.__class__fields = [for(key => value in classVariables) key];
//trace(_class.__class__fields);
//@:privateAccess
//trace(interp.__instanceFields);
//_class.__interp = interp;
//_class.__allowSetGet = false;
interp.scriptObject = _class;
for(variable => value in interp.variables) {
if(variable == "this" || variable == "super" || variable == "new") continue;
if(variable.startsWith("set_") || variable.startsWith("get_")) {
//_class.__allowSetGet = true;
}
}
var newFunc = interp.variables.get("new");
if(newFunc != null) {
var comparisonMap:Map<String, Dynamic> = [];
for(key => value in interp.variables) {
comparisonMap.set(key, value);
for(e in fields.copy()) {
var validField:Bool = false;
var staticField:Bool = false;
var fieldName:String = "";
switch (Tools.expr(e)) {
case EVar(n, _, _, _, isStatic):
validField = true;
staticField = isStatic;
fieldName = n;
case EFunction(_, _, n, _, _, isStatic, _, _, _, _):
validField = true;
staticField = isStatic;
fieldName = n;
default:
}
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
// get only variables that were not set before
var classVariables = [
for(key => value in interp.variables)
if(!comparisonMap.exists(key) || comparisonMap[key] != value)
key => value
];
for(variable => value in classVariables) {
if(variable == "this" || variable == "super" || variable == "new") continue;
@:privateAccess
if(!interp.__instanceFields.contains(variable)) {
interp.__instanceFields.push(variable);
}
/*
if(!_class.__class__fields.contains(variable)) {
_class.__class__fields.push(variable);
}
*/
if(staticField && validField) {
__interp.exprReturn(e);
__staticFields.push(fieldName);
fields.remove(e);
}
}
}
public function hnew(args:Array<Dynamic>):Dynamic
return new CustomClass(this, args);
return _class;
@:allow(hscript.Interp)
function hasField(name:String) {
return __staticFields.contains(name);
}
function getField(name:String, allowProperty:Bool = true):Dynamic {
var f = __interp.variables.get(name);
if(f is Property && allowProperty) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callGetter(name);
prop.__allowSetGet = null;
return r;
}
return f;
}
function setField(name:String, val:Dynamic):Dynamic {
var f = getField(name, false);
if(f is Property) {
var prop:Property = cast f;
prop.__allowSetGet = this.__allowSetGet;
var r = prop.callSetter(name, val);
prop.__allowSetGet = null;
return r;
}
__interp.variables.set(name, val);
return val;
}
public function hget(name:String):Dynamic {
if(name == 'new') {
var __constructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
return this.hnew(args);
});
return __constructor;
}
if(hasField(name)) {
return getField(name);
}
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
return null;
}
public function hset(name:String, val:Dynamic):Dynamic {
if(hasField(name))
return setField(name, val);
throw "field '"+ name+ "' does not exist in class '"+ this.name+ "'";
return null;
}
// UNUSED
public function __callGetter(name:String):Dynamic {
return null;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
return null;
}
public function toString():String {
return name;
}
}
/*
class CustomTemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomAccessBehaviour {
public var __interp:Interp;
public var __customClass:hscript.customclass.CustomClass;
public var __allowSetGet:Bool = true;
public var __custom__variables:Map<String, Dynamic>;
public var __real_fields:Array<String>;
public var __class__fields:Array<String>;
public function hset(name:String, val:Dynamic):Dynamic {
if(__allowSetGet && __custom__variables.exists("set_" + name))
return __callSetter(name, val);
if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
return val;
}
if(__real_fields.contains(name)) {
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
__custom__variables.set(name, val);
return val;
}
public function hget(name:String):Dynamic {
if(__allowSetGet && __custom__variables.exists("get_" + name))
return __callGetter(name);
if (__custom__variables.exists(name))
return __custom__variables.get(name);
return UnsafeReflect.getProperty(this, name);
}
public function __callGetter(name:String):Dynamic {
__allowSetGet = false;
var v = __custom__variables.get("get_" + name)();
__allowSetGet = true;
return v;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
__allowSetGet = false;
var v = __custom__variables.get("set_" + name)(val);
__allowSetGet = true;
return v;
}
}
*/
/**
* This is for backwards compatibility with old hscript-improved, since some scripts use it
*/
**/
@:dox(hide)
@:keep
class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustomAccessBehaviour {
public var __interp:Interp;
public var __allowSetGet:Bool = true;
public var __allowPrivateAccess:Bool = true;
public function hset(name:String, val:Dynamic):Dynamic {
var variables = __interp.variables;
@@ -243,8 +192,3 @@ class TemplateClass implements IHScriptCustomBehaviour implements IHScriptCustom
return v;
}
}
/*
final class StaticHandler {
public function new() {}
}
*/
+33 -34
View File
@@ -51,10 +51,9 @@ enum ExprDef {
typedef ExprDef = Expr;
enum Expr {
#end
EIgnore(skip:Bool);
EConst( c : Const );
EIdent( v : String );
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess );
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool, ?isPrivate : Bool, ?isFinal : Bool, ?isInline : Bool, ?get : FieldPropertyAccess, ?set : FieldPropertyAccess, ?isVar:Bool );
EParent( e : Expr );
EBlock( e : Array<Expr> );
EField( e : Expr, f : String , ?safe : Bool );
@@ -70,7 +69,7 @@ enum Expr {
EReturn( ?e : Expr );
EArray( e : Expr, index : Expr );
EArrayDecl( e : Array<Expr>, ?wantedType: CType );
ENew( cl : String, params : Array<Expr> );
ENew( cl : String, params : Array<Expr>, ?paramType:Array<CType> );
EThrow( e : Expr );
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
EObject( fl : Array<ObjectField> );
@@ -80,11 +79,12 @@ enum Expr {
EMeta( name : String, args : Array<Expr>, e : Expr );
ECheckType( e : Expr, t : CType );
EImport( c : String, ?asname:String );
EPackage( ?n:String );
EImport( c : String, ?asname:String, ?isUsing:Bool );
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
ERedirect( name:String, className:String, ?cl:Class<Dynamic> );
EUsing(name:String);
EEnum(name:String, fields:Array<EnumType>);
EEnum( en:EnumDecl, ?isAbstract:Bool );
ECast(e:Expr, ?t:CType);
ERegex(e:String, flags:String);
}
@:structInit
@@ -115,6 +115,18 @@ final class MetadataEntry {
typedef Metadata = Array<MetadataEntry>;
@:structInit
final class EnumDecl {
public var name : String;
public var fields : Array<EnumField>;
}
@:structInit
final class EnumField {
public var name : String;
public var args : Array<Argument>;
}
enum CType {
CTPath( path : Array<String>, ?params : Array<CType> );
CTFun( args : Array<CType>, ret : CType );
@@ -122,6 +134,7 @@ enum CType {
CTParent( t : CType );
CTOpt( t : CType );
CTNamed( n : String, t : CType );
CTExpr( e : Expr ); // for type parameters only
}
#if hscriptPos
@@ -163,10 +176,9 @@ enum Error {
enum ModuleDecl {
DPackage( path : Array<String> );
DImport( path : Array<String>, ?everything : Bool, ?asname:String );
DUsing( path : Array<String>);
DTypedef( c : TypeDecl );
DImport( path : Array<String>, ?everything : Bool );
DClass( c : ClassDecl );
DTypedef( c : TypeDecl );
}
typedef ModuleType = {
@@ -203,6 +215,15 @@ enum abstract FieldAccess(UInt8) {
var AMacro;
}
enum abstract FieldPropertyAccess(UInt8) {
var ADefault;
var ANull;
var AGet;
var ASet;
var ADynamic;
var ANever;
}
enum FieldKind {
KFunction( f : FunctionDecl );
KVar( v : VarDecl );
@@ -216,30 +237,8 @@ final class FunctionDecl {
}
typedef VarDecl = {
var get : FieldPropertyAccess;
var set : FieldPropertyAccess;
var isFinal : Bool;
var get : Null<String>;
var set : Null<String>;
var expr : Null<Expr>;
var type : Null<CType>;
}
typedef TypeParamDecl = {};
enum EnumType {
ESimple(name: String);
EConstructor(name: String, args: Array<Argument>);
}
enum abstract FieldPropertyAccess(UInt8) {
var ADefault;
var ANull;
var AGet;
var ASet;
var ADynamic;
var ANever;
}
enum AccessContext {
CInner(cl:String);
COuter;
}
+66
View File
@@ -0,0 +1,66 @@
package hscript;
import hscript.utils.UnsafeReflect;
// TODO: EnumTools for scripted enums
/**
* Wrapper class for enums, both for real and scripted.
*/
@:structInit
class HEnum implements IHScriptCustomBehaviour {
private var enumValues(default, null) = {};
public function setEnum(name:String, enumValue:Dynamic):Void {
UnsafeReflect.setField(enumValues, name, enumValue);
}
public function getEnum(name:String):Null<Dynamic> {
if (UnsafeReflect.hasField(enumValues, name))
return UnsafeReflect.field(enumValues, name);
return null;
}
public function hget(name:String):Dynamic {
return getEnum(name);
}
public function hset(name:String, val:Dynamic):Dynamic {
return null;
}
}
@:nullSafety
@:structInit
class HEnumValue {
public var enumName:String;
public var fieldName:String;
public var index:Int;
public var args:Array<Dynamic>;
public function toString():String {
return '$enumName.$fieldName${args.length > 0 ? '(${[for (a in args) a].join(", ")})' : ''}';
}
public inline function getEnumName():String
return this.enumName;
public inline function getConstructorArgs():Array<Dynamic>
return this.args;
public function compare(other:HEnumValue):Bool {
if (enumName != other.enumName || fieldName != other.fieldName)
return false;
if (args.length == 0 && other.args.length == 0)
return true;
if (args.length == 0 || other.args.length == 0)
return false;
if (args.length != other.args.length)
return false;
for (i in 0...args.length) // TODO: allow deep comparison, like arrays
if (args[i] != other.args[i])
return false;
return true;
}
}
+12
View File
@@ -0,0 +1,12 @@
package hscript;
// Soon...
interface IHScriptAbstractBehaviour extends IHScriptCustomBehaviour {
public var hasOp:Bool;
public var hasArr:Bool;
// @:op(A * B), @:op(A++), etc...
public function hop(kind:String, a:Dynamic, ?b:Dynamic):Dynamic;
public function harrayget(key:Dynamic):Dynamic;
public function harrayset(key:Dynamic, val:Dynamic):Dynamic;
}
+4 -5
View File
@@ -1,11 +1,10 @@
package hscript;
interface IHScriptCustomAccessBehaviour {
/**
* Same Interface as IHScriptCustomBehaviour but for Property.
*/
interface IHScriptCustomAccessBehaviour extends IHScriptCustomBehaviour {
var __allowSetGet:Bool;
var __allowPrivateAccess:Bool;
public function hset(name:String, val:Dynamic):Dynamic;
public function hget(name:String):Dynamic;
public function __callGetter(name:String):Dynamic;
public function __callSetter(name:String, val:Dynamic):Dynamic;
+17
View File
@@ -1,6 +1,23 @@
package hscript;
/**
* Special Interface for handling field access behaviour.
* Basically works like the operator overload `@:op(a.b)`
* for an abstract.
*/
interface IHScriptCustomBehaviour {
/**
* Field Write Access
* @param name - Field Name
* @param val - Value to assign
* @return Dynamic - The assigned value
*/
public function hset(name:String, val:Dynamic):Dynamic;
/**
* Field Read Access
* @param name - Field Name
* @return Dynamic - The returned field
*/
public function hget(name:String):Dynamic;
}
+7 -2
View File
@@ -1,6 +1,11 @@
package hscript;
interface IHScriptCustomClassBehaviour {
public var __customClass:hscript.customclass.CustomClass;
/**
* Special Interface to make a class usable for Custom Classes.
*/
interface IHScriptCustomClassBehaviour extends IHScriptCustomAccessBehaviour{
public var __interp:Interp;
public var __real_fields:Array<String>;
public var __class__fields:Array<String>;
}
+3
View File
@@ -1,5 +1,8 @@
package hscript;
/**
* Special Interface for handling new instances of an object.
*/
interface IHScriptCustomConstructor {
public function hnew(args:Array<Dynamic>):Dynamic;
}
+480 -932
View File
File diff suppressed because it is too large Load Diff
-15
View File
@@ -1,15 +0,0 @@
package hscript;
class InterpConfig {
// Automatic import redirect for certain classes
public static final IMPORT_REDIRECTS = [
"Type" => "hscript.proxy.ProxyType"
];
// Incase an import fails
// These are the module names
@:unreflective
public static final DISALLOW_IMPORT = [
"Type"
];
}
+2
View File
@@ -127,6 +127,8 @@ class Macro {
tf.push( { name : f.name, meta : meta, doc : null, access : [], kind : FVar(convertType(f.t), null), pos : p } );
}
TAnonymous(tf);
case CTExpr(_):
throw "assert";
};
}
+209 -205
View File
@@ -20,9 +20,9 @@
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import haxe.rtti.Meta;
import hscript.Expr;
import hscript.Expr.FieldPropertyAccess;
using StringTools;
enum Token {
TEof;
@@ -43,6 +43,7 @@ enum Token {
TDoubleDot;
TMeta( s : String );
TPrepro( s : String );
TRegex( e : String, f:String );
}
@:structInit
@@ -72,6 +73,11 @@ class Parser {
**/
public var allowJSON : Bool;
/**
activate Regular Expression parsing
**/
public var allowRegex : Bool;
/**
allow types declarations
**/
@@ -87,11 +93,6 @@ class Parser {
**/
public var resumeErrors : Bool;
/*
package name, set when using "package;" in your script.
*/
public var packageName:String = null;
// implementation
var input : String;
var readPos : Int;
@@ -120,6 +121,8 @@ class Parser {
#end
static inline var regexFlags: String = "igmsu";
public function new() {
line = 1;
opChars = "+*/-=!><&|^%~";
@@ -140,13 +143,21 @@ class Parser {
];
opPriority = new Map();
opRightAssoc = new Map();
#if (haxe >= "4.0.0")
for(i => p in priorities)
for(x in p) {
opPriority.set(x, i);
if( i == 9 ) opRightAssoc.set(x, true);
}
#else
for( i in 0...priorities.length )
for( x in priorities[i] ) {
opPriority.set(x, i);
if( i == 9 ) opRightAssoc.set(x, true);
}
#end
for( x in ["!", "++", "--", "~"] ) // unary "-" handled in parser directly!
opPriority.set(x, x == "++" || x == "--" ? -1 : -2);
opPriority.set(x, (x == "++" || x == "--") ? -1 : -2);
}
public inline function error( err:#if hscriptPos ErrorDef #else Error #end, pmin:Int, pmax:Int ) {
@@ -242,16 +253,6 @@ class Parser {
}
}
function extractIdent(tk: Token): String {
switch (tk) {
case TId(id):
return id;
default:
unexpected(tk);
return null;
}
}
inline function expr(e:Expr):#if hscriptPos ExprDef #else Expr #end {
#if hscriptPos
return e.e;
@@ -303,22 +304,19 @@ class Parser {
case EReturn(e): e != null && isBlock(e);
case ETry(_, _, _, e): isBlock(e);
case EMeta(_, _, e): isBlock(e);
case EIgnore(skip): skip;
default: false;
}
}
function parseFullExpr( exprs : Array<Expr> ):Void {
var e = parseExpr();
if(!expr(e).match(EIgnore(_)))
exprs.push(e);
exprs.push(e);
var tk = token();
// this is a hack to support var a,b,c; with a single EVar
while( tk == TComma && e != null && expr(e).match(EVar(_)) ) {
e = parseStructure("var"); // next variable
if(!expr(e).match(EIgnore(_)))
exprs.push(e);
exprs.push(e);
tk = token();
}
@@ -373,7 +371,7 @@ class Parser {
var p1 = tokenMin;
#end
switch( tk ) {
case TId(id):
case TId(id): // TODO: direct access to classes by typing their path
var e = parseStructure(id, oldPos);
if( e == null )
e = mk(EIdent(id));
@@ -484,16 +482,17 @@ class Parser {
case TBkOpen:
var a = [];
tk = token();
var first:Bool = true;
var first = true;
while( tk != TBkClose && (!resumeErrors || tk != TEof) ) {
if(!first) {
if(tk != TComma)
if (!first) {
if (tk != TComma)
unexpected(tk);
else {
tk = token();
if (tk == TBkClose)
break;
}
}
first = false;
push(tk);
@@ -517,8 +516,13 @@ class Parser {
}
return parseExprNext(mk(EArrayDecl(a, nextType), p1));
case TMeta(id) if( allowMetadata ):
if(id == ':isVar') isVar = true;
var args = parseMetaArgs();
return mk(EMeta(id, args, parseExpr()),p1);
var e = parseExpr();
isVar = false;
return mk(EMeta(id, args, e),p1);
case TRegex(e, f) if(allowRegex):
return mk(ERegex(e, f), p1);
default:
return unexpected(tk);
}
@@ -604,7 +608,8 @@ class Parser {
return mk(EBinop(op,e1,e),pmin(e1),pmax(e1));
return switch( expr(e) ) {
case EBinop(op2,e2,e3):
if( opPriority.get(op) <= opPriority.get(op2) && !opRightAssoc.exists(op) )
var delta = opPriority.get(op) - opPriority.get(op2);
if( delta < 0 || (delta == 0 && !opRightAssoc.exists(op)) )
mk(EBinop(op2,makeBinop(op,e1,e2),e3),pmin(e1),pmax(e3));
else
mk(EBinop(op, e1, e), pmin(e1), pmax(e));
@@ -649,7 +654,7 @@ class Parser {
if( semic ) push(TSemicolon);
}
mk(EIf(cond,e1,e2),p1,(e2 == null) ? tokenMax : pmax(e2));
case "override":
case "override": // BIG TODO: OPTIMIZE ALL THIS BELOW UNTIL "inline"
nextIsOverride = true;
var nextToken = token();
switch(nextToken) {
@@ -857,8 +862,10 @@ class Parser {
}
}
var ident = getIdent();
var get = ADefault, set = ADefault;
var get:Null<FieldPropertyAccess> = null;
var set:Null<FieldPropertyAccess> = null;
var hasGetSet:Bool = false;
var oldIsVar:Bool = isVar;
var tk = token();
if( tk == TPOpen) {
@@ -867,7 +874,7 @@ class Parser {
var getId = getIdent();
switch (getId) {
case 'default': // Do nothing
case 'default': get = ADefault;
case 'get': get = AGet;
case 'null': get = ANull;
case 'dynamic': get = ADynamic;
@@ -877,7 +884,7 @@ class Parser {
ensure(TComma);
var setId = getIdent();
switch (setId) {
case 'default': // Do nothing
case 'default': set = ADefault;
case 'set': set = ASet;
case 'null': set = ANull;
case 'dynamic': set = ADynamic;
@@ -909,7 +916,8 @@ class Parser {
checkAccess(get, set, e, t);
nextType = null;
mk(EVar(ident, t, e, nextIsPublic, nextIsStatic, nextIsPrivate, id == "final", nextIsInline, get, set), p1, (e == null) ? tokenMax : pmax(e));
if(isVar) isVar = false;
mk(EVar(ident, t, e, nextIsPublic, nextIsStatic, nextIsPrivate, id == "final", nextIsInline, get, set, oldIsVar), p1, (e == null) ? tokenMax : pmax(e));
case "while":
var econd = parseExpr();
var e = parseExpr();
@@ -960,22 +968,22 @@ class Parser {
push(tk);
mk(EFunction(inf.args, inf.body, name, inf.ret, nextIsPublic, nextIsStatic, nextIsOverride, nextIsPrivate, nextIsFinal, nextIsInline),p1,pmax(inf.body));
case "package":
// ignore package
var tk = token();
push(tk);
packageName = "";
if (tk == TSemicolon)
return mk(EIgnore(false));
if(tk == TSemicolon)
return mk(EPackage(null), p1);
var path = parsePath();
// mk(EPackage(path.join(".")));
packageName = path.join(".");
mk(EIgnore(false));
case "import":
var pkg:String = parsePath().join('.');
ensure(TSemicolon);
push(TSemicolon);
mk(EPackage(pkg), p1);
case "import" | "using":
var isUsing = id == "using";
var oldReadPos = readPos;
var tk = token();
switch( tk ) {
case TPOpen:
if(isUsing) error(ECustom('Expected identifier'),tokenMin,tokenMax);
var tok = token();
switch(tok) {
case TConst(c):
@@ -1001,6 +1009,7 @@ class Parser {
t = token();
if( t != TDot ) {
if(t.match(TId("as"))) {
if(isUsing) error(ECustom('Expected . or ;'),tokenMin,tokenMax);
t = token();
switch( t ) {
case TId(id):
@@ -1025,7 +1034,7 @@ class Parser {
ensure(TSemicolon);
push(TSemicolon);
var p = path.join(".");
mk(EImport(p, asname),p1);
mk(EImport(p, asname, isUsing),p1);
default:
unexpected(tk);
null;
@@ -1112,91 +1121,66 @@ class Parser {
var tk = token();
push(tk);
mk(EClass(name, fields, extend, interfaces, nextIsFinal, nextIsPrivate), p1);
case "typedef":
var name = getIdent();
ensureToken(TOp("="));
var t = parseType();
switch(t) {
case CTAnon(_) | CTFun(_):
/*
Ignore parsing if its,
typedef Name = {
> Person
var name:String;
var age:Int;
}
*/
mk(EIgnore(true));
case CTPath(path, params):
if (params != null && params.length > 1)
error(ECustom("Typedefs can't have parameters"), p1, tokenMax);
if (path.length == 0)
error(ECustom("Typedefs can't be empty"), p1, tokenMax);
var className = path.join(".");
var cl = Tools.getClass(className);
if(cl != null)
return mk(ERedirect(name, className, cl));
var expr = mk(EIdent(path.shift()));
while (path.length > 0) {
expr = mk(EField(expr, path.shift(), false));
}
// todo? add import to the beginning of the file?
mk(EVar(name, null, expr));
default:
error(ECustom("Typedef, unknown type " + t), p1, tokenMax);
null;
}
case "using":
var path = parsePath();
mk(EUsing(path.join(".")));
case "enum":
case "enum": // TODO: enum abstract
var name = getIdent();
ensure(TBrOpen);
var fields = [];
// TODO: optimize this
var fields:Array<EnumField> = [];
var fieldName:String = '';
var enumArgs:Array<Argument> = null;
//var tk = token();
var currentName = "";
var currentArgs: Array<Argument> = null;
while (true) {
while(!maybe(TBrClose)) {
var tk = token();
switch (tk) {
case TBrClose:
break;
case TSemicolon | TComma:
if (currentName == "")
continue;
if (currentArgs != null && currentArgs.length > 0) {
fields.push(EnumType.EConstructor(currentName, currentArgs));
currentArgs = null;
} else {
fields.push(EnumType.ESimple(currentName));
}
currentName = "";
switch(tk) {
//case TBrClose:
// break;
case TSemicolon | TComma:
if(fieldName.trim().length == 0) continue;
fields.push({
name: fieldName,
args: enumArgs == null ? [] : enumArgs
});
fieldName = '';
enumArgs = null;
case TPOpen:
if (currentArgs != null) {
if(enumArgs != null) {
error(ECustom("Cannot have multiple argument lists in one enum constructor"), tokenMin, tokenMax);
break;
}
currentArgs = parseFunctionArgs();
enumArgs = parseFunctionArgs(true);
default:
if (currentName != "") {
if(fieldName.trim().length != 0) {
error(ECustom("Expected comma or semicolon"), tokenMin, tokenMax);
break;
}
var name = extractIdent(tk);
currentName = name;
push(tk);
fieldName = getIdent();
}
}
mk(EEnum(name, fields));
mk(EEnum({ name: name, fields: fields }, false), p1);
case "cast":
var tk = token();
var e:Expr = null;
var t:CType = null; // null if is unsafe cast
switch(tk) {
case TPOpen:
e = parseExpr();
ensure(TComma);
t = parseType();
ensure(TPClose);
default:
push(tk);
e = parseExpr();
}
mk(ECast(e, t), p1);
case "return":
var tk = token();
push(tk);
@@ -1204,12 +1188,47 @@ class Parser {
mk(EReturn(e),p1,if( e == null ) tokenMax else pmax(e));
case "new":
var a = [];
var params:Null<Array<CType>> = null;
a.push(getIdent());
while( true ) {
var tk = token();
switch( tk ) {
case TDot:
a.push(getIdent());
case TOp(op) :
if( op == '<' ) {
params = [];
while(true) {
switch(token()) {
case TConst(c):
params.push(CTExpr(mk(EConst(c))));
case tk:
push(tk);
params.push(parseType());
}
tk = token();
switch(tk) {
case TComma: continue;
case TOp(op):
if( op == ">") break;
if( op.charCodeAt(0) == ">".code ) {
#if hscriptPos
tokens.add({ t : TOp(op.substr(1)), min : tokenMax - op.length - 1, max : tokenMax });
#else
tokens.add(TOp(op.substr(1)));
#end
break;
}
default:
}
unexpected(tk);
break;
}
}
else {
unexpected(tk);
break;
}
case TPOpen:
break;
default:
@@ -1218,7 +1237,7 @@ class Parser {
}
}
var args = parseExprList(TPClose);
mk(ENew(a.join("."), args), p1);
mk(ENew(a.join("."), args, params), p1);
case "throw":
var e = parseExpr();
mk(EThrow(e),p1,pmax(e));
@@ -1348,6 +1367,8 @@ class Parser {
return parseExprNext(mk(EUnop(op,false,e1),pmin(e1)));
}
return makeBinop(op,e1,parseExpr());
case TId(op) if( opPriority.exists(op) ):
return parseExprNext(makeBinop(op,e1,parseExpr()));
case TDot | TQuestionDot:
var field = getIdent();
return parseExprNext(mk(EField(e1, field, tk == TQuestionDot), pmin(e1)));
@@ -1368,7 +1389,7 @@ class Parser {
}
}
function parseFunctionArgs():Array<Argument> {
function parseFunctionArgs(?enumArgs:Bool = false):Array<Argument> {
var args:Array<Argument> = [];
var tk = token();
if( tk != TPClose ) {
@@ -1394,6 +1415,7 @@ class Parser {
if( maybe(TDoubleDot) )
arg.t = parseType();
if( maybe(TOp("="))) {
if(enumArgs) unexpected(TOp("="));
arg.value = parseExpr();
arg.opt = true;
}
@@ -1452,7 +1474,13 @@ class Parser {
if( op == "<" ) {
params = [];
while( true ) {
params.push(parseType());
switch( token() ) {
case TConst(c):
params.push(CTExpr(mk(EConst(c))));
case tk:
push(tk);
params.push(parseType());
}
t = token();
switch( t ) {
case TComma: continue;
@@ -1615,17 +1643,23 @@ class Parser {
#if hscriptPos
var p1 = tokenMin;
#end
if(!allowTypes) {
error(ECustom('"allowTypes" must be enabled for Property fields'), p1, pmax(expr));
return;
}
switch([get, set]) {
case [AGet, ASet] | [AGet, ANever] | [ANever, ASet]:
if(isVar && (expr == null && type == null))
error(ECustom('Property requires type-hint or initialization'), p1, pmax(expr));
if(expr != null && !isVar)
error(ECustom("Attempt to assign on field that is not a real variable"), p1, pmax(expr));
else if(type == null)
error(ECustom("Property requires type-hint"), p1, tokenMax);
else if(type == null && !isVar)
error(ECustom('Property requires type-hint'), p1, tokenMax);
case [ANever, ANever]:
error(ECustom("Unsupported property combination"), p1, (expr == null) ? tokenMax : pmax(expr));
default:
if(expr == null && type == null)
error(ECustom("Property requires type-hint or initialization"), p1, tokenMax);
error(ECustom('Property requires type-hint or initialization'), p1, tokenMax);
}
}
@@ -1644,13 +1678,6 @@ class Parser {
push(tk);
decls.push(parseModuleDecl());
}
haxe.ds.ArraySort.sort(decls, (m1, m2) -> {
var i1 = Type.enumIndex(m1);
var i2 = Type.enumIndex(m2);
if(i1 > i2) return 0;
return -1;
});
return decls;
}
@@ -1697,22 +1724,10 @@ class Parser {
return DPackage(path);
case "import":
var path = [getIdent()];
var asname:String = null;
var star = false; // For now, unused
var star = false;
while( true ) {
var t = token();
if( t != TDot ) {
if (t.match(TId("as"))) {
t = token();
switch (t) {
case TId(id):
asname = id;
default:
unexpected(t);
}
break;
}
push(t);
break;
}
@@ -1720,56 +1735,34 @@ class Parser {
switch( t ) {
case TId(id):
path.push(id);
/* idk if put this work
case TOp("*"):
star = true;
*/
default:
unexpected(t);
}
}
ensure(TSemicolon);
return DImport(path, star, asname);
case "using":
var path = parsePath();
ensure(TSemicolon);
return DUsing(path);
return DImport(path, star);
case "class":
var name = getIdent();
var params = parseParams();
var extend:Null<CType> = null;
var implement:Array<CType> = [];
var extend = null;
var implement = [];
while( true ) {
var t = token();
switch( t ) {
case TId("extends"):
var e = parseType();
switch(e) {
case CTPath(path, params):
if(extend != null) {
error(ECustom('Cannot extend a class twice.'), 0, 0);
}
extend = e;
default:
error(ECustom('${Std.string(e)} is not a valid path.'), 0, 0);
}
extend = parseType();
case TId("implements"):
var i = parseType();
switch(i) {
case CTPath(path, params):
implement.push(i);
default:
error(ECustom('${Std.string(i)} is not a valid interface path.'), 0, 0);
}
//implement.push(parseType());
implement.push(parseType());
default:
push(t);
break;
}
}
var fields:Array<FieldDecl> = [];
var fields = [];
ensure(TBrOpen);
while( !maybe(TBrClose) )
fields.push(parseField());
@@ -1833,42 +1826,17 @@ class Parser {
ret : inf.ret,
}),
};
case "var" | "final":
case "var":
var name = getIdent();
var get = ADefault, set = ADefault;
var hasGetSet:Bool = false;
if(maybe(TPOpen)) {
if(id == "final")
unexpected(TPOpen);
var getId = getIdent();
switch(getId) {
case 'default': // Do nothing
case 'get': get = AGet;
case 'null': get = ANull;
case 'dynamic': get = ADynamic;
case 'never': get = ANever;
default: error(ECustom("Custom property accessor is not supported"), tokenMin, tokenMax);
}
var get = null, set = null;
if( maybe(TPOpen) ) {
get = getIdent();
ensure(TComma);
var setId = getIdent();
switch(setId) {
case 'default': // Do nothing
case 'set': set = ASet;
case 'null': set = ANull;
case 'dynamic': set = ADynamic;
case 'never': set = ANever;
default: error(ECustom("Custom property accessor is not supported"), tokenMin, tokenMax);
}
set = getIdent();
ensure(TPClose);
hasGetSet = true;
}
var type:Null<CType> = maybe(TDoubleDot) ? parseType() : null;
var expr:Null<Expr> = maybe(TOp("=")) ? parseExpr() : null;
if(hasGetSet)
checkAccess(get, set, expr, type);
var type = maybe(TDoubleDot) ? parseType() : null;
var expr = maybe(TOp("=")) ? parseExpr() : null;
if( expr != null ) {
if( isBlock(expr) )
@@ -1887,9 +1855,8 @@ class Parser {
kind : KVar({
get : get,
set : set,
isFinal: id == "final",
type : type,
expr : expr
expr : expr,
}),
};
default:
@@ -1906,7 +1873,7 @@ class Parser {
return StringTools.fastCodeAt(input, readPos++);
}
function readString( until:Int ):String {
function readString( until:Int, regex:Bool = false ):String {
var c = 0;
var b = new StringBuf();
var esc = false;
@@ -1954,7 +1921,7 @@ class Parser {
b.addChar(k);
default: invalidChar(c);
}
} else if( c == 92 )
} else if( c == 92 && !regex)
esc = true;
else if( c == until )
break;
@@ -1966,6 +1933,39 @@ class Parser {
return b.toString();
}
function readFlags():String {
if(!allowRegex) return null;
var c = 0;
var b = new StringBuf();
var old = line;
var s = input;
#if hscriptPos
var p1 = readPos - 1;
#end
while(true) {
var c = readChar();
if( StringTools.isEof(c) ) {
line = old;
error(EUnterminatedString, p1, p1);
break;
}
// semicolon
if(c == 59) {
this.char = c;
break;
}
var f = String.fromCharCode(c);
if(regexFlags.indexOf(f) != -1)
b.addChar(c);
else
invalidChar(c);
}
return b.toString();
}
function token():Token {
//function token(?infos : Null<haxe.PosInfos>) {
//function ttrace(v:Dynamic, ?infos : Null<haxe.PosInfos>) {
@@ -2045,8 +2045,6 @@ class Parser {
}
if( pow == null )
invalidChar(char);
if( exp == 0 )
exp = 10;
return TConst(CFloat((Math.pow(10, pow) / exp) * n * 10));
case ".".code:
if( exp > 0 ) {
@@ -2191,7 +2189,7 @@ class Parser {
invalidChar(char);
default:
if( ops[char] ) {
var op = String.fromCharCode(char);
var op:String = String.fromCharCode(char);
while( true ) {
char = readChar();
if( StringTools.isEof(char) ) char = 0;
@@ -2201,6 +2199,11 @@ class Parser {
}
var pop = op;
op += String.fromCharCode(char);
if( allowRegex && op == "~/") {
var e = readString(char, true);
var f = readFlags();
return TRegex(e, f);
}
if( !opPriority.exists(op) && opPriority.exists(pop) ) {
if( op == "//" || op == "/*" )
return tokenComment(op,char);
@@ -2216,7 +2219,7 @@ class Parser {
if( StringTools.isEof(char) ) char = 0;
if( !idents[char] ) {
this.char = char;
if(id == "is") return TOp("is");
//if(id == "is") return TOp("is");
return TId(id);
}
id += String.fromCharCode(char);
@@ -2415,6 +2418,7 @@ class Parser {
case TDoubleDot: ":";
case TMeta(id): "@" + id;
case TPrepro(id): "#" + id;
case TRegex(e, f): '~/$e/${f == null ? '' : f}';
}
}
+103 -47
View File
@@ -98,6 +98,8 @@ class Printer {
add("(");
type(t);
add(")");
case CTExpr(e):
expr(e);
}
}
@@ -113,29 +115,80 @@ class Printer {
add("??NULL??");
return;
}
switch( Tools.expr(e) ) {
case EIgnore(_):
case EImport(c, n):
add("import " + c);
switch(Tools.expr(e)) {
case EPackage(n):
add('package');
if(n != null)
add(' $n');
add(';\n');
case EImport(c, n, u):
add('${u ? 'using' : 'import'} $c');
if(n != null)
add(' as $n');
case EClass(name, fields, extend, interfaces):
case EClass(name, fields, extend, interfaces, fnal):
var isFinal = fnal != null && fnal;
if(isFinal)
add('final ');
add('class $name');
if (extend != null)
add(' extends $extend');
for(_interface in interfaces) {
add(' implements $_interface');
}
add(' {\n');
tabs += "\t";
add(" {\n");
for( e in fields ) {
add(tabs);
expr(e);
//add(";\n");
}
//for(field in fields) {
// expr(field);
//}
tabs = tabs.substr(1);
add("}");
case ERedirect(n, cl):
add('typedef $n = $cl');
case EEnum(en, _): // TODO: enum abstracts
add('enum ${en.name}');
if(en.fields.length == 0) {
add(' {}');
return;
}
tabs += "\t";
add(" {\n");
for(e in en.fields) {
add(tabs);
add(e.name);
if(e.args.length > 0) {
add("(");
var first = true;
for( a in e.args ) {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(a.name);
addType(a.t);
}
add(')');
}
add(";\n");
}
tabs = tabs.substr(1);
add("}");
case ECast(e, t):
var safe = t != null;
add("cast ");
if(safe) add("(");
expr(e);
if(safe) {
add(", ");
addType(t);
add(")");
}
case ERegex(e, f):
add('~/$e/$f');
add(';\n');
case EConst(c):
switch( c ) {
case CInt(i): add(i);
@@ -144,8 +197,35 @@ class Printer {
}
case EIdent(v):
add(v);
case EVar(n, t, e): // TODO: static, public, override
add("var " + n);
case EVar(n, t, e, p, s, pr, isFinal, isInline, get, set, _):
if(p) add("public ");
else if(pr) add("private ");
if(s) add("static ");
if(isInline) add("inline ");
if(isFinal) add("final " + n);
else add("var " + n);
if(get != null || set != null) {
add("(");
switch(get) {
case ADefault: add("default, ");
case ANull: add("null, ");
case AGet: add("get, ");
case ADynamic: add("dynamic, ");
case ANever: add("never, ");
default:
}
switch(set) {
case ADefault: add("default");
case ANull: add("null");
case ASet: add("set");
case ADynamic: add("dynamic");
case ANever: add("never");
default:
}
add(")");
}
addType(t);
if( e != null ) {
add(" = ");
@@ -185,7 +265,7 @@ class Printer {
case ECall(e, args):
if( e == null )
expr(e);
else switch( #if hscriptPos e.e #else e #end ) {
else switch( Tools.expr(e)) {
case EField(_), EIdent(_), EConst(_):
expr(e);
default:
@@ -267,8 +347,18 @@ class Printer {
expr(e);
}
add("]");
case ENew(cl, args):
add("new " + cl + "(");
case ENew(cl, args, params):
add("new " + cl);
if(params != null) {
add("<");
var first = true;
for( p in params ) {
if( first ) first = false else add(", ");
type(p);
}
add(">");
}
add("(");
var first = true;
for( e in args ) {
if( first ) first = false else add(", ");
@@ -347,43 +437,9 @@ class Printer {
add(" : ");
addType(t);
add(")");
case EUsing(name):
add("using ");
add(name);
case EEnum(name, params):
if (params.length == 0) {
add("enum " + name + " {}");
return;
}
add("enum " + name + " {\n");
tabs += "\t";
for (p in params) {
add(tabs);
switch p {
case EConstructor(name, args):
add(name);
add("(");
for (a in args)
addArgument(a);
add(")");
case ESimple(name):
add(name);
}
add(";\n");
}
tabs = tabs.substr(1);
add(tabs);
add("}");
}
}
function addArgument(a: Argument) {
if (a.opt)
add("?");
add(a.name);
addType(a.t);
}
public static function toString( e : Expr ):String {
return new Printer().exprToString(e);
}
+128
View File
@@ -0,0 +1,128 @@
package hscript;
import hscript.utils.UnsafeReflect;
import hscript.Interp;
import hscript.Expr.FieldPropertyAccess;
/**
* Special variable that handles 'getter/setter' function calls
* depending of the read/write access combination.
*
* Example:
* ```haxe
* public var myvar(get, set):Int;
* var _myvar:Int = 10;
*
* function get_myvar():Int {
* return _myvar;
* }
*
* function set_myvar(val:Int):Int {
* if(val > 10) return _myvar = val;
* return val;
* }
* ```
*
* @see https://haxe.org/manual/class-field-property.html
*/
@:access(hscript.Interp)
@:structInit
class Property {
private static inline var GET = 'get_';
private static inline var SET = 'set_';
public var r:Dynamic;
public var getter:FieldPropertyAccess;
public var setter:FieldPropertyAccess;
public var isStatic(get, never):Bool;
function get_isStatic() {
return __isStatic && interp.allowStaticVariables;
}
var isVar:Bool;
var interp:Interp;
public function new(r:Dynamic, getter:FieldPropertyAccess, setter:FieldPropertyAccess, isVar:Bool, isStatic:Bool, interp:Interp) {
this.r = r;
this.getter = getter;
this.setter = setter;
this.isVar = isVar;
this.__isStatic = isStatic;
this.interp = interp;
}
var __allowReadAccess:Bool = false;
var __allowWriteAccess:Bool = false;
var __allowSetGet:Null<Bool> = null;
final __isStatic:Bool = false;
public function callGetter(name:String) {
switch (getter) {
case AGet | ADynamic:
var fName:String = '$GET$name';
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
if (varExists(fName)) {
return callAccessor(fName);
} else
interp.error(ECustom('Method $fName required by property $name is missing'));
} else {
if ((setter == ADefault || setter == ANull) || isVar)
return r;
else
interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}'));
}
case ANever:
interp.error(ECustom('This expression cannot be accessed for reading'));
default:
}
return r;
}
public function callSetter(name:String, val:Dynamic) {
switch (setter) {
case ASet | ADynamic:
var fName:String = '$SET$name';
if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
if (varExists(fName))
return callAccessor(fName, [val], true);
else
interp.error(ECustom('Method $fName required by property $name is missing'));
} else {
if ((getter == ADefault || getter == ANull) || isVar)
return r = val;
else
interp.error(ECustom('Field $name cannot be accessed because it is not a real variable${interp.isBypassAccessor ? '. Add @:isVar to enable it' : ''}'));
}
case ANever:
interp.error(ECustom('This expression cannot be accessed for writing'));
default:
}
return r = val;
}
private function callAccessor(f:String, ?args:Array<Dynamic>, isWrite:Bool = false):Dynamic {
var fn = isStatic ? interp.staticVariables.get(f) : interp.variables.get(f);
var rt:Dynamic = null;
if (fn != null && Reflect.isFunction(fn)) {
if (isWrite) __allowWriteAccess = true;
else __allowReadAccess = true;
rt = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args);
if (isWrite) __allowWriteAccess = false;
else __allowReadAccess = false;
return rt;
} else
interp.error(ECustom('Method $f required by property ${f.substr(3)} is missing'));
return rt;
}
private function varExists(n:String) {
return isStatic ? interp.staticVariables.exists(n) : interp.variables.exists(n);
}
}
+9 -132
View File
@@ -22,8 +22,6 @@
package hscript;
import hscript.Expr;
using StringTools;
class Tools {
public static function iter( e : Expr, f : Expr -> Void ):Void {
@@ -93,7 +91,7 @@ class Tools {
case ECheckType(e,t): ECheckType(f(e), t);
case EImport(c): EImport(c);
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
default: #if hscriptPos e.e #else e #end;
default: expr(e);
}
return mk(edef, e);
}
@@ -114,147 +112,26 @@ class Tools {
#end
}
@:access(hscript.Interp)
public static function clearClasses() {
#if CUSTOM_CLASSES
Interp._customClasses.clear();
Interp._customClassAliases.clear();
#end
}
public static function removeInnerClass(name: String): String {
var ll = name.lastIndexOf(".");
if (name.indexOf(".") != ll) { // checks if there are 2 or more dots
// wtf did i make -neo
// this is awesome -crow
return name.substr(0, name.lastIndexOf(".", ll - 1)) + "." + name.substr(ll + 1);
}
// only one dot or no dots (will work since -1 + 1 = 0)
return name.substr(ll + 1);
}
// TODO: maybe use this function for import since also check for innerclasses
public static function getClass(name: String): Dynamic {
var splitClassName = [for (e in name.split(".")) e.trim()];
var realClassName = splitClassName.join(".");
var c: Dynamic = Type.resolveClass(realClassName);
if (c == null) // try importing as enum
try
c = Type.resolveEnum(name);
if (c == null) {
// lastly try removing any inner class from it
// this allows you to import stuff like
// flixel.text.FlxText.FlxTextBorderStyle
// without the script crashing immediately
var className = removeInnerClass(realClassName);
if (className != name) {
c = Type.resolveClass(className);
if (c == null)
c = Type.resolveEnum(className);
}
}
return c;
}
public static function isIterable(v: Dynamic): Bool {
// TODO: test for php and lua, they might have issues with this check
return v != null && v.iterator != null;
}
/**
* DO NOT USE INLINE ON THIS FUNCTION
**/
public static function argCount(func: haxe.Constraints.Function): Int {
// https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L206
#if cpp
return untyped __cpp__("{0}->__ArgCount()", func);
#elseif js
return untyped js.Syntax.code("{0}.length", func);
#elseif hl
var ft = hl.Type.getDynamic(func);
if (ft.kind != HFun)
return -1;
return ft.getArgsCount();
#else
return -1;
#end
}
public static function getClassDeclFields(classDecl:hscript.Expr.ClassDecl, onlyStatic:Bool = false):Array<FieldDecl> {
var fields = classDecl.fields.filter(function(field:FieldDecl) {
var isStatic = field.access.contains(AStatic);
return onlyStatic ? isStatic : !isStatic;
});
return fields;
}
public static inline function isCustomAbstract(obj:Dynamic):Bool
return obj != null && obj is IHScriptAbstractBehaviour;
}
/**
* @see https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Tools.hx#L217
*/
class EnumValue {
public var enumName: String;
public var name: String;
public var index: Int;
public var args: Array<Dynamic>;
public var enumParent:Dynamic;
public function new(enumName: String, name: String, index: Int, enumParent:Dynamic, ?args: Array<Dynamic>) {
this.enumName = enumName;
this.name = name;
this.index = index;
this.enumParent = enumParent;
this.args = args;
}
public function toString(): String {
if (args == null)
return enumName + "." + name;
return enumName + "." + name + "(" + [for (arg in args) arg].join(", ") + ")";
}
public inline function getEnumName(): String
return this.enumName;
public inline function getConstructorArgs(): Array<Dynamic>
return this.args != null ? this.args : [];
public function compare(other: EnumValue): Bool {
if (enumName != other.enumName || name != other.name)
return false;
if (args == null && other.args == null)
return true;
if (args == null || other.args == null)
return false;
if (args.length != other.args.length)
return false;
for (i in 0...args.length) {
// TODO: allow deep comparison, like arrays
if (args[i] != other.args[i])
return false;
}
return true;
}
}
class HScriptEnum implements IHScriptCustomBehaviour {
public var enumValues(default, null) = {};
public function new() {}
public function setEnum(name:String, enumValue:Dynamic) {
Reflect.setField(enumValues, name, enumValue);
}
public function getEnumValue(name:String):Null<Dynamic> {
if(Reflect.hasField(enumValues, name)) return Reflect.field(enumValues, name);
return null;
}
public function hget(name:String):Dynamic {
return getEnumValue(name);
}
public function hset(name:String, val:Dynamic):Dynamic {
return null;
}
}
-512
View File
@@ -1,512 +0,0 @@
package hscript.customclass;
import hscript.utils.UnsafeReflect;
import haxe.Constraints.Function;
import hscript.Expr;
import hscript.Expr.FieldPropertyAccess;
import hscript.Expr.VarDecl;
import hscript.Expr.FunctionDecl;
import hscript.Expr.FieldDecl;
using Lambda;
using StringTools;
/**
* The Custom Class core.
*
* Provides handlers for custom classes.
*
* Rest in peace, CustomClassHandler.hx
*
* Based on Polymod Hscript class system
* @see https://github.com/larsiusprime/polymod/tree/master/polymod/hscript/_internal
*/
@:access(hscript.customclass.CustomClassDecl)
class CustomClass implements IHScriptCustomAccessBehaviour {
public var interp:Interp;
public var superClass:Dynamic;
public var superConstructor(default, null):Dynamic;
public var superIsCustomClass(get, never):Bool;
private function get_superIsCustomClass():Bool
return (superClass != null && superClass is CustomClass);
public var className(get, never):String;
private function get_className():String {
return __class.toString();
}
private var __class:CustomClassDecl;
private var __cachedSuperFields:Null<Map<String, Dynamic>> = null;
private var __cachedFieldDecls:Map<String, FieldDecl> = [];
private var __cachedFunctionDecls:Map<String, FunctionDecl> = [];
private var __cachedVarDecls:Map<String, VarDecl> = [];
public var accessContext:AccessContext = COuter;
public var __allowSetGet:Bool = false;
public var __allowPrivateAccess:Bool = false;
private var isInline(default, null):Bool = false;
private var ogVariables(default, null):Map<String, Dynamic>;
private var initializing(default, null):Bool = false; // Allows final variables to be initialized
public function new(__class:CustomClassDecl, args:Array<Dynamic>, ?extendFieldDecl:Map<String, Dynamic>, ?ogInterp:Interp, ?callNew:Bool = true) {
this.__class = __class;
this.interp = new Interp(this);
if (ogInterp != null) {
interp.importFailedCallback = ogInterp.importFailedCallback;
interp.errorHandler = ogInterp.errorHandler;
interp.allowStaticVariables = ogInterp.allowStaticVariables;
interp.staticVariables = ogInterp.staticVariables;
// todo: make it so you can use variables from the same scope as where the class was defined
if(__class.isInline != null && __class.isInline) {
isInline = __class.isInline;
ogVariables = ogInterp.variables;
interp.allowPublicVariables = __class.staticInterp.allowPublicVariables;
interp.publicVariables = __class.staticInterp.publicVariables;
}
}
buildImports();
buildUsings();
if (extendFieldDecl != null)
__cachedSuperFields = extendFieldDecl;
buildClass();
if (hasFunction('new') && callNew) {
buildSuperConstructor();
initializing = true;
callFunction('new', args);
initializing = false;
if (this.superClass == null && this.__class.classDecl.extend != null)
this.interp.error(ECustom("super() not called"));
} else if (__class.classDecl.extend != null) {
createSuperClass(args);
}
}
function buildClass() {
if (__cachedSuperFields == null)
__cachedSuperFields = [];
for (f in __class.classDecl.fields) {
if (f.access.contains(AStatic))
continue; // Skip static field. It's handled by CustomClassDecl.hx
__cachedFieldDecls.set(f.name, f);
switch (f.kind) {
case KFunction(fn):
__cachedFunctionDecls.set(f.name, fn);
#if hscriptPos
var fexpr:Expr = {
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
pmin: fn.body.pmin,
pmax: fn.body.pmax,
line: fn.body.line,
origin: fn.body.origin
};
#else
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
#end
var func:Function = this.interp.expr(fexpr);
this.interp.variables.set(f.name, func);
case KVar(v):
if(v.get != ADefault || v.set != ADefault)
__allowSetGet = true;
__cachedVarDecls.set(f.name, v);
if (v.expr != null) {
var varValue = this.interp.expr(v.expr);
this.interp.variables.set(f.name, varValue);
}
}
}
if (!__cachedSuperFields.empty()) {
for (f => v in __cachedSuperFields) {
this.hset(f, v);
}
__cachedSuperFields.clear();
}
}
function buildSuperConstructor() {
superConstructor = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
createSuperClass(args);
});
}
private function createSuperClass(?args:Array<Dynamic>) {
if (args == null)
args = [];
if(__class.superClassDecl is CustomClassDecl)
superClass = new CustomClass(__class.superClassDecl, args, __cachedSuperFields, this.interp);
else {
if (__cachedSuperFields != null) {
Reflect.setField(__class.superClassDecl, "__cachedFields", __cachedSuperFields); // Static field
}
var disallowCopy = Type.getInstanceFields(__class.superClassDecl);
superClass = Type.createInstance(__class.superClassDecl, args);
superClass.__customClass = this;
superClass.__real_fields = disallowCopy;
if(isInline) {
for(s => v in ogVariables)
if(!disallowCopy.contains(s))
interp.variables.set(s, v);
}
}
}
function buildImports() {
var i:Int = 0;
for (_import in __class.imports) {
var importedClass = _import.fullPath;
var importAlias = _import.as;
if (Interp.customClassExist(importedClass) && this.interp.importFailedCallback != null) {
this.interp.importFailedCallback(importedClass.split("."), importAlias);
continue;
}
#if hscriptPos
var e:Expr = {
e: ExprDef.EImport(importedClass, importAlias),
pmin: 0,
pmax: 0,
origin: this.className,
line: i
};
#else
var e = Expr.EImport(importedClass, importAlias);
#end
this.interp.expr(e);
i++;
}
}
inline function buildUsings() {
for (us in __class.usings) {
@:privateAccess this.interp.useUsing(us);
}
}
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
var r:Dynamic = null;
if (hasField(name)) {
var fn = getFunction(name);
try {
if (fn == null)
interp.error(ECustom('${name} is not a function'));
r = UnsafeReflect.callMethodUnsafe(null, fn, args == null ? [] : args);
} catch (e:hscript.Expr.Error) {
// A script error occurred while executing the custom class function.
// Purge the function from the cache so it is not called again.
purgeFunction(name);
}
}
else {
var fixedArgs = [];
for (a in args) {
if ((a is CustomClass)) {
var customClass:CustomClass = cast(a, CustomClass);
fixedArgs.push(customClass.superClass != null ? customClass.getSuperclass() : customClass);
} else {
fixedArgs.push(a);
}
}
var superFn:Function = null;
if(superClass is CustomClass) {
superFn = cast(superClass, CustomClass).hget(name);
}
else {
var fixedName = '_HX_SUPER__${name}';
superFn = Reflect.field(superClass, fixedName);
}
if (superFn == null || !Reflect.isFunction(superFn)) {
this.interp.error(ECustom('Error while calling function super.${name}(): EInvalidAccess'
+ '\n'
+ 'InvalidAccess error: Super function "${name}" does not exist! Define it or call the correct superclass function.'));
}
r = Reflect.callMethod((superClass is CustomClass) ? null : superClass, superFn, fixedArgs);
}
return r;
}
// Field check
private function hasField(name:String):Bool {
return __cachedFieldDecls.exists(name);
}
private function getField(name:String):FieldDecl {
return __cachedFieldDecls != null ? __cachedFieldDecls.get(name) : null;
}
private function hasVar(name:String):Bool {
return __cachedVarDecls.exists(name);
}
private function getVar(name:String):VarDecl {
return __cachedVarDecls.get(name);
}
private function hasFunction(name:String):Bool {
return __cachedFunctionDecls.exists(name);
}
private function getFunction(name:String):Function {
var fn = this.interp.variables.get(name);
return Reflect.isFunction(fn) ? fn : null;
}
// SuperClass field check
private function cacheSuperField(name:String, value:Dynamic) {
if (__cachedSuperFields != null) {
__cachedSuperFields.set(name, value);
}
}
var __superClassFieldList:Array<String> = null;
public function superHasField(name:String):Bool {
if (superClass == null)
return false;
// Reflect.hasField(this, name) is REALLY expensive so we use a cache.
if (__superClassFieldList == null) {
__superClassFieldList = [];
if(superClass is CustomClass) {
var cls:Null<Dynamic> = superClass;
while(cls != null && cls is CustomClass) {
var currentClass = cast(cls, CustomClass);
var fields = [for(f in currentClass.__cachedFieldDecls.keys()) f];
__superClassFieldList.concat(fields);
var next = currentClass.superClass;
if(next == null)
break;
cls = next;
}
// The last fetched class is a real class
if(!(cls is CustomClass))
__superClassFieldList.concat(Reflect.fields(cls).concat(Type.getInstanceFields(Type.getClass(cls))));
}
else {
var realFields = Reflect.fields(superClass).concat(Type.getInstanceFields(Type.getClass(superClass)));
__superClassFieldList.concat(realFields);
}
}
return __superClassFieldList.indexOf(name) != -1;
}
/**
* Remove a function from the cache.
* This is useful when a function is broken and needs to be skipped.
* @param name The name of the function to remove from the cache.
*/
private function purgeFunction(name:String):Void {
if (__cachedFunctionDecls != null) {
__cachedFunctionDecls.remove(name);
}
}
// Access fields
public function hget(name:String):Dynamic {
switch (name) {
case "superClass": return this.superClass;
case "createSuperClass": return this.createSuperClass;
case "hasFunction": return this.hasFunction;
case "callFunction": return this.callFunction;
default:
if (hasFunction(name)) {
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
return this.callFunction(name, args);
});
return fn;
}
if (hasVar(name)) {
var v = getVar(name);
var getter = v.get;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.className;
default: false;
}
var value:Dynamic = null;
if(getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
throw 'field $name cannot be accessed for reading';
if (__allowSetGet && getter == AGet) {
if (hasFunction('get_$name'))
value = __callGetter(name);
else
throw 'Method get_$name required by property $name is missing';
}
else if (this.interp.variables.exists(name))
value = this.interp.variables.get(name);
else {
if (v.expr != null) {
value = this.interp.expr(v.expr);
this.interp.variables.set(name, value);
}
}
return value;
}
if (this.superClass != null) {
if (superHasField(name)) {
// Anonymous structure as a super class???
if (Type.getClass(this.superClass) == null) {
// Anonymous structure
if (Reflect.hasField(this.superClass, name))
return Reflect.field(this.superClass, name);
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
}
if (this.superClass is CustomClass) {
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
superCustomClass.__allowSetGet = this.__allowSetGet;
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
superCustomClass.accessContext = switch (accessContext) {
case CInner(_): CInner(superCustomClass.className);
default: COuter;
}
return superCustomClass.hget(name);
}
// Real Class
if (__allowSetGet)
Reflect.getProperty(this.superClass, name);
else
Reflect.field(this.superClass, name);
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
}
return null;
}
public function hset(name:String, val:Dynamic):Dynamic {
switch (name) {
default:
if (hasVar(name)) {
var v = getVar(name);
var setter = v.set;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.className;
default: false;
}
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || (v.isFinal && !initializing))
throw 'field $name cannot be accessed for writing';
if (__allowSetGet && setter == ASet) {
if (hasFunction('set_$name'))
return __callSetter(name, val);
else
throw 'Method set_$name required by property $name is missing';
}
this.interp.variables.set(name, val);
}
else if (this.superClass != null) {
if (superHasField(name)) {
if (Type.getClass(this.superClass) == null) {
// Anonymous structure
if (Reflect.hasField(this.superClass, name)) {
Reflect.setField(this.superClass, name, val);
return val;
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '[ANONYMOUS STRUCTURE]'";
}
if (this.superClass is CustomClass) {
var superCustomClass:CustomClass = cast(this.superClass, CustomClass);
superCustomClass.__allowSetGet = this.__allowSetGet;
superCustomClass.__allowPrivateAccess = this.__allowPrivateAccess;
superCustomClass.accessContext = switch (accessContext) {
case CInner(_): CInner(superCustomClass.className);
default: COuter;
}
return superCustomClass.hset(name, val);
}
// Real Class
if (__allowSetGet)
Reflect.setProperty(this.superClass, name, val);
else
Reflect.setField(this.superClass, name, val);
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'";
}
else
throw "field '" + name + "' does not exist in custom class '" + this.className + "'";
}
return val;
}
public function __callGetter(name:String):Dynamic {
__allowSetGet = false;
var r = callFunction('get_$name');
__allowSetGet = true;
return r;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
__allowSetGet = false;
var r = callFunction('set_$name', [val]);
__allowSetGet = true;
return r;
}
/**
* Returns the real superClass if the Custom Class
* extends another Custom Class, and so on until
* it reaches a real class, otherwise it will
* return the last fetched Custom Class
* @return Null<Dynamic>
*/
public function getSuperclass():Null<Dynamic> {
var cls:Null<Dynamic> = this.superClass;
// Check if the superClass is another custom class,
// so it will find for a real class, otherwise
// returns the last super CustomClass parent.
while(cls != null && cls is CustomClass) {
var next = cast(cls, CustomClass).superClass;
if(next == null)
break; // Return the Custom Class itself
cls = next;
}
return cls;
}
}
-290
View File
@@ -1,290 +0,0 @@
package hscript.customclass;
import hscript.Expr.AccessContext;
import hscript.proxy.ProxyType;
import hscript.customclass.utils.FunctionUtils;
import haxe.Constraints.Function;
import hscript.Expr.FieldDecl;
import hscript.Expr.VarDecl;
import hscript.Expr.FunctionDecl;
/**
* The ACTUAL "StaticHandler"
* @author Jamextreme140
*/
@:access(hscript.Interp)
@:structInit
class CustomClassDecl implements IHScriptCustomAccessBehaviour {
public var classDecl:Expr.ClassDecl; //This holds the class instantiation info
public var imports:Map<String, CustomClassImport>;
public var usings:Array<String>;
public var pkg:Null<Array<String>> = null;
public var ogInterp:Null<Interp> = null;
public var isInline:Null<Bool> = null;
public var staticInterp:Interp = new Interp();
public var superClassDecl(default, null):Dynamic = null; //This holds the super class reference.
var _cachedStaticFields:Map<String, FieldDecl> = [];
var _cachedStaticFunctions:Map<String, FunctionDecl> = [];
var _cachedStaticVariables:Map<String, VarDecl> = [];
public var __allowSetGet:Bool = true;
public var __allowPrivateAccess:Bool = false;
public var accessContext:AccessContext = COuter;
public final name:String;
public function new(classDecl:Expr.ClassDecl, imports:Map<String, CustomClassImport>, usings:Array<String>, ?pkg:Array<String>, ?ogInterp:Interp, ?isInline:Bool) {
this.classDecl = classDecl;
this.imports = imports;
this.usings = usings;
this.pkg = pkg;
this.ogInterp = ogInterp;
this.isInline = isInline;
// Cache the classname
var pkg = pkg != null ? '${pkg.join(".")}.' : "";
var className = classDecl.name;
this.name = '$pkg$className';
if(ogInterp != null) {
staticInterp.importFailedCallback = ogInterp.importFailedCallback;
staticInterp.errorHandler = ogInterp.errorHandler;
staticInterp.allowStaticVariables = ogInterp.allowStaticVariables;
staticInterp.staticVariables = ogInterp.staticVariables;
if(isInline != null && isInline) {
// uses public variables from the same scope as where the class was defined
staticInterp.variables = ogInterp.variables;
staticInterp.allowPublicVariables = ogInterp.allowPublicVariables;
staticInterp.publicVariables = ogInterp.publicVariables;
}
}
cacheImports();
processUsings();
cacheFields();
if(classDecl.extend != null)
buildSuperClass();
}
function cacheImports() {
// This will make imported classes available for Static Functions
var i:Int = 0;
for(s => imp in imports) {
var importedClass = imp.fullPath;
var importAlias = imp.as;
if(this.staticInterp.variables.exists(imp.name)) continue; // class is already imported
if (Interp.customClassExist(importedClass) && this.staticInterp.importFailedCallback != null) {
this.staticInterp.importFailedCallback(importedClass.split("."), importAlias);
continue;
}
#if hscriptPos
var e:Expr = {
e: ExprDef.EImport(importedClass, importAlias),
pmin: 0,
pmax: 0,
origin: this.classDecl.name,
line: i
};
i++;
#else
var e = Expr.EImport(importedClass, importAlias);
#end
this.staticInterp.expr(e);
}
}
function cacheFields() {
for (f in classDecl.fields) {
if (f.access.contains(AStatic)) {
_cachedStaticFields.set(f.name, f);
switch (f.kind) {
case KFunction(fn):
_cachedStaticFunctions.set(f.name, fn);
#if hscriptPos
var fexpr:Expr = {
e: ExprDef.EFunction(fn.args, fn.body, f.name, fn.ret, false, false),
pmin: fn.body.pmin,
pmax: fn.body.pmax,
line: fn.body.line,
origin: fn.body.origin
};
#else
var fexpr = Expr.EFunction(fn.args, fn.body, f.name, fn.ret, false, false);
#end
var func:Function = this.staticInterp.expr(fexpr);
this.staticInterp.variables.set(f.name, func);
case KVar(v):
if(v.get != ADefault || v.set != ADefault)
__allowSetGet = true;
_cachedStaticVariables.set(f.name, v);
if (v.expr != null) {
var varValue = this.staticInterp.expr(v.expr);
this.staticInterp.variables.set(f.name, varValue);
}
}
}
}
}
function processUsings() {
for(us in usings) {
this.staticInterp.useUsing(us);
}
}
function buildSuperClass() {
var extendString = new Printer().typeToString(classDecl.extend);
if (this.pkg != null && extendString.indexOf(".") == -1) {
extendString = this.pkg.join(".") + "." + extendString;
}
var cls:Dynamic = Type.resolveClass('${extendString}_HSX');
if(cls == null)
cls = ProxyType.resolveClass(extendString);
superClassDecl = cls;
if(superClassDecl == null)
staticInterp.error(ECustom("could not resolve super class: " + extendString));
}
public function callFunction(name:String, ?args:Array<Dynamic>):Dynamic {
var func:Function = getFunction(name);
return FunctionUtils.callStaticFunction(name, this, staticInterp, func, args != null ? args : []);
}
public function hasField(name:String):Bool {
return _cachedStaticFields.exists(name);
}
private function hasFunction(name:String) {
return _cachedStaticFunctions.exists(name);
}
private function getFunction(name:String):Function {
var fn = this.staticInterp.variables.get(name);
return Reflect.isFunction(fn) ? fn : null;
}
private function hasVar(name:String):Bool {
return _cachedStaticVariables.exists(name);
}
private function getVar(name:String):VarDecl {
return _cachedStaticVariables.get(name);
}
/**
* Remove a function from the cache.
* This is useful when a function is broken and needs to be skipped.
* @param name The name of the function to remove from the cache.
*/
private function purgeFunction(name:String):Void {
if (_cachedStaticFunctions != null) {
_cachedStaticFunctions.remove(name);
}
}
public function hget(name:String):Dynamic {
if(hasVar(name)) {
var v = getVar(name);
var getter = v.get;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.toString();
default: false;
}
var r:Dynamic = null;
if (getter == ANever || getter == ANull && (!isInner || !__allowPrivateAccess))
throw 'field $name cannot be accessed for reading';
if(__allowSetGet && getter == AGet){
if(hasFunction('get_$name'))
r = __callGetter(name);
else
throw 'Method get_$name required by property $name is missing';
}
else if (this.staticInterp.variables.exists(name))
r = this.staticInterp.variables.get(name);
else {
if(v.expr != null) {
r = this.staticInterp.expr(v.expr);
this.staticInterp.variables.set(name, r);
}
}
return r;
}
if(hasFunction(name)) {
// TODO: optimize this
var fn:Function = Reflect.makeVarArgs(function(args:Array<Dynamic>) {
return this.callFunction(name, args);
});
return fn;
}
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
}
public function hset(name:String, val:Dynamic):Dynamic {
if (hasVar(name)) {
var v = getVar(name);
var setter = v.set;
var isInner = switch (accessContext) {
case CInner(cl): return cl == this.toString();
default: false;
}
if (setter == ANever || setter == ANull && (!isInner || !__allowPrivateAccess) || v.isFinal)
throw 'field $name cannot be accessed for writing';
if (__allowSetGet && setter == ASet) {
if (hasFunction('set_$name'))
return __callSetter(name, val);
else
throw 'Method set_$name required by property $name is missing';
}
else {
this.staticInterp.variables.set(name, val);
return val;
}
}
throw "static field '" + name + "' does not exist in custom class '" + this.classDecl.name + "'";
}
public function __callGetter(name:String):Dynamic {
__allowSetGet = false;
var r = callFunction('get_${name}');
__allowSetGet = true;
return r;
}
public function __callSetter(name:String, val:Dynamic):Dynamic {
__allowSetGet = false;
var r = callFunction('set_${name}', [val]);
__allowSetGet = true;
return r;
}
public function toString():String {
return name;
}
}
typedef CustomClassImport = {
var ?name:String;
var ?pkg:Array<String>;
var ?fullPath:String; // pkg.pkg.pkg.name
var ?as:Null<String>; // import pkg.Name as OtherName
}
@@ -1,25 +0,0 @@
package hscript.customclass.utils;
import hscript.utils.UnsafeReflect;
import haxe.Constraints.Function;
@:access(hscript.customclass.CustomClassDecl)
class FunctionUtils {
public static inline function callStaticFunction(name:String, classDecl:CustomClassDecl, interp:Interp, fn:Function, args:Array<Dynamic> = null) {
var r:Dynamic = null;
try {
if (fn == null)
interp.error(ECustom('${name} is not a function'));
r = UnsafeReflect.callMethodUnsafe(null, fn, args);
} catch (e:hscript.Expr.Error) {
// A script error occurred while executing the custom class function.
// Purge the function from the cache so it is not called again.
classDecl.purgeFunction(name);
interp.error(#if hscriptPos e.e #else e #end);
}
return r;
}
}
+132 -91
View File
@@ -10,10 +10,10 @@ import haxe.macro.Type.FieldKind;
import haxe.macro.Type.ClassField;
import haxe.macro.Type.VarAccess;
import haxe.macro.*;
import Sys;
using StringTools;
// BIG TODO: make typed classes scriptable
class ClassExtendMacro {
public static inline final FUNC_PREFIX = "_HX_SUPER__";
public static inline final CLASS_SUFFIX = "_HSX";
@@ -29,6 +29,7 @@ class ClassExtendMacro {
for(apply in Config.ALLOWED_CUSTOM_CLASSES) {
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.ClassExtendMacro.build())");
}
//Context.onAfterTyping(buildTyped);
#end
#end
}
@@ -61,6 +62,7 @@ class ClassExtendMacro {
if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier
if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined
if(fkey == "hscript.CustomClassHandler.CustomTemplateClass") return fields; // Error: Redefined
if(fkey == "hscript.CustomClass") 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
@@ -254,41 +256,27 @@ class ClassExtendMacro {
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});
}
if (__interp != null && __class__fields.contains(name)) {
var v:Dynamic = null;
if (Reflect.isFunction(v = __interp.variables.get(name))) {
return v($a{arguments});
}
}
*/
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
return __customClass.callFunction(name, [$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;
}
if (__interp != null && __class__fields.contains(name)) {
var v:Dynamic = null;
if (Reflect.isFunction(v = __interp.variables.get(name))) {
v($a{arguments});
return;
}
}
*/
if (__customClass != null && @:privateAccess __customClass.hasFunction(name)) {
__customClass.callFunction(name, [$a{arguments}]);
return;
}
super.$name($a{arguments});
};
}
@@ -351,7 +339,6 @@ class ClassExtendMacro {
pack: cl.pack.copy(),
name: cl.name
}, [
{name: "IHScriptCustomBehaviour", pack: ["hscript"]},
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
], false, true, false);
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
@@ -359,28 +346,21 @@ class ClassExtendMacro {
Utils.setupMetas(shadowClass, imports);
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
// Adding hscript getters and setters
shadowClass.fields.push({
name: "__cachedFields",
name: "__cachedFieldSet",
pos: Context.currentPos(),
kind: FVar(macro: Map<String, Dynamic>),
access: [APublic, AStatic]
});
// Adding hscript getters and setters
/*
shadowClass.fields.push({
name: "__interp",
pos: Context.currentPos(),
kind: FVar(macro: hscript.Interp),
access: [APublic]
});
*/
shadowClass.fields.push({
name: "__customClass",
pos: Context.currentPos(),
kind: FVar(macro: hscript.customclass.CustomClass),
access: [APublic]
});
/*
shadowClass.fields.push({
name: "__custom__variables",
@@ -388,30 +368,28 @@ class ClassExtendMacro {
kind: FVar(macro: Map<String, Dynamic>),
access: [APublic]
});
*/
shadowClass.fields.push({
name: "__allowSetGet",
pos: Context.currentPos(),
kind: FVar(macro: Bool, macro true),
access: [APublic]
});
*/
shadowClass.fields.push({
name: "__real_fields",
pos: Context.currentPos(),
kind: FVar(macro: Array<String>),
access: [APublic]
});
/*
shadowClass.fields.push({
name: "__class__fields",
pos: Context.currentPos(),
kind: FVar(macro: Array<String>),
access: [APublic]
});
*/
// Unneccessary since the get/set call is already handled.
/*
shadowClass.fields.push({
name: "__callGetter",
pos: Context.currentPos(),
@@ -419,10 +397,7 @@ class ClassExtendMacro {
ret: macro: Dynamic,
params: [],
expr: macro {
__allowSetGet = false;
var v = __custom__variables.get("get_" + name)();
__allowSetGet = true;
return v;
return null;
},
args: [
{
@@ -443,10 +418,7 @@ class ClassExtendMacro {
ret: macro: Dynamic,
params: [],
expr: macro {
__allowSetGet = false;
var v = __custom__variables.get("set_" + name)(val);
__allowSetGet = true;
return v;
return null;
},
args: [
{
@@ -465,7 +437,7 @@ class ClassExtendMacro {
}),
access: [APublic]
});
*/
// Todo: make it possible to override
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
Context.defineModule(cl.module, [shadowClass], imports);
@@ -508,43 +480,81 @@ class ClassExtendMacro {
var hgetField = if(hasHgetInSuper) {
macro {
/*
if(__custom__variables != null) {
if(__allowSetGet && __custom__variables.exists("get_" + name))
return __callGetter(name);
if (__custom__variables.exists(name))
return __custom__variables.get(name);
if (__interp != null) {
if(__class__fields.contains(name)) {
var v:Dynamic = __interp.variables.get(name);
if(v != null && v is hscript.Property)
return cast(v, hscript.Property).callGetter(name);
return v;
}
else @:privateAccess {
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
while(cls != null) {
if(cls.hasField(name))
return cls.getField(name);
var prev:hscript.CustomClass = cast cls.__upperClass;
if(prev == null)
break;
cls = prev;
}
}
}
*/
return super.hget(name);
}
} else {
macro {
/*
if(__custom__variables != null) {
if(__allowSetGet && __custom__variables.exists("get_" + name))
return __callGetter(name);
if (__custom__variables.exists(name))
return __custom__variables.get(name);
if (__interp != null) {
if(__class__fields.contains(name)) {
var v:Dynamic = __interp.variables.get(name);
if(v != null && v is hscript.Property)
return cast(v, hscript.Property).callGetter(name);
return v;
}
else @:privateAccess {
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
while(cls != null) {
if(cls.hasField(name))
return cls.getField(name);
var prev:hscript.CustomClass = cast cls.__upperClass;
if(prev == null)
break;
cls = prev;
}
}
}
*/
return UnsafeReflect.getProperty(this, name);
}
}
var hsetField = if(hasHsetInSuper) {
macro {
/*
if(__custom__variables != null) {
if(__allowSetGet && __custom__variables.exists("set_" + name))
return __callSetter(name, val);
if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
if (__interp != null) {
if(__class__fields.contains(name)) {
var v:Dynamic = __interp.variables.get(name);
if(v != null && v is hscript.Property)
return cast(v, hscript.Property).callSetter(name, val);
__interp.variables.set(name, val);
return val;
}
else @:privateAccess {
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
while(cls != null) {
if(cls.hasField(name))
return cls.setField(name, val);
var prev:hscript.CustomClass = cast cls.__upperClass;
if(prev == null)
break;
cls = prev;
}
}
}
*/
if(__real_fields != null && __real_fields.contains(name)) {
if(__real_fields.contains(name)) {
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
@@ -552,21 +562,33 @@ class ClassExtendMacro {
}
} else {
macro {
/*
if(__custom__variables != null) {
if(__allowSetGet && __custom__variables.exists("set_" + name))
return __callSetter(name, val);
if (__custom__variables.exists(name)) {
__custom__variables.set(name, val);
if (__interp != null) {
if(__class__fields.contains(name)) {
var v:Dynamic = __interp.variables.get(name);
if(v != null && v is hscript.Property)
return cast(v, hscript.Property).callSetter(name, val);
__interp.variables.set(name, val);
return val;
}
else @:privateAccess {
var cls:hscript.CustomClass = cast __interp.__customClass.__upperClass;
while(cls != null) {
if(cls.hasField(name))
return cls.setField(name, val);
var prev:hscript.CustomClass = cast cls.__upperClass;
if(prev == null)
break;
cls = prev;
}
}
}
*/
if(__real_fields != null && __real_fields.contains(name)) {
if(__real_fields.contains(name)) {
UnsafeReflect.setProperty(this, name, val);
return UnsafeReflect.field(this, name);
}
//if(__custom__variables != null) __custom__variables.set(name, val);
//__custom__variables.set(name, val);
return val;
}
}
@@ -618,7 +640,7 @@ class ClassExtendMacro {
]
})
});
/*var p = new Printer();
var aa = p.printTypeDefinition(shadowClass);
if(aa.length < 5024)
@@ -641,22 +663,41 @@ class ClassExtendMacro {
pos: Context.currentPos(),
kind: FFun({
args: constArgs,
expr: macro
{
expr: macro {
// Call the super constructor with appropriate args
super($a{superCallArgs});
if(__cachedFields != null) {
for(k => v in __cachedFields) {
if(__cachedFieldSet != null) {
for(k => v in __cachedFieldSet) {
Reflect.setProperty(this, k, v);
trace(k);
}
__cachedFields = null;
__cachedFieldSet.clear();
__cachedFieldSet = null;
}
},
}
}),
};
}
static function buildTyped(modules:Array<haxe.macro.Type.ModuleType>) {
for(m in modules) {
switch(m) {
case TClassDecl(c):
var cl = c.get();
if (cl.isAbstract || cl.isExtern || cl.isFinal || cl.isInterface)
continue;
if (cl.params.length == 0)
continue;
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith(CLASS_SUFFIX) && !cl.name.endsWith("_HSC"))
buildTypedClass(cl);
default:
}
}
}
static function buildTypedClass(cl:ClassType) {}
static function buildShadowClass(cl:ClassType) {}
}
#else
class ClassExtendMacro {
-173
View File
@@ -1,173 +0,0 @@
package hscript.macros;
#if macro
import Type.ValueType;
import haxe.macro.Compiler;
import haxe.macro.Context;
import haxe.macro.Expr;
using Lambda;
using StringTools;
using haxe.macro.Tools;
/**
* Macro used for the `using Class;` keyword
*
* you can make classes be able to be used specifing the classes/packages on Config.hx!
* or implementing the interface `hscript.utils.UsingClass`
* ```haxe
* public static final ALLOWED_USING = ["my.pack.VeryNiceTools"];
* ```
*
* Usage:
*
* ```haxe
* package my.pack;
* // @:usableEntry() // optional
* // @:usableEntry(forceAny) // optional // forces the class to be called with any type
* // @:usableEntry(onlyBasic) // optional // only basic types will be allowed
* // @:usableEntry(onlyBasic, forceAny) // optional // only basic types will be allowed, and the class will be called with any type
* class VeryNiceTools implements hscript.utils.UsingClass {}
* ```
*
* @author NeeEoo
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/macro/UsingMacro.macro.hx
**/
class UsingMacro {
public static inline final USING_PREFIX = "_HX_USING__";
public static var unallowedMetas:Array<String> = [":noUsing", ":noUse"];
public static function init() {
#if !display
if(Context.defined("display")) return;
for(apply in Config.ALLOWED_USING) {
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.UsingMacro.build())");
}
#end
}
public static function build() {
var cls: haxe.macro.Type.ClassType = Context.getLocalClass().get();
var fields = Context.getBuildFields();
var key = cls.module;
var fkey = cls.module + "." + cls.name;
var packName = (cls.pack.length > 0 ? cls.pack.join(".") + "." : "") + cls.name;
if(cls.meta.get().find(function(m) return m.name == ':usingProcessed') != null) return fields;
if (Config.DISALLOW_USING.contains(key) || Config.DISALLOW_USING.contains(fkey) || Config.DISALLOW_USING.contains(packName))
return fields;
var entryField = cls.meta.get().find(function(m) return m.name == ':usableEntry');
var hasParams = entryField != null && entryField.params != null;
var forceAny = false;
var onlyBasic = false;
if (hasParams) {
for (i in 0...entryField.params.length) {
if (entryField.params[i].expr.match(EConst(CIdent("onlyBasic"))))
onlyBasic = true;
if (entryField.params[i].expr.match(EConst(CIdent("forceAny"))))
forceAny = true;
}
}
var data: Array<Array<String>> = [];
for (field in fields) {
// functions marked with @:noUsing won't be able to be used by variables
// also if you want it to be usable in source, but not in the script, use @:noUse
for(m in field.meta)
if(unallowedMetas.contains(m.name))
continue;
// It's called "static extensions" for some reason
if(!field.access.contains(AStatic))
continue;
switch (field.kind) {
default:
case FFun(f):
if (f.args.length == 0)
continue;
var arg = f.args[0];
if (arg.type == null)
continue;
var type = arg.type;
var valueType: String = switch (type) {
case TPath({name: "Int", pack: []}):
"TInt";
case TPath({name: "Float", pack: []}):
"TFloat";
case TPath({name: "Single", pack: []}):
"TFloat";
case TPath({name: "String", pack: []}):
"TClass(String)";
case TPath({name: "Bool", pack: []}):
"TBool";
case TPath({name: "Array", pack: []}):
"TClass(Array)";
case TPath({name: "Map", pack: []}):
"TClass(haxe.Constraints.IMap)";
case TPath({name: "Dynamic", pack: []}):
null;
case TPath({name: "Class", pack: []}):
"TClass(null)"; // this feels wrong
case TPath({name: "Enum", pack: []}):
"TEnum(null)";
// case TPath({name: "Void", pack: []}): "ValueType.TVoid";
default:
null; // null acts as a wildcard
}
// MIGHT CRASH COMPILATION? YEAH, IT CRASH ON TYPE PARAMETERS
if (!onlyBasic && valueType == null) {
var rtype:haxe.macro.Type = null;
try {rtype = type.toType();} catch(e) {}
if(rtype != null) {
switch (rtype) {
case TInst(t, []):
valueType = "TClass(" + t.toString() + ")";
default:
}
}
}
if (forceAny) {
valueType = null;
}
data.push([field.name, valueType]);
}
}
if(data.length == 0)
return fields;
fields.push({
name: '$USING_PREFIX${packName.replace(".", "_")}',
access: [APrivate, AStatic],
kind: FVar(macro : Map<String, Type.ValueType>, {
var arr: Array<Expr> = [];
for (i in data)
if (i[1] != null)
arr.push(macro $v{i[0]} => ${Context.parse("Type.ValueType." + i[1], Context.currentPos())});
else
arr.push(macro $v{i[0]} => null);
macro $a{arr};
}),
pos: cls.pos,
});
//var printer = new haxe.macro.Printer();
//trace(printer.printField(fields[fields.length - 1]));
cls.meta.add(':usingProcessed', [], cls.pos);
return fields;
}
}
#end
-364
View File
@@ -1,364 +0,0 @@
package hscript.proxy;
import hscript.customclass.CustomClass;
import hscript.customclass.CustomClassDecl;
import hscript.Tools.HScriptEnum;
import hscript.Tools.EnumValue as HScriptEnumValue;
abstract OneOfTwo<T1, T2>(Dynamic) from T1 from T2 to T1 to T2 {}
@:allow(hscript.customclass.CustomClass)
@:allow(hscript.customclass.CustomClassDecl)
class ProxyType {
/**
Returns the class of `o`, if `o` is a class instance.
If `o` is null or of a different type, null is returned.
In general, type parameter information cannot be obtained at runtime.
**/
inline static function getClass<T>(o:OneOfTwo<T, CustomClass>):Null<Dynamic> {
if(o is CustomClass)
@:privateAccess return cast(o, CustomClass).__class;
return Type.getClass(cast o);
}
/**
Returns the enum of enum instance `o`.
An enum instance is the result of using an enum constructor. Given an
`enum Color { Red; }`, `getEnum(Red)` returns `Enum<Color>`.
If `o` is null, null is returned.
In general, type parameter information cannot be obtained at runtime.
**/
inline static function getEnum(o:OneOfTwo<EnumValue, HScriptEnumValue>):Dynamic {
// TODO: SUPPORT HSCRIPT ENUMS
var isScripted:Bool = Std.isOfType(o, HScriptEnumValue);
if(isScripted) {
return cast(o, HScriptEnumValue).enumParent;
}
return Type.getEnum(cast o);
}
/**
Returns the super-class of class `c`.
If `c` has no super class, null is returned.
If `c` is null, the result is unspecified.
In general, type parameter information cannot be obtained at runtime.
**/
inline static function getSuperClass(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Dynamic {
if(c is CustomClassDecl) {
return cast(c, CustomClassDecl).superClassDecl;
}
return Type.getSuperClass(cast c);
}
/**
Returns the name of class `c`, including its path.
If `c` is inside a package, the package structure is returned dot-
separated, with another dot separating the class name:
`pack1.pack2.(...).packN.ClassName`
If `c` is a sub-type of a Haxe module, that module is not part of the
package structure.
If `c` has no package, the class name is returned.
If `c` is null, the result is unspecified.
The class name does not include any type parameters.
**/
inline static function getClassName(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):String {
if(c is CustomClassDecl) {
var cls:CustomClassDecl = cast c;
var name = cls.classDecl.name;
var pkg = cls.pkg != null ? '${cls.pkg.join(".")}.' : "";
return '$pkg$name';
}
return Type.getClassName(cast c);
}
/**
Returns the name of enum `e`, including its path.
If `e` is inside a package, the package structure is returned dot-
separated, with another dot separating the enum name:
`pack1.pack2.(...).packN.EnumName`
If `e` is a sub-type of a Haxe module, that module is not part of the
package structure.
If `e` has no package, the enum name is returned.
If `e` is null, the result is unspecified.
The enum name does not include any type parameters.
**/
inline static function getEnumName(e:Enum<Dynamic>):String {
var isScripted:Bool = Std.isOfType(e, HScriptEnumValue);
if (isScripted)
return cast(e, HScriptEnumValue).getEnumName();
return Type.getEnumName(e);
}
/**
Resolves a class by name.
If `name` is the path of an existing class, that class is returned.
Otherwise null is returned.
If `name` is null or the path to a different type, the result is
unspecified.
The class name must not include any type parameters.
**/
inline static function resolveClass(name:String):Dynamic {
if(Interp.customClassExist(name)) {
return Interp.getCustomClass(name);
}
return Type.resolveClass(name);
}
/**
Resolves an enum by name.
If `name` is the path of an existing enum, that enum is returned.
Otherwise null is returned.
If `name` is null the result is unspecified.
If `name` is the path to a different type, null is returned.
The enum name must not include any type parameters.
**/
inline static function resolveEnum(name:String):Enum<Dynamic> {
return Type.resolveEnum(name);
}
/**
Creates an instance of class `cl`, using `args` as arguments to the
class constructor.
This function guarantees that the class constructor is called.
Default values of constructors arguments are not guaranteed to be
taken into account.
If `cl` or `args` are null, or if the number of elements in `args` does
not match the expected number of constructor arguments, or if any
argument has an invalid type, or if `cl` has no own constructor, the
result is unspecified.
In particular, default values of constructor arguments are not
guaranteed to be taken into account.
**/
inline static function createInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>, args:Array<Dynamic>):T {
// TODO: somehow pass the ogInterp argument without breaking the original functionality
if(cl is CustomClassDecl) {
return cast new CustomClass(cast cl, args);
}
return Type.createInstance(cast cl, args);
}
/**
Creates an instance of class `cl`.
This function guarantees that the class constructor is not called.
If `cl` is null, the result is unspecified.
**/
inline static function createEmptyInstance<T>(cl:OneOfTwo<Class<T>, CustomClassDecl>):T {
// TODO: same as above
if(cl is CustomClassDecl)
return cast new CustomClass(cast cl, [], null, null, false);
return Type.createEmptyInstance(cast cl);
}
/**
Creates an instance of enum `e` by calling its constructor `constr` with
arguments `params`.
If `e` or `constr` is null, or if enum `e` has no constructor named
`constr`, or if the number of elements in `params` does not match the
expected number of constructor arguments, or if any argument has an
invalid type, the result is unspecified.
**/
inline static function createEnum<T>(e:OneOfTwo<Enum<T>, HScriptEnum>, constr:String, ?params:Array<Dynamic>):T {
// TODO: SUPPORT HSCRIPT ENUMS
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
if(isScripted) {
var scriptEnum:HScriptEnum = cast e;
var enumToCreate = scriptEnum.getEnumValue(constr);
if(Reflect.isFunction(enumToCreate)) {
return Reflect.callMethod(null, enumToCreate, params);
}
return enumToCreate;
}
return Type.createEnum(cast e, constr, params);
}
/**
Creates an instance of enum `e` by calling its constructor number
`index` with arguments `params`.
The constructor indices are preserved from Haxe syntax, so the first
declared is index 0, the next index 1 etc.
If `e` or `constr` is null, or if enum `e` has no constructor named
`constr`, or if the number of elements in `params` does not match the
expected number of constructor arguments, or if any argument has an
invalid type, the result is unspecified.
**/
inline static function createEnumIndex<T>(e:Enum<T>, index:Int, ?params:Array<Dynamic>):T {
// TODO: SUPPORT HSCRIPT ENUMS
return Type.createEnumIndex(e, index, params);
}
/**
Returns a list of the instance fields of class `c`, including
inherited fields.
This only includes fields which are known at compile-time. In
particular, using `getInstanceFields(getClass(obj))` will not include
any fields which were added to `obj` at runtime.
The order of the fields in the returned Array is unspecified.
If `c` is null, the result is unspecified.
**/
inline static function getInstanceFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
if(c is CustomClassDecl) {
var cls:CustomClassDecl = cast c;
return [for(f in Tools.getClassDeclFields(cls.classDecl)) f.name];
}
return Type.getInstanceFields(cast c);
}
/**
Returns a list of static fields of class `c`.
This does not include static fields of parent classes.
The order of the fields in the returned Array is unspecified.
If `c` is null, the result is unspecified.
**/
inline static function getClassFields(c:OneOfTwo<Class<Dynamic>, CustomClassDecl>):Array<String> {
if(c is CustomClassDecl) {
var cls:CustomClassDecl = cast c;
return [for(f in Tools.getClassDeclFields(cls.classDecl, true)) f.name];
}
return Type.getClassFields(cast c);
}
/**
Returns a list of the names of all constructors of enum `e`.
The order of the constructor names in the returned Array is preserved
from the original syntax.
If `e` is null, the result is unspecified.
**/
inline static function getEnumConstructs(e:OneOfTwo<Enum<Dynamic>, HScriptEnum>):Array<String> {
// TODO: SUPPORT HSCRIPT ENUMS
return Type.getEnumConstructs(cast e);
}
/**
Returns the runtime type of value `v`.
The result corresponds to the type `v` has at runtime, which may vary
per platform. Assumptions regarding this should be minimized to avoid
surprises.
**/
inline static function typeof(v:Dynamic):Type.ValueType {
return Type.typeof(v);
}
/**
Recursively compares two enum instances `a` and `b` by value.
Unlike `a == b`, this function performs a deep equality check on the
arguments of the constructors, if exists.
If `a` or `b` are null, the result is unspecified.
**/
inline static function enumEq(a:Dynamic, b:Dynamic):Bool {
var isScripted:Bool = Std.isOfType(a, HScriptEnumValue) && Std.isOfType(b, HScriptEnumValue);
if (isScripted)
return cast(a, HScriptEnumValue).compare(cast(b, HScriptEnumValue));
return Type.enumEq(a, b);
}
/**
Returns the constructor name of enum instance `e`.
The result String does not contain any constructor arguments.
If `e` is null, the result is unspecified.
**/
inline static function enumConstructor(e:Dynamic):String {
if (Std.isOfType(e, HScriptEnumValue))
return cast(e, HScriptEnumValue).name;
return Type.enumConstructor(e);
}
/**
Returns a list of the constructor arguments of enum instance `e`.
If `e` has no arguments, the result is [].
Otherwise the result are the values that were used as arguments to `e`,
in the order of their declaration.
If `e` is null, the result is unspecified.
**/
inline static function enumParameters(e:Dynamic):Array<Dynamic> {
if (Std.isOfType(e, HScriptEnumValue))
return cast(e, HScriptEnumValue).args;
return Type.enumParameters(e);
}
/**
Returns the index of enum instance `e`.
This corresponds to the original syntactic position of `e`. The index of
the first declared constructor is 0, the next one is 1 etc.
If `e` is null, the result is unspecified.
**/
inline static function enumIndex(e:Dynamic):Int {
if (Std.isOfType(e, HScriptEnumValue))
return cast(e, HScriptEnumValue).index;
return Type.enumIndex(e);
}
/**
Returns a list of all constructors of enum `e` that require no
arguments.
This may return the empty Array `[]` if all constructors of `e` require
arguments.
Otherwise an instance of `e` constructed through each of its non-
argument constructors is returned, in the order of the constructor
declaration.
If `e` is null, the result is unspecified.
**/
inline static function allEnums<T>(e:OneOfTwo<Enum<T>, HScriptEnum>):Array<T> {
// TODO: SUPPORT HSCRIPT ENUMS
var isScripted:Bool = Std.isOfType(e, HScriptEnum);
if(isScripted) {
var hEnum:HScriptEnum = cast e;
return [for(en in Reflect.fields(hEnum.enumValues)) Reflect.field(hEnum, en)];
}
return Type.allEnums(cast e);
}
}
-9
View File
@@ -1,9 +0,0 @@
package hscript.utils;
/**
* This is used to mark classes that can be used with the `using` keyword.
* You can also add @:usableEntry to your class.
* If you wanna force the class to be called with any type, you can add @:usableEntry(forceAny)
**/
@:autoBuild(hscript.macros.UsingMacro.build())
interface UsingClass {}
-17
View File
@@ -1,17 +0,0 @@
package hscript.utils;
typedef UsingCall = (o: Dynamic, f: String, args: Array<Dynamic>) -> Dynamic;
/**
* Code based on Using system from "hscript-iris"
* @see https://github.com/pisayesiwsi/hscript-iris/blob/master/crowplexus/iris/utils/UsingEntry.hx
*/
class UsingEntry {
public var name: String;
public var call: UsingCall;
public function new(name: String, call: UsingCall) {
this.name = name;
this.call = call;
}
}
+75 -38
View File
@@ -1,45 +1,82 @@
package hscript.utils;
import hscript.utils.UsingEntry.UsingCall;
import StringTools;
import Lambda;
@:structInit
class UsingEntry {
public var call:Dynamic->String->Array<Dynamic>->Dynamic;
public var fields:Array<String>;
class UsingHandler {
public static var usingEntries:Array<UsingEntry> = [
new UsingEntry("StringTools", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
if (f == "isEof") // has @:noUsing
return null;
switch (Type.typeof(o)) {
case TInt if (f == "hex"):
return StringTools.hex(o, args[0]);
case TClass(String):
if (Reflect.hasField(StringTools, f)) {
var field = Reflect.field(StringTools, f);
if (Reflect.isFunction(field)) {
return Reflect.callMethod(StringTools, field, [o].concat(args));
}
}
default:
}
return null;
}),
new UsingEntry("Lambda", function(o: Dynamic, f: String, args: Array<Dynamic>): Dynamic {
if (Tools.isIterable(o)) {
// TODO: Check if the values are Iterable<T>
if (Reflect.hasField(Lambda, f)) {
var field = Reflect.field(Lambda, f);
if (Reflect.isFunction(field)) {
return Reflect.callMethod(Lambda, field, [o].concat(args));
}
public function hasField(name:String) {
return fields.contains(name);
}
}
/**
* Special class that handles static extension function calls.
*
* A static extension allows pseudo-extending
* existing types without modifying their source.
* In Haxe this is achieved by declaring a static method with a first argument
* of the extending type and then bringing the defining class into context through `using`.
*
* Example:
* ```haxe
* class IntExtender {
* static public function triple(i:Int) {
* return i * 3;
* }
* }
*
* using IntExtender;
*
* trace(12.triple()); // 36
* ```
*
* @see https://haxe.org/manual/lf-static-extension.html
*/
class UsingHandler {
// Predefined static extension classes
public static final defaultExtension:Map<String, UsingEntry> = [
"StringTools" => { // https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/iris/Iris.hx#L45
fields: Type.getClassFields(StringTools),
call: function(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
if (f == "isEof") // has @:noUsing
return null;
return switch (Type.typeof(o)) {
case TInt if (f == 'hex'):
StringTools.hex(o, args[0]);
case TClass(String):
var field = UnsafeReflect.field(StringTools, f);
if (UnsafeReflect.isFunction(field)) UnsafeReflect.callMethodUnsafe(StringTools, field, [o].concat(args)); else null;
default:
null;
}
}
return null;
}),
];
},
"Lambda" => { // https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/iris/Iris.hx#L62
fields: Type.getClassFields(Lambda),
call: function(o:Dynamic, f:String, args:Array<Dynamic>):Dynamic {
if (o != null && o.iterator != null) {
var field = UnsafeReflect.field(Lambda, f);
if (UnsafeReflect.isFunction(field)) {
return UnsafeReflect.callMethodUnsafe(Lambda, field, [o].concat(args));
}
}
return null;
}
}
];
public static function registerUsingGlobal(name: String, call:UsingCall):UsingEntry {
var entry = new UsingEntry(name, call);
usingEntries.push(entry);
return entry;
@:allow(hscript.CustomClass)
@:allow(hscript.CustomClassHandler)
public var usingEntries(default, null):Map<String, UsingEntry> = [];
public function new() {}
public function registerEntry(name:String, entry:Dynamic->String->Array<Dynamic>->Dynamic, fields:Array<String>) {
usingEntries.set(name, {call: entry, fields: fields});
}
}
public function entryExists(name:String):Bool {
return usingEntries.exists(name);
}
}