Compare commits
29
Commits
@@ -1,6 +1,8 @@
|
||||
hscript-improved
|
||||
=======
|
||||
|
||||
- [Features](docs/FEATURES.md)
|
||||
|
||||
How to install
|
||||
```
|
||||
haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
|
||||
@@ -18,7 +20,10 @@ or set this in build.hxml
|
||||
-D CUSTOM_CLASSES
|
||||
```
|
||||
|
||||
Warning: custom class system is still in development, would like help to test it. 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. Also you cannot create custom classes that extends a typed class (those ones that has `<T>`), this will be implemented in the future.
|
||||
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.
|
||||
|
||||
-----------
|
||||
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
--macro keep('IntIterator')
|
||||
--macro hscript.macros.AbstractHandler.init()
|
||||
--macro hscript.macros.ClassExtendMacro.init()
|
||||
--macro hscript.macros.CustomClassMacro.init()
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
@@ -124,6 +124,8 @@ class Bytes {
|
||||
#end
|
||||
bout.addByte(Type.enumIndex(e));
|
||||
switch( e ) {
|
||||
case EPackage(n):
|
||||
// TODO
|
||||
case EImport(c):
|
||||
// TODO
|
||||
case EClass(_, _, _, _):
|
||||
@@ -132,6 +134,8 @@ class Bytes {
|
||||
// TODO
|
||||
case ECast(e, _):
|
||||
// TODO
|
||||
case ERegex(e, f):
|
||||
// TODO
|
||||
case EConst(c):
|
||||
doEncodeConst(c);
|
||||
case EIdent(v):
|
||||
|
||||
+24
-12
@@ -48,6 +48,12 @@ class CustomClass implements IHScriptCustomClassBehaviour {
|
||||
__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);
|
||||
@@ -59,11 +65,6 @@ class CustomClass implements IHScriptCustomClassBehaviour {
|
||||
@:privateAccess __interp.exprReturn(f);
|
||||
}
|
||||
|
||||
for (f => v in __class.__interp.variables) {
|
||||
if (!__interp.variables.exists(f))
|
||||
__interp.variables.set(f, v);
|
||||
}
|
||||
|
||||
__interp.scriptObject = this;
|
||||
|
||||
initializing = true;
|
||||
@@ -124,20 +125,31 @@ class CustomClass implements IHScriptCustomClassBehaviour {
|
||||
if(__cachedFieldSet != null)
|
||||
UnsafeReflect.setField(__class.cl, "__cachedFieldSet", __cachedFieldSet);
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(__class.cl);
|
||||
__superClass = Type.createInstance(__class.cl, args);
|
||||
__superClass.__real_fields = disallowCopy;
|
||||
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 {
|
||||
// TODO: optimize this
|
||||
var fnName = toSuper ? '_HX_SUPER__$name' : name;
|
||||
var fn:Dynamic = __interp.variables.get(fnName);
|
||||
if(toSuper && fn == null)
|
||||
fn = __interp.variables.get(name);
|
||||
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);
|
||||
|
||||
@@ -56,6 +56,10 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
|
||||
__interp.staticVariables = ogInterp.staticVariables;
|
||||
__interp.customClasses = ogInterp.customClasses;
|
||||
|
||||
for(f => v in ogInterp.variables)
|
||||
if(!__interp.variables.exists(f))
|
||||
__interp.variables.set(f, v);
|
||||
|
||||
for(e in fields.copy()) {
|
||||
var validField:Bool = false;
|
||||
var staticField:Bool = false;
|
||||
@@ -78,10 +82,6 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
|
||||
fields.remove(e);
|
||||
}
|
||||
}
|
||||
|
||||
for(f => v in ogInterp.variables)
|
||||
if(!__interp.variables.exists(f))
|
||||
__interp.variables.set(f, v);
|
||||
}
|
||||
|
||||
public function hnew(args:Array<Dynamic>):Dynamic
|
||||
@@ -118,6 +118,13 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
+3
-1
@@ -34,7 +34,7 @@ typedef UInt64 = #if cpp cpp.UInt64 #else Int #end;
|
||||
enum Const {
|
||||
CInt( v : Int );
|
||||
CFloat( f : Float );
|
||||
CString( s : String );
|
||||
CString( s : String, ?i : Bool );
|
||||
}
|
||||
|
||||
#if hscriptPos
|
||||
@@ -79,10 +79,12 @@ enum Expr {
|
||||
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||
ECheckType( e : Expr, t : CType );
|
||||
|
||||
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 );
|
||||
EEnum( en:EnumDecl, ?isAbstract:Bool );
|
||||
ECast(e:Expr, ?t:CType);
|
||||
ERegex(e:String, flags:String);
|
||||
}
|
||||
|
||||
@:structInit
|
||||
|
||||
+79
-46
@@ -122,7 +122,8 @@ class Interp {
|
||||
return inCustomClass ? cast scriptObject : null;
|
||||
|
||||
public var errorHandler:Error->Void;
|
||||
public var importFailedCallback:Array<String>->Bool;
|
||||
public var warnHandler:Error->Void;
|
||||
public var importFailedCallback:Array<String>->Null<String>->Bool;
|
||||
|
||||
public var customClasses:Map<String, CustomClassHandler>;
|
||||
public var variables:Map<String, Dynamic>;
|
||||
@@ -143,6 +144,7 @@ class Interp {
|
||||
var beforeAlias:Null<String> = null;
|
||||
|
||||
public var importEnabled:Bool = true;
|
||||
public var allowStaticImports:Bool = true;
|
||||
|
||||
public var allowStaticVariables:Bool = false;
|
||||
public var allowPublicVariables:Bool = false;
|
||||
@@ -602,6 +604,15 @@ class Interp {
|
||||
return null;
|
||||
}
|
||||
|
||||
public inline function warn(e:#if hscriptPos ErrorDef #else Error #end) {
|
||||
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
|
||||
|
||||
if(warnHandler != null)
|
||||
warnHandler(e);
|
||||
else
|
||||
trace('[ Warning ] ${Printer.errorToString(e)}');
|
||||
}
|
||||
|
||||
inline function rethrow(e:Dynamic):Void {
|
||||
#if hl
|
||||
hl.Api.rethrow(e);
|
||||
@@ -703,13 +714,17 @@ class Interp {
|
||||
var e = e.e;
|
||||
#end
|
||||
switch (e) {
|
||||
case EPackage(_):
|
||||
case EClass(name, fields, extend, interfaces, isFinal):
|
||||
// TODO: module isolation
|
||||
var oldName:String = name;
|
||||
var hasAlias:Bool = (setAlias != null && beforeAlias == oldName);
|
||||
var toSetName:String = hasAlias ? setAlias : oldName;
|
||||
|
||||
if (customClasses.exists(toSetName))
|
||||
error(EAlreadyExistingClass(toSetName));
|
||||
if (customClasses.exists(toSetName)) {
|
||||
warn(EAlreadyExistingClass(toSetName));
|
||||
return null;// ignore it
|
||||
}
|
||||
|
||||
inline function importVar(thing:String):String {
|
||||
if (thing == null)
|
||||
@@ -724,20 +739,20 @@ class Interp {
|
||||
beforeAlias = null;
|
||||
setAlias = null;
|
||||
}
|
||||
case EImport(c, n, isUsing):
|
||||
if (!importEnabled)
|
||||
return null;
|
||||
var splitClassName = [for (e in c.split(".")) e.trim()];
|
||||
case EImport(clsName, aliasAs, isUsing):
|
||||
if(!importEnabled) return null;
|
||||
|
||||
var splitClassName = [for (e in clsName.split(".")) e.trim()];
|
||||
var realClassName = splitClassName.join(".");
|
||||
var claVarName = splitClassName[splitClassName.length - 1];
|
||||
var toSetName = n != null ? n : claVarName;
|
||||
var toSetName = aliasAs != null ? aliasAs : claVarName;
|
||||
var oldClassName = realClassName;
|
||||
var oldSplitName = splitClassName.copy();
|
||||
|
||||
if (variables.exists(toSetName)) { // class is already imported
|
||||
if(variables.exists(toSetName)) { // class is already imported
|
||||
if(isUsing && !usingHandler.entryExists(toSetName))
|
||||
setUsing(toSetName, variables.get(toSetName));
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -747,56 +762,70 @@ class Interp {
|
||||
// setting the extension
|
||||
if(isUsing && !usingHandler.entryExists(toSetName))
|
||||
setCustomClassUsing(toSetName, customClasses.get(toSetName));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function importResolve(__clsName:String):Null<Dynamic> {
|
||||
var _realClassName = getLocalImportRedirect(__clsName);
|
||||
if(importBlocklist.contains(_realClassName)) return null;
|
||||
|
||||
var realClassName = getLocalImportRedirect(realClassName);
|
||||
|
||||
if (importBlocklist.contains(realClassName))
|
||||
return null;
|
||||
var cl = Type.resolveClass(realClassName);
|
||||
if (cl == null)
|
||||
cl = Type.resolveClass('${realClassName}_HSC');
|
||||
var _cl = Type.resolveClass(_realClassName);
|
||||
if(_cl == null) _cl = Type.resolveClass('${_realClassName}_HSC');
|
||||
return _cl;
|
||||
}
|
||||
|
||||
var cl = importResolve(realClassName);
|
||||
var en = Type.resolveEnum(realClassName);
|
||||
|
||||
//trace(realClassName, cl, en, splitClassName);
|
||||
|
||||
// Allow for flixel.ui.FlxBar.FlxBarFillDirection;
|
||||
if (cl == null && en == null) {
|
||||
if(cl == null && en == null) {
|
||||
if(splitClassName.length > 1) {
|
||||
splitClassName.splice(-2, 1); // Remove the last last item
|
||||
realClassName = splitClassName.join(".");
|
||||
|
||||
var realClassName = getLocalImportRedirect(realClassName);
|
||||
|
||||
if (importBlocklist.contains(realClassName))
|
||||
return null;
|
||||
|
||||
cl = Type.resolveClass(realClassName);
|
||||
if (cl == null)
|
||||
cl = Type.resolveClass('${realClassName}_HSC');
|
||||
|
||||
cl = importResolve(realClassName);
|
||||
en = Type.resolveEnum(realClassName);
|
||||
|
||||
//trace(realClassName, cl, en, splitClassName);
|
||||
}
|
||||
}
|
||||
|
||||
if (cl == null && en == null) {
|
||||
if(cl == null && en == null) {
|
||||
if(allowStaticImports) { //allows for static imports like "haxe.io.Path.normalize"
|
||||
var clPth = oldSplitName.copy();
|
||||
var funcName = clPth.pop();
|
||||
var statField = Reflect.getProperty(Type.resolveClass(StringTools.trim(clPth.join("."))), funcName);
|
||||
|
||||
if(statField != null) {
|
||||
variables.set((toSetName != null && toSetName.length > 0 ? toSetName : funcName), statField);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
beforeAlias = claVarName;
|
||||
setAlias = n;
|
||||
if (importFailedCallback == null || !importFailedCallback(oldSplitName)){
|
||||
setAlias = aliasAs;
|
||||
if(importFailedCallback == null || !importFailedCallback(oldSplitName, toSetName)){
|
||||
beforeAlias = null;
|
||||
setAlias = null;
|
||||
error(EInvalidClass(oldClassName));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (en != null) {
|
||||
if(isUsing) error(EInvalidClass(oldClassName));
|
||||
// ENUM!!!!
|
||||
//If the first letter of the alias is not an uppercase letter, then throw an error.
|
||||
//We don't need to worry about this for static imports.
|
||||
if(toSetName != claVarName && !Tools.isUppercase(toSetName)) {
|
||||
error(ECustom("Type aliases must start with an uppercase letter"));
|
||||
return null;
|
||||
}
|
||||
|
||||
if(en != null) { // ENUM!!!!
|
||||
if(isUsing) {
|
||||
error(EInvalidClass(oldClassName));
|
||||
return null;
|
||||
}
|
||||
|
||||
var enumThingy:HEnum = {};
|
||||
for (c in en.getConstructors()) {
|
||||
for(c in en.getConstructors()) {
|
||||
try {
|
||||
//UnsafeReflect.setField(enumThingy, c, en.createByName(c));
|
||||
enumThingy.setEnum(c, en.createByName(c));
|
||||
@@ -810,12 +839,11 @@ class Interp {
|
||||
}
|
||||
}
|
||||
variables.set(toSetName, enumThingy);
|
||||
} else {
|
||||
} else { //Standard class
|
||||
if(isUsing) setUsing(toSetName, cl);
|
||||
variables.set(toSetName, cl);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
case EEnum(en, _): // TODO: enum abstracts
|
||||
@@ -884,6 +912,8 @@ class Interp {
|
||||
variables.set(en.name, enumThingy);
|
||||
case ECast(e, _): // TODO
|
||||
return expr(e);
|
||||
case ERegex(e, f):
|
||||
return new EReg(e, f);
|
||||
case EConst(c):
|
||||
switch (c) {
|
||||
case CInt(v): return v;
|
||||
@@ -907,6 +937,7 @@ class Interp {
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
isVar: isVar,
|
||||
isStatic: isStatic,
|
||||
interp: this,
|
||||
}
|
||||
}
|
||||
@@ -1218,7 +1249,7 @@ class Interp {
|
||||
var match = false;
|
||||
for (c in cases) {
|
||||
for (v in c.values) {
|
||||
// https://github.com/FunkinCrew/hscript/blob/funkin-dev/hscript/Interp.hx#L531
|
||||
// https://github.com/FunkinCrew/hscript/blob/funkin-dev/hscript/Interp.hx#L611
|
||||
switch (Tools.expr(v)) {
|
||||
case ECall(e, params):
|
||||
switch (Tools.expr(e)) {
|
||||
@@ -1426,7 +1457,7 @@ class Interp {
|
||||
if (o == null)
|
||||
error(EInvalidAccess(f));
|
||||
|
||||
var cls = Type.getClass(o);
|
||||
var cls:Null<Class<Dynamic>> = useRedirects ? Type.getClass(o) : null;
|
||||
if (useRedirects && {
|
||||
var cl:Null<String> = getClassType(o, cls);
|
||||
cl != null && getRedirects.exists(cl) && (_getRedirect = getRedirects[cl]) != null;
|
||||
@@ -1449,9 +1480,9 @@ class Interp {
|
||||
var obj:IHScriptCustomBehaviour = cast o;
|
||||
return obj.hget(f);
|
||||
}
|
||||
var v = null;
|
||||
var v:Null<Dynamic> = null;
|
||||
if(isBypassAccessor) {
|
||||
if ((v = UnsafeReflect.field(o, f)) == null)
|
||||
if ((v = UnsafeReflect.field(o, f)) == null && useRedirects)
|
||||
v = Reflect.field(cls, f);
|
||||
}
|
||||
|
||||
@@ -1459,15 +1490,15 @@ class Interp {
|
||||
#if php
|
||||
// https://github.com/HaxeFoundation/haxe/issues/4915
|
||||
try {
|
||||
if ((v = UnsafeReflect.getProperty(o, f)) == null)
|
||||
if ((v = UnsafeReflect.getProperty(o, f)) == null && useRedirects)
|
||||
v = Reflect.getProperty(cls, f);
|
||||
}
|
||||
catch(e:Dynamic) {
|
||||
if ((v = UnsafeReflect.field(o, f)) == null)
|
||||
if ((v = UnsafeReflect.field(o, f)) == null && useRedirects)
|
||||
v = Reflect.field(cls, f);
|
||||
}
|
||||
#else
|
||||
if ((v = UnsafeReflect.getProperty(o, f)) == null)
|
||||
if ((v = UnsafeReflect.getProperty(o, f)) == null && useRedirects)
|
||||
v = Reflect.getProperty(cls, f);
|
||||
#end
|
||||
}
|
||||
@@ -1622,3 +1653,5 @@ class Interp {
|
||||
return Type.createInstance(c, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+207
-9
@@ -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
|
||||
**/
|
||||
@@ -115,6 +121,8 @@ class Parser {
|
||||
|
||||
#end
|
||||
|
||||
static inline var regexFlags: String = "igmsu";
|
||||
|
||||
public function new() {
|
||||
line = 1;
|
||||
opChars = "+*/-=!><&|^%~";
|
||||
@@ -332,7 +340,7 @@ class Parser {
|
||||
if( !allowJSON )
|
||||
unexpected(tk);
|
||||
switch( c ) {
|
||||
case CString(s): id = s;
|
||||
case CString(s, _): id = s;
|
||||
default: unexpected(tk);
|
||||
}
|
||||
case TBrClose:
|
||||
@@ -369,6 +377,11 @@ class Parser {
|
||||
e = mk(EIdent(id));
|
||||
return parseExprNext(e);
|
||||
case TConst(c):
|
||||
switch(c) {
|
||||
case CString(s, i):
|
||||
if(i) return parseExprNext(interpolateString(s));
|
||||
default:
|
||||
}
|
||||
return parseExprNext(mk(EConst(c)));
|
||||
case TPOpen:
|
||||
tk = token();
|
||||
@@ -513,6 +526,8 @@ class Parser {
|
||||
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);
|
||||
}
|
||||
@@ -957,6 +972,16 @@ class Parser {
|
||||
var tk = token();
|
||||
push(tk);
|
||||
mk(EFunction(inf.args, inf.body, name, inf.ret, nextIsPublic, nextIsStatic, nextIsOverride, nextIsPrivate, nextIsFinal, nextIsInline),p1,pmax(inf.body));
|
||||
case "package":
|
||||
var tk = token();
|
||||
push(tk);
|
||||
if(tk == TSemicolon)
|
||||
return mk(EPackage(null), p1);
|
||||
|
||||
var pkg:String = parsePath().join('.');
|
||||
ensure(TSemicolon);
|
||||
push(TSemicolon);
|
||||
mk(EPackage(pkg), p1);
|
||||
case "import" | "using":
|
||||
var isUsing = id == "using";
|
||||
var oldReadPos = readPos;
|
||||
@@ -1160,7 +1185,7 @@ class Parser {
|
||||
e = parseExpr();
|
||||
}
|
||||
|
||||
mk(ECast(e, t));
|
||||
mk(ECast(e, t), p1);
|
||||
case "return":
|
||||
var tk = token();
|
||||
push(tk);
|
||||
@@ -1618,6 +1643,94 @@ class Parser {
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// From hscript-iris: https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Parser.hx#L409
|
||||
// by inky03
|
||||
function interpolateString(s:String):Expr {
|
||||
var exprs:Array<Expr> = [];
|
||||
var dollarPos:Int = s.indexOf('$');
|
||||
|
||||
if(dollarPos == -1)
|
||||
return mk(EConst(CString(s)));
|
||||
|
||||
final singleFirst:EReg = ~/[a-zA-Z_]/i;
|
||||
final singleExpr:EReg = ~/[a-zA-Z0-9_]/i;
|
||||
// TODO: optimize this using regex
|
||||
while(dollarPos > -1) {
|
||||
var pos:Int = dollarPos;
|
||||
var pre:String = s.substr(0, pos);
|
||||
var next:String = s.charAt(++pos);
|
||||
if(next == '{') {
|
||||
if (pre != '')
|
||||
exprs.push(mk(EConst(CString(pre))));
|
||||
var exprStr:String = '';
|
||||
var depth:Int = 1;
|
||||
while (true) {
|
||||
next = s.charAt(++pos);
|
||||
if (next == '{') {
|
||||
depth++;
|
||||
} else if (next == '}') {
|
||||
depth--;
|
||||
}
|
||||
if (depth < 1)
|
||||
break;
|
||||
if (pos >= s.length) {
|
||||
error(EUnterminatedString, pos, pos);
|
||||
}
|
||||
exprStr += next;
|
||||
}
|
||||
if (exprStr.trim() == '') {
|
||||
error(ECustom("Expression cannot be empty"), pos, pos);
|
||||
}
|
||||
var prevChar = char;
|
||||
var prevInput = input;
|
||||
var prevReadPos = readPos; // a bit stupid innit???
|
||||
#if hscriptPos
|
||||
var prevOrigin = origin;
|
||||
#end
|
||||
var expr = parseString('($exprStr)' #if hscriptPos, origin #end);
|
||||
readPos = prevReadPos; // rolling back parser state because otherwise we get problems...
|
||||
input = prevInput;
|
||||
char = prevChar;
|
||||
#if hscriptPos
|
||||
origin = prevOrigin;
|
||||
#end
|
||||
exprs.push(expr);
|
||||
pos++;
|
||||
}
|
||||
else if(singleFirst.match(next)) {
|
||||
if (pre != '')
|
||||
exprs.push(mk(EConst(CString(pre))));
|
||||
var ident: String = '';
|
||||
while(singleExpr.match(next)) {
|
||||
ident += next;
|
||||
next = s.charAt(++pos);
|
||||
}
|
||||
exprs.push(mk(EIdent(ident)));
|
||||
}
|
||||
else if (next == '$') {
|
||||
var secondToNext: String = s.charAt(pos);
|
||||
if (secondToNext == "$") { // if its another dollar, skip...
|
||||
s = pre + s.substr(pos, pos + 1); // remove $ ahead of the current one
|
||||
break;
|
||||
}
|
||||
exprs.push(mk(EConst(CString(pre + '$'))));
|
||||
}
|
||||
s = s.substr(pos++);
|
||||
dollarPos = s.indexOf('$');
|
||||
}
|
||||
if (exprs.length == 0) {
|
||||
return mk(EConst(CString(s)));
|
||||
} else {
|
||||
exprs.push(mk(EConst(CString(s))));
|
||||
var expr:Null<Expr> = exprs.shift();
|
||||
while(true) {
|
||||
if(exprs.length == 0) break;
|
||||
expr = mk(EBinop('+', expr, exprs.shift()));
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
function checkAccess(get:FieldPropertyAccess, set:FieldPropertyAccess, ?expr:Expr, ?type:CType) {
|
||||
#if hscriptPos
|
||||
@@ -1853,8 +1966,9 @@ class Parser {
|
||||
return StringTools.fastCodeAt(input, readPos++);
|
||||
}
|
||||
|
||||
function readString( until:Int ):String {
|
||||
function readString( until:Int, regex:Bool = false, interpolate:Bool = false ):String {
|
||||
var c = 0;
|
||||
var prev = 0;
|
||||
var b = new StringBuf();
|
||||
var esc = false;
|
||||
var old = line;
|
||||
@@ -1901,18 +2015,93 @@ class Parser {
|
||||
b.addChar(k);
|
||||
default: invalidChar(c);
|
||||
}
|
||||
} else if( c == 92 )
|
||||
} else if( c == 92 && !regex)
|
||||
esc = true;
|
||||
else if( c == until )
|
||||
break;
|
||||
else if( c == until ) {
|
||||
if(regex && prev == 92) {
|
||||
b.addChar(c);
|
||||
prev = c;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (c == 36 && interpolate) { // brace for impact !!
|
||||
// From hscript-iris: https://github.com/pisayesiwsi/hscript-iris/blob/dev/crowplexus/hscript/Parser.hx#L1665
|
||||
// by inky03
|
||||
// TODO: optimize this
|
||||
b.addChar(c);
|
||||
var next = readChar();
|
||||
if (next == 123) {
|
||||
b.addChar(next);
|
||||
var depth:Int = 0;
|
||||
while (true) {
|
||||
next = readChar();
|
||||
if (StringTools.isEof(next)) {
|
||||
error(EUnterminatedString, p1, p1);
|
||||
}
|
||||
b.addChar(next);
|
||||
if (next == "'".code) {
|
||||
var nextStr:String = readString("'".code, false, true);
|
||||
for (char in nextStr) {
|
||||
b.addChar(char);
|
||||
}
|
||||
b.addChar("'".code);
|
||||
next = readChar();
|
||||
b.addChar(next);
|
||||
}
|
||||
if (next == 125) {
|
||||
depth--;
|
||||
if (depth < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
readPos--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( c == 10 ) line++;
|
||||
b.addChar(c);
|
||||
if(regex)
|
||||
prev = c;
|
||||
}
|
||||
}
|
||||
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>) {
|
||||
@@ -2080,7 +2269,10 @@ class Parser {
|
||||
case "}".code: return TBrClose;
|
||||
case "[".code: return TBkOpen;
|
||||
case "]".code: return TBkClose;
|
||||
case "'".code, '"'.code: return TConst( CString(readString(char)) );
|
||||
case "'".code:
|
||||
return TConst( CString(readString(char, false, true), true) );
|
||||
case '"'.code:
|
||||
return TConst( CString(readString(char), false) );
|
||||
case "?".code:
|
||||
char = readChar();
|
||||
switch (char) {
|
||||
@@ -2136,7 +2328,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;
|
||||
@@ -2146,6 +2338,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);
|
||||
@@ -2336,7 +2533,7 @@ class Parser {
|
||||
return switch(c) {
|
||||
case CInt(v): Std.string(v);
|
||||
case CFloat(f): Std.string(f);
|
||||
case CString(s): s; // TODO : escape + quote
|
||||
case CString(s, _): s; // TODO : escape + quote
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2360,6 +2557,7 @@ class Parser {
|
||||
case TDoubleDot: ":";
|
||||
case TMeta(id): "@" + id;
|
||||
case TPrepro(id): "#" + id;
|
||||
case TRegex(e, f): '~/$e/$f';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,11 @@ class Printer {
|
||||
return;
|
||||
}
|
||||
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)
|
||||
@@ -181,6 +186,9 @@ class Printer {
|
||||
addType(t);
|
||||
add(")");
|
||||
}
|
||||
case ERegex(e, f):
|
||||
add('~/$e/$f');
|
||||
add(';\n');
|
||||
case EConst(c):
|
||||
switch( c ) {
|
||||
case CInt(i): add(i);
|
||||
|
||||
+20
-6
@@ -28,31 +28,41 @@ import hscript.Expr.FieldPropertyAccess;
|
||||
@: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, 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';
|
||||
var fName:String = '$GET$name';
|
||||
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
||||
if (interp.varExists(fName)) {
|
||||
if (varExists(fName)) {
|
||||
return callAccessor(fName);
|
||||
} else
|
||||
interp.error(ECustom('Method $fName required by property $name is missing'));
|
||||
@@ -73,9 +83,9 @@ class Property {
|
||||
public function callSetter(name:String, val:Dynamic) {
|
||||
switch (setter) {
|
||||
case ASet | ADynamic:
|
||||
var fName:String = 'set_$name';
|
||||
var fName:String = '$SET$name';
|
||||
if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
||||
if (interp.varExists(fName))
|
||||
if (varExists(fName))
|
||||
return callAccessor(fName, [val], true);
|
||||
else
|
||||
interp.error(ECustom('Method $fName required by property $name is missing'));
|
||||
@@ -94,7 +104,7 @@ class Property {
|
||||
}
|
||||
|
||||
private function callAccessor(f:String, ?args:Array<Dynamic>, isWrite:Bool = false):Dynamic {
|
||||
var fn = interp.variables.get(f);
|
||||
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;
|
||||
@@ -111,4 +121,8 @@ class Property {
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
private function varExists(n:String) {
|
||||
return isStatic ? interp.staticVariables.exists(n) : interp.variables.exists(n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,13 @@ class Tools {
|
||||
#end
|
||||
}
|
||||
|
||||
public static function isUppercase(s:String) {
|
||||
if(s.length == 0) return false;
|
||||
var c:Int = StringTools.fastCodeAt(s, 0);
|
||||
if(StringTools.isEof(c)) return false; // Just in case :3
|
||||
return c >= 65 && c <= 90; // A-Z
|
||||
}
|
||||
|
||||
public static inline function isCustomAbstract(obj:Dynamic):Bool
|
||||
return obj != null && obj is IHScriptAbstractBehaviour;
|
||||
|
||||
|
||||
@@ -26,9 +26,11 @@ class ClassExtendMacro {
|
||||
#if !display
|
||||
#if CUSTOM_CLASSES
|
||||
if(Context.defined("display")) return;
|
||||
/*
|
||||
for(apply in Config.ALLOWED_CUSTOM_CLASSES) {
|
||||
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.ClassExtendMacro.build())");
|
||||
}
|
||||
*/
|
||||
//Context.onAfterTyping(buildTyped);
|
||||
#end
|
||||
#end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user