Compare commits
15
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
-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
|
||||
|
||||
+10
-6
@@ -703,6 +703,7 @@ class Interp {
|
||||
var e = e.e;
|
||||
#end
|
||||
switch (e) {
|
||||
case EPackage(_):
|
||||
case EClass(name, fields, extend, interfaces, isFinal):
|
||||
var oldName:String = name;
|
||||
var hasAlias:Bool = (setAlias != null && beforeAlias == oldName);
|
||||
@@ -884,6 +885,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 +910,7 @@ class Interp {
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
isVar: isVar,
|
||||
isStatic: isStatic,
|
||||
interp: this,
|
||||
}
|
||||
}
|
||||
@@ -1426,7 +1430,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 +1453,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 +1463,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
|
||||
}
|
||||
|
||||
+206
-8
@@ -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);
|
||||
@@ -1619,6 +1644,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
|
||||
var p1 = tokenMin;
|
||||
@@ -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 )
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user