Compare commits

..
Author SHA1 Message Date
Jaime Humberto Macias Bustamante 9fa4a548e5 String Interpolation Support
implementation from hscript-iris (by inky03)

Note: it needs optimization
2025-09-06 19:39:57 -07:00
Jaime Humberto Macias Bustamante 18f4ebccd9 Fix small bug with slash literal ("\/") 2025-09-06 19:31:48 -07:00
Jaime Humberto Macias Bustamante 8dcebe3930 Regular Expression support (regex) 2025-09-06 19:31:48 -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
11 changed files with 377 additions and 40 deletions
+6 -1
View File
@@ -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.
----------- -----------
+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 -1
View File
@@ -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",
+4
View File
@@ -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
View File
@@ -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);
+11 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
} }
+206 -8
View File
@@ -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);
@@ -1619,6 +1644,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
var p1 = tokenMin; var p1 = tokenMin;
@@ -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 ) {
if(regex && prev == 92) {
b.addChar(c);
prev = c;
}
else
break; 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';
} }
} }
+8
View File
@@ -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
View File
@@ -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);
}
} }