Compare commits
15
Commits
@@ -1,6 +1,8 @@
|
|||||||
hscript-improved
|
hscript-improved
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
- [Features](docs/FEATURES.md)
|
||||||
|
|
||||||
How to install
|
How to install
|
||||||
```
|
```
|
||||||
haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
|
haxelib git hscript-improved https://github.com/CodenameCrew/hscript-improved.git
|
||||||
@@ -18,7 +20,10 @@ or set this in build.hxml
|
|||||||
-D CUSTOM_CLASSES
|
-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
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "hscript",
|
"name": "hscript-improved",
|
||||||
"url": "https://github.com/HaxeFoundation/hscript",
|
"url": "https://github.com/HaxeFoundation/hscript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ class Bytes {
|
|||||||
#end
|
#end
|
||||||
bout.addByte(Type.enumIndex(e));
|
bout.addByte(Type.enumIndex(e));
|
||||||
switch( e ) {
|
switch( e ) {
|
||||||
|
case EPackage(n):
|
||||||
|
// TODO
|
||||||
case EImport(c):
|
case EImport(c):
|
||||||
// TODO
|
// TODO
|
||||||
case EClass(_, _, _, _):
|
case EClass(_, _, _, _):
|
||||||
@@ -132,6 +134,8 @@ class Bytes {
|
|||||||
// TODO
|
// TODO
|
||||||
case ECast(e, _):
|
case ECast(e, _):
|
||||||
// TODO
|
// TODO
|
||||||
|
case ERegex(e, f):
|
||||||
|
// TODO
|
||||||
case EConst(c):
|
case EConst(c):
|
||||||
doEncodeConst(c);
|
doEncodeConst(c);
|
||||||
case EIdent(v):
|
case EIdent(v):
|
||||||
|
|||||||
+24
-12
@@ -48,6 +48,12 @@ class CustomClass implements IHScriptCustomClassBehaviour {
|
|||||||
__interp.staticVariables = __class.ogInterp.staticVariables;
|
__interp.staticVariables = __class.ogInterp.staticVariables;
|
||||||
__interp.customClasses = __class.ogInterp.customClasses;
|
__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) {
|
for (f in __class.fields) {
|
||||||
switch (Tools.expr(f)) {
|
switch (Tools.expr(f)) {
|
||||||
case EVar(n): __class__fields.push(n);
|
case EVar(n): __class__fields.push(n);
|
||||||
@@ -59,11 +65,6 @@ class CustomClass implements IHScriptCustomClassBehaviour {
|
|||||||
@:privateAccess __interp.exprReturn(f);
|
@:privateAccess __interp.exprReturn(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (f => v in __class.__interp.variables) {
|
|
||||||
if (!__interp.variables.exists(f))
|
|
||||||
__interp.variables.set(f, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
__interp.scriptObject = this;
|
__interp.scriptObject = this;
|
||||||
|
|
||||||
initializing = true;
|
initializing = true;
|
||||||
@@ -124,20 +125,31 @@ class CustomClass implements IHScriptCustomClassBehaviour {
|
|||||||
if(__cachedFieldSet != null)
|
if(__cachedFieldSet != null)
|
||||||
UnsafeReflect.setField(__class.cl, "__cachedFieldSet", __cachedFieldSet);
|
UnsafeReflect.setField(__class.cl, "__cachedFieldSet", __cachedFieldSet);
|
||||||
|
|
||||||
var disallowCopy = Type.getInstanceFields(__class.cl);
|
|
||||||
__superClass = Type.createInstance(__class.cl, args);
|
__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.__class__fields = this.__class__fields;
|
||||||
__superClass.__interp = this.__interp;
|
__superClass.__interp = this.__interp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function call(name:String, ?args:Array<Dynamic>, ?toSuper:Bool = false):Dynamic {
|
public function call(name:String, ?args:Array<Dynamic>, ?toSuper:Bool = false):Dynamic {
|
||||||
// TODO: optimize this
|
var superFnName:Null<String> = toSuper ? '_HX_SUPER__$name' : null;
|
||||||
var fnName = toSuper ? '_HX_SUPER__$name' : name;
|
var fn:Dynamic = {
|
||||||
var fn:Dynamic = __interp.variables.get(fnName);
|
if(toSuper && __interp.variables.exists(superFnName)) {
|
||||||
if(toSuper && fn == null)
|
__interp.variables.get(superFnName);
|
||||||
fn = __interp.variables.get(name);
|
}
|
||||||
|
else
|
||||||
|
__interp.variables.get(name);
|
||||||
|
};
|
||||||
|
|
||||||
if (fn != null && Reflect.isFunction(fn))
|
if (fn != null && Reflect.isFunction(fn))
|
||||||
return UnsafeReflect.callMethodUnsafe(null, fn, (args == null) ? [] : args);
|
return UnsafeReflect.callMethodUnsafe(null, fn, (args == null) ? [] : args);
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
|
|||||||
__interp.staticVariables = ogInterp.staticVariables;
|
__interp.staticVariables = ogInterp.staticVariables;
|
||||||
__interp.customClasses = ogInterp.customClasses;
|
__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()) {
|
for(e in fields.copy()) {
|
||||||
var validField:Bool = false;
|
var validField:Bool = false;
|
||||||
var staticField:Bool = false;
|
var staticField:Bool = false;
|
||||||
@@ -78,10 +82,6 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
|
|||||||
fields.remove(e);
|
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
|
public function hnew(args:Array<Dynamic>):Dynamic
|
||||||
@@ -118,6 +118,13 @@ class CustomClassHandler implements IHScriptCustomConstructor implements IHScrip
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function hget(name:String):Dynamic {
|
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)) {
|
if(hasField(name)) {
|
||||||
return getField(name);
|
return getField(name);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -34,7 +34,7 @@ typedef UInt64 = #if cpp cpp.UInt64 #else Int #end;
|
|||||||
enum Const {
|
enum Const {
|
||||||
CInt( v : Int );
|
CInt( v : Int );
|
||||||
CFloat( f : Float );
|
CFloat( f : Float );
|
||||||
CString( s : String );
|
CString( s : String, ?i : Bool );
|
||||||
}
|
}
|
||||||
|
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
@@ -79,10 +79,12 @@ enum Expr {
|
|||||||
EMeta( name : String, args : Array<Expr>, e : Expr );
|
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||||
ECheckType( e : Expr, t : CType );
|
ECheckType( e : Expr, t : CType );
|
||||||
|
|
||||||
|
EPackage( ?n:String );
|
||||||
EImport( c : String, ?asname:String, ?isUsing:Bool );
|
EImport( c : String, ?asname:String, ?isUsing:Bool );
|
||||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
|
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String>, ?isFinal:Bool, ?isPrivate:Bool );
|
||||||
EEnum( en:EnumDecl, ?isAbstract:Bool );
|
EEnum( en:EnumDecl, ?isAbstract:Bool );
|
||||||
ECast(e:Expr, ?t:CType);
|
ECast(e:Expr, ?t:CType);
|
||||||
|
ERegex(e:String, flags:String);
|
||||||
}
|
}
|
||||||
|
|
||||||
@:structInit
|
@:structInit
|
||||||
|
|||||||
+10
-6
@@ -703,6 +703,7 @@ class Interp {
|
|||||||
var e = e.e;
|
var e = e.e;
|
||||||
#end
|
#end
|
||||||
switch (e) {
|
switch (e) {
|
||||||
|
case EPackage(_):
|
||||||
case EClass(name, fields, extend, interfaces, isFinal):
|
case EClass(name, fields, extend, interfaces, isFinal):
|
||||||
var oldName:String = name;
|
var oldName:String = name;
|
||||||
var hasAlias:Bool = (setAlias != null && beforeAlias == oldName);
|
var hasAlias:Bool = (setAlias != null && beforeAlias == oldName);
|
||||||
@@ -884,6 +885,8 @@ class Interp {
|
|||||||
variables.set(en.name, enumThingy);
|
variables.set(en.name, enumThingy);
|
||||||
case ECast(e, _): // TODO
|
case ECast(e, _): // TODO
|
||||||
return expr(e);
|
return expr(e);
|
||||||
|
case ERegex(e, f):
|
||||||
|
return new EReg(e, f);
|
||||||
case EConst(c):
|
case EConst(c):
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case CInt(v): return v;
|
case CInt(v): return v;
|
||||||
@@ -907,6 +910,7 @@ class Interp {
|
|||||||
getter: getter,
|
getter: getter,
|
||||||
setter: setter,
|
setter: setter,
|
||||||
isVar: isVar,
|
isVar: isVar,
|
||||||
|
isStatic: isStatic,
|
||||||
interp: this,
|
interp: this,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1426,7 +1430,7 @@ class Interp {
|
|||||||
if (o == null)
|
if (o == null)
|
||||||
error(EInvalidAccess(f));
|
error(EInvalidAccess(f));
|
||||||
|
|
||||||
var cls = Type.getClass(o);
|
var cls:Null<Class<Dynamic>> = useRedirects ? Type.getClass(o) : null;
|
||||||
if (useRedirects && {
|
if (useRedirects && {
|
||||||
var cl:Null<String> = getClassType(o, cls);
|
var cl:Null<String> = getClassType(o, cls);
|
||||||
cl != null && getRedirects.exists(cl) && (_getRedirect = getRedirects[cl]) != null;
|
cl != null && getRedirects.exists(cl) && (_getRedirect = getRedirects[cl]) != null;
|
||||||
@@ -1449,9 +1453,9 @@ class Interp {
|
|||||||
var obj:IHScriptCustomBehaviour = cast o;
|
var obj:IHScriptCustomBehaviour = cast o;
|
||||||
return obj.hget(f);
|
return obj.hget(f);
|
||||||
}
|
}
|
||||||
var v = null;
|
var v:Null<Dynamic> = null;
|
||||||
if(isBypassAccessor) {
|
if(isBypassAccessor) {
|
||||||
if ((v = UnsafeReflect.field(o, f)) == null)
|
if ((v = UnsafeReflect.field(o, f)) == null && useRedirects)
|
||||||
v = Reflect.field(cls, f);
|
v = Reflect.field(cls, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1459,15 +1463,15 @@ class Interp {
|
|||||||
#if php
|
#if php
|
||||||
// https://github.com/HaxeFoundation/haxe/issues/4915
|
// https://github.com/HaxeFoundation/haxe/issues/4915
|
||||||
try {
|
try {
|
||||||
if ((v = UnsafeReflect.getProperty(o, f)) == null)
|
if ((v = UnsafeReflect.getProperty(o, f)) == null && useRedirects)
|
||||||
v = Reflect.getProperty(cls, f);
|
v = Reflect.getProperty(cls, f);
|
||||||
}
|
}
|
||||||
catch(e:Dynamic) {
|
catch(e:Dynamic) {
|
||||||
if ((v = UnsafeReflect.field(o, f)) == null)
|
if ((v = UnsafeReflect.field(o, f)) == null && useRedirects)
|
||||||
v = Reflect.field(cls, f);
|
v = Reflect.field(cls, f);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if ((v = UnsafeReflect.getProperty(o, f)) == null)
|
if ((v = UnsafeReflect.getProperty(o, f)) == null && useRedirects)
|
||||||
v = Reflect.getProperty(cls, f);
|
v = Reflect.getProperty(cls, f);
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|||||||
+207
-9
@@ -43,6 +43,7 @@ enum Token {
|
|||||||
TDoubleDot;
|
TDoubleDot;
|
||||||
TMeta( s : String );
|
TMeta( s : String );
|
||||||
TPrepro( s : String );
|
TPrepro( s : String );
|
||||||
|
TRegex( e : String, f:String );
|
||||||
}
|
}
|
||||||
|
|
||||||
@:structInit
|
@:structInit
|
||||||
@@ -72,6 +73,11 @@ class Parser {
|
|||||||
**/
|
**/
|
||||||
public var allowJSON : Bool;
|
public var allowJSON : Bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
activate Regular Expression parsing
|
||||||
|
**/
|
||||||
|
public var allowRegex : Bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
allow types declarations
|
allow types declarations
|
||||||
**/
|
**/
|
||||||
@@ -115,6 +121,8 @@ class Parser {
|
|||||||
|
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
static inline var regexFlags: String = "igmsu";
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
line = 1;
|
line = 1;
|
||||||
opChars = "+*/-=!><&|^%~";
|
opChars = "+*/-=!><&|^%~";
|
||||||
@@ -332,7 +340,7 @@ class Parser {
|
|||||||
if( !allowJSON )
|
if( !allowJSON )
|
||||||
unexpected(tk);
|
unexpected(tk);
|
||||||
switch( c ) {
|
switch( c ) {
|
||||||
case CString(s): id = s;
|
case CString(s, _): id = s;
|
||||||
default: unexpected(tk);
|
default: unexpected(tk);
|
||||||
}
|
}
|
||||||
case TBrClose:
|
case TBrClose:
|
||||||
@@ -369,6 +377,11 @@ class Parser {
|
|||||||
e = mk(EIdent(id));
|
e = mk(EIdent(id));
|
||||||
return parseExprNext(e);
|
return parseExprNext(e);
|
||||||
case TConst(c):
|
case TConst(c):
|
||||||
|
switch(c) {
|
||||||
|
case CString(s, i):
|
||||||
|
if(i) return parseExprNext(interpolateString(s));
|
||||||
|
default:
|
||||||
|
}
|
||||||
return parseExprNext(mk(EConst(c)));
|
return parseExprNext(mk(EConst(c)));
|
||||||
case TPOpen:
|
case TPOpen:
|
||||||
tk = token();
|
tk = token();
|
||||||
@@ -513,6 +526,8 @@ class Parser {
|
|||||||
var e = parseExpr();
|
var e = parseExpr();
|
||||||
isVar = false;
|
isVar = false;
|
||||||
return mk(EMeta(id, args, e),p1);
|
return mk(EMeta(id, args, e),p1);
|
||||||
|
case TRegex(e, f) if(allowRegex):
|
||||||
|
return mk(ERegex(e, f), p1);
|
||||||
default:
|
default:
|
||||||
return unexpected(tk);
|
return unexpected(tk);
|
||||||
}
|
}
|
||||||
@@ -957,6 +972,16 @@ class Parser {
|
|||||||
var tk = token();
|
var tk = token();
|
||||||
push(tk);
|
push(tk);
|
||||||
mk(EFunction(inf.args, inf.body, name, inf.ret, nextIsPublic, nextIsStatic, nextIsOverride, nextIsPrivate, nextIsFinal, nextIsInline),p1,pmax(inf.body));
|
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":
|
case "import" | "using":
|
||||||
var isUsing = id == "using";
|
var isUsing = id == "using";
|
||||||
var oldReadPos = readPos;
|
var oldReadPos = readPos;
|
||||||
@@ -1160,7 +1185,7 @@ class Parser {
|
|||||||
e = parseExpr();
|
e = parseExpr();
|
||||||
}
|
}
|
||||||
|
|
||||||
mk(ECast(e, t));
|
mk(ECast(e, t), p1);
|
||||||
case "return":
|
case "return":
|
||||||
var tk = token();
|
var tk = token();
|
||||||
push(tk);
|
push(tk);
|
||||||
@@ -1618,6 +1643,94 @@ class Parser {
|
|||||||
}
|
}
|
||||||
return args;
|
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) {
|
function checkAccess(get:FieldPropertyAccess, set:FieldPropertyAccess, ?expr:Expr, ?type:CType) {
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
@@ -1853,8 +1966,9 @@ class Parser {
|
|||||||
return StringTools.fastCodeAt(input, readPos++);
|
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 c = 0;
|
||||||
|
var prev = 0;
|
||||||
var b = new StringBuf();
|
var b = new StringBuf();
|
||||||
var esc = false;
|
var esc = false;
|
||||||
var old = line;
|
var old = line;
|
||||||
@@ -1901,18 +2015,93 @@ class Parser {
|
|||||||
b.addChar(k);
|
b.addChar(k);
|
||||||
default: invalidChar(c);
|
default: invalidChar(c);
|
||||||
}
|
}
|
||||||
} else if( c == 92 )
|
} else if( c == 92 && !regex)
|
||||||
esc = true;
|
esc = true;
|
||||||
else if( c == until )
|
else if( c == until ) {
|
||||||
break;
|
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 {
|
else {
|
||||||
if( c == 10 ) line++;
|
if( c == 10 ) line++;
|
||||||
b.addChar(c);
|
b.addChar(c);
|
||||||
|
if(regex)
|
||||||
|
prev = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b.toString();
|
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():Token {
|
||||||
//function token(?infos : Null<haxe.PosInfos>) {
|
//function token(?infos : Null<haxe.PosInfos>) {
|
||||||
//function ttrace(v:Dynamic, ?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 TBrClose;
|
||||||
case "[".code: return TBkOpen;
|
case "[".code: return TBkOpen;
|
||||||
case "]".code: return TBkClose;
|
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:
|
case "?".code:
|
||||||
char = readChar();
|
char = readChar();
|
||||||
switch (char) {
|
switch (char) {
|
||||||
@@ -2136,7 +2328,7 @@ class Parser {
|
|||||||
invalidChar(char);
|
invalidChar(char);
|
||||||
default:
|
default:
|
||||||
if( ops[char] ) {
|
if( ops[char] ) {
|
||||||
var op = String.fromCharCode(char);
|
var op:String = String.fromCharCode(char);
|
||||||
while( true ) {
|
while( true ) {
|
||||||
char = readChar();
|
char = readChar();
|
||||||
if( StringTools.isEof(char) ) char = 0;
|
if( StringTools.isEof(char) ) char = 0;
|
||||||
@@ -2146,6 +2338,11 @@ class Parser {
|
|||||||
}
|
}
|
||||||
var pop = op;
|
var pop = op;
|
||||||
op += String.fromCharCode(char);
|
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( !opPriority.exists(op) && opPriority.exists(pop) ) {
|
||||||
if( op == "//" || op == "/*" )
|
if( op == "//" || op == "/*" )
|
||||||
return tokenComment(op,char);
|
return tokenComment(op,char);
|
||||||
@@ -2336,7 +2533,7 @@ class Parser {
|
|||||||
return switch(c) {
|
return switch(c) {
|
||||||
case CInt(v): Std.string(v);
|
case CInt(v): Std.string(v);
|
||||||
case CFloat(f): Std.string(f);
|
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 TDoubleDot: ":";
|
||||||
case TMeta(id): "@" + id;
|
case TMeta(id): "@" + id;
|
||||||
case TPrepro(id): "#" + id;
|
case TPrepro(id): "#" + id;
|
||||||
|
case TRegex(e, f): '~/$e/$f';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,11 @@ class Printer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch(Tools.expr(e)) {
|
switch(Tools.expr(e)) {
|
||||||
|
case EPackage(n):
|
||||||
|
add('package');
|
||||||
|
if(n != null)
|
||||||
|
add(' $n');
|
||||||
|
add(';\n');
|
||||||
case EImport(c, n, u):
|
case EImport(c, n, u):
|
||||||
add('${u ? 'using' : 'import'} $c');
|
add('${u ? 'using' : 'import'} $c');
|
||||||
if(n != null)
|
if(n != null)
|
||||||
@@ -181,6 +186,9 @@ class Printer {
|
|||||||
addType(t);
|
addType(t);
|
||||||
add(")");
|
add(")");
|
||||||
}
|
}
|
||||||
|
case ERegex(e, f):
|
||||||
|
add('~/$e/$f');
|
||||||
|
add(';\n');
|
||||||
case EConst(c):
|
case EConst(c):
|
||||||
switch( c ) {
|
switch( c ) {
|
||||||
case CInt(i): add(i);
|
case CInt(i): add(i);
|
||||||
|
|||||||
+20
-6
@@ -28,31 +28,41 @@ import hscript.Expr.FieldPropertyAccess;
|
|||||||
@:access(hscript.Interp)
|
@:access(hscript.Interp)
|
||||||
@:structInit
|
@:structInit
|
||||||
class Property {
|
class Property {
|
||||||
|
private static inline var GET = 'get_';
|
||||||
|
private static inline var SET = 'set_';
|
||||||
|
|
||||||
public var r:Dynamic;
|
public var r:Dynamic;
|
||||||
public var getter:FieldPropertyAccess;
|
public var getter:FieldPropertyAccess;
|
||||||
public var setter:FieldPropertyAccess;
|
public var setter:FieldPropertyAccess;
|
||||||
|
|
||||||
|
public var isStatic(get, never):Bool;
|
||||||
|
function get_isStatic() {
|
||||||
|
return __isStatic && interp.allowStaticVariables;
|
||||||
|
}
|
||||||
|
|
||||||
var isVar:Bool;
|
var isVar:Bool;
|
||||||
var interp:Interp;
|
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.r = r;
|
||||||
this.getter = getter;
|
this.getter = getter;
|
||||||
this.setter = setter;
|
this.setter = setter;
|
||||||
this.isVar = isVar;
|
this.isVar = isVar;
|
||||||
|
this.__isStatic = isStatic;
|
||||||
this.interp = interp;
|
this.interp = interp;
|
||||||
}
|
}
|
||||||
|
|
||||||
var __allowReadAccess:Bool = false;
|
var __allowReadAccess:Bool = false;
|
||||||
var __allowWriteAccess:Bool = false;
|
var __allowWriteAccess:Bool = false;
|
||||||
var __allowSetGet:Null<Bool> = null;
|
var __allowSetGet:Null<Bool> = null;
|
||||||
|
final __isStatic:Bool = false;
|
||||||
|
|
||||||
public function callGetter(name:String) {
|
public function callGetter(name:String) {
|
||||||
switch (getter) {
|
switch (getter) {
|
||||||
case AGet | ADynamic:
|
case AGet | ADynamic:
|
||||||
var fName:String = 'get_$name';
|
var fName:String = '$GET$name';
|
||||||
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
if (!__allowReadAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
||||||
if (interp.varExists(fName)) {
|
if (varExists(fName)) {
|
||||||
return callAccessor(fName);
|
return callAccessor(fName);
|
||||||
} else
|
} else
|
||||||
interp.error(ECustom('Method $fName required by property $name is missing'));
|
interp.error(ECustom('Method $fName required by property $name is missing'));
|
||||||
@@ -73,9 +83,9 @@ class Property {
|
|||||||
public function callSetter(name:String, val:Dynamic) {
|
public function callSetter(name:String, val:Dynamic) {
|
||||||
switch (setter) {
|
switch (setter) {
|
||||||
case ASet | ADynamic:
|
case ASet | ADynamic:
|
||||||
var fName:String = 'set_$name';
|
var fName:String = '$SET$name';
|
||||||
if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
if (!__allowWriteAccess && (__allowSetGet != null && __allowSetGet || !interp.isBypassAccessor)) {
|
||||||
if (interp.varExists(fName))
|
if (varExists(fName))
|
||||||
return callAccessor(fName, [val], true);
|
return callAccessor(fName, [val], true);
|
||||||
else
|
else
|
||||||
interp.error(ECustom('Method $fName required by property $name is missing'));
|
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 {
|
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;
|
var rt:Dynamic = null;
|
||||||
if (fn != null && Reflect.isFunction(fn)) {
|
if (fn != null && Reflect.isFunction(fn)) {
|
||||||
if (isWrite) __allowWriteAccess = true;
|
if (isWrite) __allowWriteAccess = true;
|
||||||
@@ -111,4 +121,8 @@ class Property {
|
|||||||
|
|
||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function varExists(n:String) {
|
||||||
|
return isStatic ? interp.staticVariables.exists(n) : interp.variables.exists(n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user