Field Imports + Automatic Imports

This commit is contained in:
NeeEoo
2024-04-26 01:48:28 +02:00
parent 5ef937d6d4
commit 38634e1528
12 changed files with 286 additions and 134 deletions
+1
View File
@@ -1,3 +1,4 @@
--macro keep('IntIterator')
--macro hscript.macros.UsingHandler.init()
--macro hscript.macros.ClassExtendMacro.init()
--define HSCRIPT_NEW
+3 -3
View File
@@ -22,7 +22,7 @@
package hscript;
#if hscriptPos
class Error {
class Error_ {
public var e : ErrorDef;
public var pmin : Int;
public var pmax : Int;
@@ -41,7 +41,7 @@ class Error {
}
enum ErrorDef
#else
enum Error
enum Error_
#end
{
EInvalidChar( c : Int );
@@ -54,7 +54,7 @@ enum Error
EInvalidIterator( v : String );
EInvalidType( t : String );
EInvalidOp( op : String );
EInvalidAccess( f : String );
EInvalidAccess( f : String, ?on : String );
ECustom( msg : String );
EPreset( msg : ErrorMessage );
EInvalidClass( className : String);
+9 -1
View File
@@ -38,6 +38,8 @@ enum MapType {
UnknownMap;
}
typedef Error = hscript.Error.Error_;
#if hscriptPos
class Expr {
public var e : ExprDef;
@@ -89,10 +91,16 @@ enum Expr
EMeta( name : String, args : Array<Expr>, e : Expr );
ECheckType( e : Expr, t : CType );
EImport( c : String, ?as:String, ?f:String );
EImport( c : String, mode: KImportMode );
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
}
enum KImportMode {
INormal;
IAs( name : String );
IAll;
}
class ObjectField {
public var name:String;
public var e:Expr;
+71 -59
View File
@@ -159,10 +159,12 @@ class Interp {
variables.set("true", true);
variables.set("false", false);
#if !NO_FROM_CHAR_CODE_FIX
// DONT CALL THIS DIRECTLY, USE String.fromCharCode, the preprocessor will call it for you
variables.set("__StringWorkaround__fromCharCode", function(a:Int) { // TODO: make hscript only add this if its used
return String.fromCharCode(a);
});
if(!Preprocessor.isStringFromCharCodeFixed) {
// DONT CALL THIS DIRECTLY, USE String.fromCharCode, the preprocessor will call it for you
variables.set("__StringWorkaround__fromCharCode", function(a:Int) { // TODO: make hscript only add this if its used
return String.fromCharCode(a);
});
}
#end
variables.set("trace", Reflect.makeVarArgs(function(el) {
var inf = posInfos();
@@ -529,6 +531,22 @@ class Interp {
return null;
}
function getClass(c:String):haxe.ds.Either<Class<Any>, Enum<Any>> {
if (importBlocklist.contains(c))
return null;
var en = Type.resolveEnum(c);
if(en != null)
return Right(en);
var cl = Type.resolveClass(c);
if (cl == null)
cl = Type.resolveClass(c + '_HSC');
if(cl != null)
return Left(cl);
return null;
}
public function expr(e:Expr):Dynamic {
#if hscriptPos
curExpr = e;
@@ -546,73 +564,67 @@ class Interp {
return variable == null ? thing : Type.getClassName(variable);
}
customClasses.set(name, new CustomClassHandler(this, name, fields, importVar(extend), [for (i in interfaces) importVar(i)]));
case EImport(c, n):
case EImport(c, mode):
if (!importEnabled)
return null;
var splitClassName = [for (e in c.split(".")) e.trim()];
var realClassName = splitClassName.join(".");
var claVarName = splitClassName[splitClassName.length - 1];
var toSetName = n != null ? n : claVarName;
var oldClassName = realClassName;
var oldSplitName = splitClassName.copy();
if (variables.exists(toSetName)) // class is already imported
if(mode == IAll) {
throw "TODO";
return null;
}
var splitClassName = c.split(".");
var origSplitClassName = splitClassName.copy();
var field = null;
var varName = switch(mode) {
case IAs(name): name;
default: splitClassName[splitClassName.length - 1];
}
if (variables.exists(varName)) // class is already imported
return null;
if (importBlocklist.contains(realClassName))
return null;
var cl = Type.resolveClass(realClassName);
if (cl == null)
cl = Type.resolveClass('${realClassName}_HSC');
var en = Type.resolveEnum(realClassName);
//trace(realClassName, cl, en, splitClassName);
var cl = getClass(c);
// Allow for flixel.ui.FlxBar.FlxBarFillDirection;
if (cl == null && en == null) {
if(splitClassName.length > 1) {
splitClassName.splice(-2, 1); // Remove the last last item
realClassName = splitClassName.join(".");
if(cl == null && origSplitClassName.length > 1) {
splitClassName = origSplitClassName.copy();
if (importBlocklist.contains(realClassName))
return null;
cl = Type.resolveClass(realClassName);
if (cl == null)
cl = Type.resolveClass('${realClassName}_HSC');
en = Type.resolveEnum(realClassName);
//trace(realClassName, cl, en, splitClassName);
}
splitClassName.splice(-2, 1); // Remove the last last item
cl = getClass(splitClassName.join("."));
}
if (cl == null && en == null) {
if (importFailedCallback == null || !importFailedCallback(oldSplitName))
error(EInvalidClass(oldClassName));
} else {
if (en != null) {
// ENUM!!!!
var enumThingy = {};
for (c in en.getConstructors()) {
try {
UnsafeReflect.setField(enumThingy, c, en.createByName(c));
} catch(e) {
try {
UnsafeReflect.setField(enumThingy, c, Reflect.makeVarArgs((args:Array<Dynamic>) -> en.createByName(c, args)));
} catch(ex) {
throw e;
}
}
}
//var enumThingy = en;
variables.set(toSetName, enumThingy);
} else {
variables.set(toSetName, cl);
}
// Allow for Std.isOfType;
if(cl == null && origSplitClassName.length > 1) {
splitClassName = origSplitClassName.copy();
field = splitClassName.pop();
cl = getClass(splitClassName.join("."));
}
if (cl != null) {
var value:Dynamic = switch(cl) {
case Left(e): e;
case Right(e): Tools.getEnum(e);
}
if(field != null) { // import Std.isOfType;
var v:Dynamic = null;
if(v == null)
v = UnsafeReflect.getProperty(value, field);
if(v == null)
v = UnsafeReflect.field(value, field);
if(v == null)
error(EInvalidAccess(field, c));
value = v;
}
variables.set(varName, value);
return value; // If someone wants to import and use the value as a return, they can
}
if (importFailedCallback == null || !importFailedCallback(c.split("."))) // Incase of custom import
error(EInvalidClass(c));
return null;
case EConst(c):
+11 -19
View File
@@ -227,9 +227,8 @@ class Parser {
expr = Preprocessor.process(expr);
if(Parser.optimize) {
expr = Optimizer.optimize(expr);
var printer = new Printer();
trace("INPUT: " + s);
trace("OUTPUT: " + printer.exprToString(expr));
trace("OUTPUT: " + Printer.toString(expr));
}
return expr;
}
@@ -1049,27 +1048,21 @@ class Parser {
var oldReadPos = readPos;
var tk = token();
switch( tk ) {
case TPOpen:
case TPOpen: // Support legacy import() syntax
var tok = token();
switch(tok) {
case TConst(c):
switch(c) {
case CString(s):
token();
ensure(TSemicolon);
push(TSemicolon);
mk(EImport(s), p1);
default:
unexpected(tok);
null;
}
case TConst(CString(s)):
token();
ensure(TSemicolon);
push(TSemicolon);
mk(EImport(s, INormal), p1);
default:
unexpected(tok);
null;
}
case TId(id):
var path = [id];
var as:String = null;
var mode:KImportMode = INormal;
var t = null;
while( true ) {
t = token();
@@ -1078,20 +1071,19 @@ class Parser {
t = token();
switch( t ) {
case TId(id):
as = id;
mode = IAs(StringTools.trim(id));
default:
unexpected(t);
}
break;
}
push(t);
break;
}
t = token();
switch( t ) {
case TId(id):
path.push(id);
path.push(StringTools.trim(id));
default:
unexpected(t);
}
@@ -1099,7 +1091,7 @@ class Parser {
ensure(TSemicolon);
push(TSemicolon);
var p = path.join(".");
mk(EImport(p, as),p1);
mk(EImport(p, mode),p1);
default:
unexpected(tk);
null;
+80 -32
View File
@@ -9,12 +9,16 @@ import hscript.Parser;
class Preprocessor {
static inline function expr(e:Expr) return Tools.expr(e);
private static var importStack:Array<Array<String>> = [];
private static function addImport(e:String, ?as:String) {
for(i in importStack)
if(i[0] == e)
return;
importStack.push([e, as]);
private static var importStackName:Array<String> = [];
private static var importStackMode:Array<KImportMode> = [];
private static function addImport(e:String, mode:KImportMode = INormal) {
for(i in importStackName) if(i == e) return;
importStackName.push(e);
importStackMode.push(mode);
}
private static function popImport(e:Expr) {
return mk(EImport(importStackName.pop(), importStackMode.pop()), e);
}
/**
@@ -24,23 +28,22 @@ class Preprocessor {
* Also to automatically add imports for stuff that is not imported
**/
public static function process(e:Expr, top:Bool = true):Expr {
importStack = [];
importStackName = [];
importStackMode = [];
var e = _process(e, top);
// Automatically add imports for stuff
switch(expr(e)) {
case EBlock(exprs):
while(importStack.length > 0) {
var im = importStack.pop();
exprs.unshift(mk(EImport(im[0], im[1]), e));
while(importStackName.length > 0) {
exprs.unshift(popImport(e));
}
return mk(EBlock(exprs), e);
default:
if(importStack.length > 0) {
if(importStackName.length > 0) {
var exprs = [];
while(importStack.length > 0) {
var im = importStack.pop();
exprs.unshift(mk(EImport(im[0], im[1]), e));
while(importStackName.length > 0) {
exprs.unshift(popImport(e));
}
exprs.push(e);
return mk(EBlock(exprs), e);
@@ -53,11 +56,12 @@ class Preprocessor {
if(e == null)
return null;
//trace(expr(e));
// If stuff looks wrong, add this back
//e = Tools.map(e, function(e) {
// return _process(e, false);
//});
e = Tools.map(e, function(e) {
return _process(e, false);
});
//trace(expr(e));
switch(expr(e)) {
case EField(expr(_) => EConst(CString(s)), "code", _): // Transform string.code into charCode
@@ -65,27 +69,58 @@ class Preprocessor {
throw Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI));
}
return mk(EConst(CInt(s.charCodeAt(0))), e);
case ECall(expr(_) => EField(expr(_) => EIdent("String"), "fromCharCode", _), [e]):
switch(expr(e)) {
case ECall(expr(_) => EField(expr(_) => EIdent("String"), "fromCharCode", _), [e]): // Seperate this later?
switch(expr(e)) { // should this be Optimizer?
case EConst(CInt(i)):
return mk(EConst(CString(String.fromCharCode(i))), e);
default:
}
// __StringWorkaround__fromCharCode(i);
#if !NO_FROM_CHAR_CODE_FIX
return mk(ECall(mk(EIdent("__StringWorkaround__fromCharCode"), e), [e]), e);
#else
throw Parser.getBaseError(EPreset(FROM_CHAR_CODE_NON_INT));
#end
case ENew("String", _):
addImport("String");
case ENew("EReg", _):
addImport("EReg");
case EField(expr(_) => EIdent("EReg"), "escape", _):
addImport("EReg");
if(!Preprocessor.isStringFromCharCodeFixed) {
// __StringWorkaround__fromCharCode(i);
#if !NO_FROM_CHAR_CODE_FIX
return mk(ECall(mk(EIdent("__StringWorkaround__fromCharCode"), e), [e]), e);
#else
throw Parser.getBaseError(EPreset(FROM_CHAR_CODE_NON_INT));
#end
}
// Automatically add imports for stuff
case ENew("String", _): addImport("String");
case EIdent("String"): addImport("String");
case ENew("StringBuf", _): addImport("StringBuf");
case EIdent("StringBuf"): addImport("StringBuf");
case EIdent("Bool"): addImport("Bool");
case EIdent("Float"): addImport("Float");
case EIdent("Int"): addImport("Int");
case ENew("IntIterator", _): addImport("IntIterator");
case EIdent("IntIterator"): addImport("IntIterator");
case EIdent("Array"): addImport("Array");
case EIdent("Sys"): addImport("Sys");
case EIdent("Std"): addImport("Std");
case EIdent("Type"): addImport("Type");
case EIdent("Reflect"): addImport("Reflect");
case EIdent("StringTools"): addImport("StringTools");
case EIdent("Math"): addImport("Math");
case ENew("Date", _): addImport("Date");
case EIdent("Date"): addImport("Date");
case EIdent("DateTools"): addImport("DateTools");
case EIdent("Lambda"): addImport("Lambda");
case ENew("Xml", _): addImport("Xml");
case EIdent("Xml"): addImport("Xml");
//case EIdent("List"): addImport("haxe.ds.List");
case ENew("EReg", _): addImport("EReg");
case EIdent("EReg"): addImport("EReg");
//case EField(expr(_) => EIdent("EReg"), "escape", _):
// addImport("EReg");
default:
}
e = Tools.map(e, function(e) {
return _process(e, false);
});
return e;
}
@@ -96,4 +131,17 @@ class Preprocessor {
return e;
#end
}
public static var isStringFromCharCodeFixed(get, null):Null<Bool> = null;
static function get_isStringFromCharCodeFixed():Null<Bool> {
if(isStringFromCharCodeFixed == null) {
try {
Reflect.callMethod(null, Reflect.field(String, "fromCharCode"), [65]);
isStringFromCharCodeFixed = true;
} catch(e:Dynamic) {
isStringFromCharCodeFixed = false;
}
}
return isStringFromCharCodeFixed;
}
}
+7 -3
View File
@@ -205,10 +205,13 @@ class Printer {
// TODO: make else if print correctly
switch( Tools.expr(e) ) {
case EImport(c, n):
case EImport(c, mode):
add("import " + c);
if(n != null)
add(' as $n');
switch(mode) {
case IAs(name): add(' as $name');
case IAll: add('.*');
default:
}
case EClass(name, fields, extend, interfaces):
add('class $name');
if (extend != null)
@@ -540,6 +543,7 @@ class Printer {
case EInvalidIterator(v): "Invalid iterator: "+v;
case EInvalidType(t): "Invalid type: "+t;
case EInvalidOp(op): "Invalid operator: "+op;
case EInvalidAccess(f, on) if (on != null): "Invalid access to field " + f + " on " + on;
case EInvalidAccess(f): "Invalid access to field " + f;
case ECustom(msg): msg;
case EPreset(msg): msg.toString();
+18 -1
View File
@@ -21,13 +21,14 @@
*/
package hscript;
import hscript.Expr;
import hscript.utils.UnsafeReflect;
class Tools {
public static function iter( e : Expr, f : Expr -> Void ) {
switch( expr(e) ) {
case EConst(_), EIdent(_):
case EImport(c): f(e);
case EImport(c, _): f(e);
case EClass(_, e, _, _): for( a in e ) f(a);
case EVar(_, _, e): if( e != null ) f(e);
case EParent(e): f(e);
@@ -204,4 +205,20 @@ class Tools {
return -1;
}
public static function getEnum(cl:Enum<Dynamic>):Dynamic {
var enumThingy:Dynamic = {};
for (c in cl.getConstructors()) {
try {
UnsafeReflect.setField(enumThingy, c, cl.createByName(c));
} catch(e) {
try {
UnsafeReflect.setField(enumThingy, c, Reflect.makeVarArgs((args:Array<Dynamic>) -> cl.createByName(c, args)));
} catch(ex) {
throw e;
}
}
}
return enumThingy;
}
}
+10 -6
View File
@@ -1,4 +1,4 @@
import hscript.Error;
import hscript.Expr.Error;
import hscript.Printer;
import haxe.Constraints.IMap;
import haxe.EnumTools.EnumValueTools;
@@ -225,15 +225,19 @@ class Util {
return p.parseString(str);
}
public static var defaultImport = true;
public static function getInterp() {
var interp = new Interp();
//interp.importEnabled = true;
//interp.allowStaticVariables = true;
//interp.allowPublicVariables = true;
interp.variables.set("Math", Math);
interp.variables.set("Std", Std);
interp.variables.set("Type", Type);
interp.variables.set("StringTools", StringTools);
if(defaultImport) {
interp.variables.set("Math", Math);
interp.variables.set("Std", Std);
interp.variables.set("Type", Type);
interp.variables.set("StringTools", StringTools);
}
return interp;
}
@@ -273,7 +277,7 @@ class Util {
return nanoseconds + " ns";
}
public static function errorHandler(error:hscript.Error) {
public static function errorHandler(error:Error) {
#if hscriptPos
var fileName = error.origin;
var fn = '$fileName:${error.line}: ';
+31 -1
View File
@@ -5,10 +5,13 @@ class EnumCase extends TestCase {
super.setup();
}
var importEnabled = true;
override function getNewInterp() {
var interp = super.getNewInterp();
interp.variables.set("assertEq", Util.assertEq);
interp.variables.set("SomeEnum", SomeEnum);
if(importEnabled)
interp.variables.set("SomeEnum", SomeEnum);
interp.variables.set("Type", Type);
return interp;
}
@@ -24,6 +27,33 @@ class EnumCase extends TestCase {
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
importEnabled = false;
headerCode = "import tests.EnumCase.SomeEnum;";
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
headerCode = "import tests.SomeEnum;";
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
}
override function teardown() {
+42 -7
View File
@@ -6,14 +6,16 @@ class FinalCase extends TestCase {
}
override function getNewInterp() {
Util.defaultImport = false;
var interp = super.getNewInterp();
interp.variables.set("Std", Std);
interp.variables.set("String", String);
interp.variables.set("Bool", Bool);
interp.variables.set("Float", Float);
interp.variables.set("Array", Array);
interp.variables.set("Int", Int);
interp.variables.set("IntIterator", IntIterator);
Util.defaultImport = true;
//interp.variables.set("Std", Std);
//interp.variables.set("String", String);
//interp.variables.set("Bool", Bool);
//interp.variables.set("Float", Float);
//interp.variables.set("Array", Array);
//interp.variables.set("Int", Int);
//interp.variables.set("IntIterator", IntIterator);
return interp;
}
@@ -85,9 +87,42 @@ class FinalCase extends TestCase {
assertEq("[0=>'hello', 1=>'world'][1]", [0=>'hello', 1=>'world'][1]);
assertEq("[0=>'hello', 5=>'world'][1]", [0=>'hello', 5=>'world'][1]);
assertEq("
import tests.FinalCase;
FinalCase.test1();
", FinalCase.test1());
assertEq("
import tests.FinalCase as FC;
FC.test1();
", FinalCase.test1());
assertEq("
import tests.FinalCase as FC;
import tests.FinalCase.test;
FC.test1() + test();
", FinalCase.test1() + test());
assertEq("
//import Std.isOfType;
Std.isOfType('', String);
", Std.isOfType('', String));
// Test EOF with preprocessor
}
static function test1() {
return "hello";
}
static function test() {
return "world";
}
function assertDisplay(script:String, expected:Dynamic) {
var _script = script;
script = '"${script}";\n' + script;
+3 -2
View File
@@ -2,6 +2,7 @@ package tests;
import hscript.Printer;
import hscript.Tools;
import hscript.Expr.Error;
@:access(hscript.Interp)
@:access(hscript.Parser)
@@ -25,7 +26,7 @@ class TestCase extends HScriptRunner {
message = script;
try {
var result = Util.parseUnsafe(script);
} catch(e:hscript.Error) {
} catch(e:Error) {
var e = Printer.getPrintableError(e);
Sys.println("# Error trying to compile: " + script);
Sys.println("## Error: " + e);
@@ -34,7 +35,7 @@ class TestCase extends HScriptRunner {
return Util.passed();
}
public function assertError(script:String, expectedError:hscript.Error, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
public function assertError(script:String, expectedError:Error, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
var expectedError = Printer.getPrintableError(expectedError);
if(message == null)
message = script;