diff --git a/.gitignore b/.gitignore index 971f766..3c2abe7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ bin/ build/ report/ +tmp/ test/issues/generated/ run.n *.hxproj -out/ \ No newline at end of file +out/ +*.iml +.idea +.unittest/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..feea8e9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "haxeTestExplorer.testCommand": [ + "haxelib", + "run", + "munit", + "test", + "-neko" + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3d5ce68 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "hxml", + "file": "as3hx.hxml", + "problemMatcher": [ + "$haxe-absolute", + "$haxe", + "$haxe-error", + "$haxe-trace" + ] + }, + { + "label": "test", + "type": "shell", + "command": ["neko", "run", "test/", "out/", "-convert-flexunit", "."], //"-debug-inferred-type" + "dependsOn": "build", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 8e81b04..a3087cf 100644 --- a/README.md +++ b/README.md @@ -61,62 +61,56 @@ http://www.kirupa.com/forum/showthread.php?223798-ActionScript-3-Tip-of-the-Day/ E4X is currently partly done. This will fail in some cases, just examine source and output carefully. -#### For Initializations: -The output of - -```as3 -if (true) { - for (var i:uint = 0; i < 7; i++) - val += "0"; -} else { - for (i = 0; i < 8; i++) - val += "0"; -} -``` - -is - -```haxe -if (true) { - var i:UInt = 0; - while (i < 7) { - val += "0"; - i++; +### AS3 markup +You can use comments in ActionScript 3 code to enchance quality of conversion + +#### Haxe code injection +Use comment `/*haxe:*/` in arbitrary place in code to inject enclosed string as a raw Haxe code. + + /*haxe: + methodToCallInHaxe(); + */ + + var trueInHaxe:Boolean = /*haxe:true;//*/false; + +Use conditional compilation blocks to hide AS3 code from Haxe compiler + + CONFIG::AS3 { + methodToCallInAs3(); } -} else { - i = 0; - while (i < 8) { - val += "0"; - i++; + +#### Type hints +Use comment `/*haxe:*/` after AS3 type to override AS3 type with type from comment. + + var o:*/*haxe:utils.RawData*/ = {}; + +You can use it to define strict Haxe function types: + + function registerCallback(callback:Function/*haxe:Event->Void*/):void { } + + + +Use comment `/*<>*/` to force Haxe type parameters on AS3 types: + + var b:Dictionary = new Dictionary/**/(); + + public class ItemRenderer/**/ { } -} -``` - -As you can see, the scope of `i` in Flash is not the same as in Haxe, -so the `else` section will produce Unknown identifier : i. The solution -is to move the `var i : UInt = 0;` outside of the blocks in the generated -code. - -This can not be avoided by always creating the `i` variable, as the code - -```as3 -for (var i:uint = 0; i < 7; i++) - val += "0"; -for (i = 0; i < 8; i++) - val += "0"; -``` - -would then produce a double initialization of `i`, also causing a compiler error. - -```haxe -var i:UInt = 0; -while (i < 7) { - val += "0"; - i++; -} -var i = 0; -while (i < 8) { - val += "0"; - i++; -} -``` + +### Conversion tips +The bigger part of the complete project is converted at once, the better results you will get since type info takes a great role in conversion. + +You can chain src paths `neko as3hx.n "src\path\1" "src\path\2" "src\path\2"` if your project has more than one src path. + +Use `-libPath "path\to\as3\classes"` command line parameter to define path with all related code that should be taken into account during conversion but should not be converted by itself. The internal directory structure is not important since only package names are taken into consideration. + +For better results include interfaces to swc libraries that you use in your project. You can decompile swcs to get such interfaces. + +It's a good idea to include decompiled interfaces from playerglobal.swc to enhance interaction with Flash API. + + + +### Disclaimer +This fork is made for a single project in mind. So there are probably some breaking changes in formatting of converted code for different code styles. + +Also there are no publicly available test cases for added functionality. diff --git a/checkstyle.json b/checkstyle.json new file mode 100644 index 0000000..0be67b2 --- /dev/null +++ b/checkstyle.json @@ -0,0 +1,504 @@ +{ + "defaultSeverity": "INFO", + "checks": [ + { + "type": "Anonymous" + }, + { + "type": "ArrayAccess" + }, + { + "type": "ArrayLiteral" + }, + { + "type": "AvoidStarImport" + }, + { + "props": { + "format": "^(_|[a-z]*)$" + }, + "type": "CatchParameterName" + }, + { + "type": "ConditionalCompilation" + }, + { + "props": { + "ignoreExtern": true, + "tokens": [ + "INLINE" + ], + "format": "^[A-Z][A-Z0-9]*(_[A-Z0-9_]+)*$" + }, + "type": "ConstantName" + }, + { + "props": { + "thresholds": [ + { + "complexity": 32, + "severity": "WARNING" + }, + { + "complexity": 45, + "severity": "ERROR" + } + ] + }, + "type": "CyclomaticComplexity" + }, + { + "type": "DefaultComesLast" + }, + { + "type": "ERegLiteral" + }, + { + "props": { + "tokens": [ + "CLASS_DEF", + "ENUM_DEF", + "ABSTRACT_DEF", + "TYPEDEF_DEF", + "INTERFACE_DEF", + "OBJECT_DECL", + "FUNCTION", + "FOR", + "IF", + "WHILE", + "SWITCH", + "TRY", + "CATCH" + ], + "option": "empty" + }, + "type": "EmptyBlock" + }, + { + "props": { + "max": 1, + "requireEmptyLineAfterAbstract": false, + "requireEmptyLineAfterClass": false + }, + "type": "EmptyLines" + }, + { + "type": "EmptyPackage" + }, + { + "props": { + "max": 2000 + }, + "type": "FileLength" + }, + { + "props": { + "option": "upperCase" + }, + "type": "HexadecimalLiteral" + }, + { + "props": { + "ignoreSetter": true, + "ignoreFormat": "^(main|run|new)$", + "ignoreConstructorParameter": true + }, + "type": "HiddenField" + }, + { + "props": { + "character": "space" + }, + "type": "IndentationCharacter" + }, + { + "type": "InnerAssignment" + }, + { + "props": { + "allowMarkerInterfaces": true, + "allowProperties": false + }, + "type": "Interface" + }, + { + "props": { + "tokens": [ + "CLASS_DEF", + "ENUM_DEF", + "ABSTRACT_DEF", + "TYPEDEF_DEF", + "INTERFACE_DEF", + "FUNCTION", + "FOR", + "IF", + "WHILE", + "SWITCH", + "TRY", + "CATCH" + ], + "ignoreEmptySingleline": true, + "option": "eol" + }, + "type": "LeftCurly" + }, + { + "props": { + "max": 160, + "ignorePattern": "(^@desc)|(LONG LINE TEST)" + }, + "type": "LineLength" + }, + { + "props": { + "listeners": [ + "addEventListener", + "addListener", + "on", + "once" + ], + "format": "^_?[a-z][a-zA-Z0-9]*$" + }, + "type": "ListenerName" + }, + { + "props": { + "ignoreExtern": true, + "format": "^[_a-z][_a-zA-Z0-9]*$" + }, + "type": "LocalVariableName" + }, + { + "props": { + "ignoreNumbers": [-1, 0, 1, 2, 3, 5, 8, 13, 21, 34, 100, 1000] + }, + "type": "MagicNumber" + }, + { + "props": { + "ignoreExtern": true, + "format": "^[_a-z][_a-zA-Z0-9]*$", + "tokens": [ + "CLASS", + "PUBLIC", + "PRIVATE", + "TYPEDEF" + ] + }, + "type": "MemberName" + }, + { + "props": { + "ignoreExtern": true, + "format": "^[A-Z][A-z0-9_]*$", + "tokens": [ + "ENUM" + ] + }, + "type": "MemberName" + }, + { + "props": { + "maxPrivate": 50, + "maxPublic": 50, + "maxTotal": 50 + }, + "type": "MethodCount" + }, + { + "props": { + "max": 100 + }, + "type": "MethodLength" + }, + { + "props": { + "ignoreExtern": true, + "format": "^[_a-z][_a-zA-Z0-9]*$" + }, + "type": "MethodName" + }, + { + "props": { + "modifiers": [ + "MACRO", + "OVERRIDE", + "PUBLIC_PRIVATE", + "STATIC", + "INLINE", + "DYNAMIC" + ] + }, + "type": "ModifierOrder" + }, + { + "props": { + "minLength": 2, + "ignore": "^\\s+$", + "allowDuplicates": 2 + }, + "type": "MultipleStringLiterals" + }, + { + "type": "MultipleVariableDeclarations" + }, + { + "type": "NestedForDepth" + }, + { + "type": "NestedTryDepth" + }, + { + "props": { + "assignOpPolicy": "around", + "unaryOpPolicy": "none", + "ternaryOpPolicy": "around", + "arithmeticOpPolicy": "around", + "compareOpPolicy": "around", + "bitwiseOpPolicy": "around", + "boolOpPolicy": "around", + "intervalOpPolicy": "none", + "arrowPolicy": "around", + "functionArgPolicy": "around" + }, + "type": "OperatorWhitespace" + }, + { + "props": { + "tokens": [ + "=", + "+", + "-", + "*", + "/", + "%", + ">", + "<", + ">=", + "<=", + "==", + "!=", + "&", + "|", + "^", + "&&", + "||", + "<<", + ">>", + ">>>", + "+=", + "-=", + "*=", + "/=", + "%=", + "<<=", + ">>=", + ">>>=", + "|=", + "&=", + "^=", + "...", + "=>", + "++", + "--" + ], + "option": "eol" + }, + "type": "OperatorWrap" + }, + { + "props": { + "ignoreExtern": true, + "format": "^(_|[a-z][a-zA-Z0-9]*$)" + }, + "type": "ParameterName" + }, + { + "props": { + "max": 7, + "ignoreOverriddenMethods": false + }, + "type": "ParameterNumber" + }, + { + "props": { + "allowEmptyReturn": true, + "enforceReturnType": true + }, + "type": "Return" + }, + { + "props": { + "dotPolicy": "none", + "commaPolicy": "after", + "semicolonPolicy": "after" + }, + "type": "SeparatorWhitespace" + }, + { + "props": { + "tokens": [ + ",", + "." + ], + "option": "eol" + }, + "type": "SeparatorWrap" + }, + { + "type": "SimplifyBooleanExpression" + }, + { + "type": "SimplifyBooleanReturn" + }, + { + "props": { + "spaceIfCondition": true, + "spaceAroundBinop": true, + "spaceForLoop": true, + "ignoreRangeOperator": true, + "spaceWhileLoop": true, + "spaceCatch": true, + "spaceSwitchCase": true, + "noSpaceAroundUnop": true + }, + "type": "Spacing" + }, + { + "type": "TODOComment" + }, + { + "type": "TabForAligning" + }, + { + "props": { + "ignoreEnumAbstractValues": true + }, + "type": "Type" + }, + { + "props": { + "ignoreExtern": true, + "format": "^[A-Z]+[a-zA-Z0-9]*$" + }, + "type": "TypeName" + }, + { + "type": "UnnecessaryConstructor" + }, + { + "props": { + "tokens": [ + ",", + ";" + ] + }, + "type": "WhitespaceAfter" + }, + { + "props": { + "tokens": [ + "=", + "+", + "-", + "*", + "/", + "%", + ">", + "<", + ">=", + "<=", + "==", + "!=", + "&", + "|", + "^", + "&&", + "||", + "<<", + ">>", + ">>>", + "+=", + "-=", + "*=", + "/=", + "%=", + "<<=", + ">>=", + ">>>=", + "|=", + "&=", + "^=", + "=>" + ] + }, + "type": "WhitespaceAround" + }, + { + "type": "StringLiteral", + "props": { + "policy": "onlyDouble", + "allowException": true, + "severity": "WARNING" + } + } + ], + "exclude": { + "all": [ + "Sys" + ], + "Dynamic": [ + "checkstyle.Main", + "checkstyle.Checker", + "checkstyle.ChecksInfo", + "checkstyle.checks.Check", + "checkstyle.checks.Directive", + "checkstyle.checks.whitespace.SpacingCheck", + "checkstyle.checks.imports.UnusedImportCheck", + "TestMain", + "misc.ExtensionsTest", + "token.TokenTreeBuilderParsingTest" + ], + "MultipleStringLiterals": [ + "checks", + "token" + ], + "CyclomaticComplexity": [ + "checkstyle.checks.Check", + "checkstyle.utils.ExprUtils", + "checkstyle.utils.ComplexTypeUtils", + "checkstyle.checks.metrics.CyclomaticComplexityCheck", + "checkstyle.checks.block.LeftCurlyCheck", + "checkstyle.checks.block.RightCurlyCheck", + "checkstyle.checks.naming.MethodNameCheck", + "checkstyle.checks.type.ReturnCheck", + "checkstyle.checks.whitespace.OperatorWrapCheck", + "checkstyle.checks.whitespace.WhitespaceAfterCheck", + "checkstyle.checks.whitespace.WhitespaceAroundCheck", + "checkstyle.checks.block.EmptyBlockCheck", + "checkstyle.checks.whitespace.EmptyLinesCheck", + "checkstyle.token.walk.WalkStatement" + ], + "LeftCurly": [ + "checkstyle.checks.metrics.CyclomaticComplexityCheck" + ], + "RightCurly": [ + "checkstyle.checks.metrics.CyclomaticComplexityCheck" + ], + "MethodCount": [ + "checkstyle.token.TokenTreeBuilder" + ], + "MethodLength": [ + "checkstyle.checks.whitespace.WhitespaceAfterCheck", + "checkstyle.checks.whitespace.WhitespaceAroundCheck", + "checkstyle.checks.whitespace.EmptyLinesCheck", + "checkstyle.token.walk.WalkStatement" + ], + "NestedForDepth": [ + "TestMain" + ], + "MemberName": [ + "checkstyle.Main" + ] + } +} \ No newline at end of file diff --git a/src/AS3.hx b/src/AS3.hx new file mode 100644 index 0000000..805f1ed --- /dev/null +++ b/src/AS3.hx @@ -0,0 +1,190 @@ +package; +import haxe.macro.Context; +import haxe.macro.Expr.ExprOf; +import haxe.macro.Expr; + +/** + * ... + * @author + */ +class AS3 { + + public static function str(e:Dynamic):String { + var t:Class = Type.getClass(e); + return t == null ? Std.string(e) : "[object " + Type.getClassName(t) + "]"; + } + + public static inline function asDynamic(e:Dynamic):Dynamic { + return Std.is(e, haxe.Constraints.IMap) ? e : null; + } + + public static inline function asDictionary(e:Dynamic):Dynamic { + return Std.is(e, haxe.Constraints.IMap) ? untyped e : null; + } + + #if openfl + public static inline function asObject(e:Dynamic):openfl.utils.Object { + return untyped e; + } + #end + + public static inline function asClass(e:Dynamic, t:Class):T { + return Std.is(e, t) ? untyped e : null; + } + + public static inline function asFloat(e:Dynamic):Float { + return as3hx.Compat.parseFloat(e); + } + + public static inline function asBool(e:Dynamic):Bool { + #if (js || flash) + return untyped Boolean(e); + #else + return e != false && e != null && e != 0 && !(Std.is(e, String) && e.length == 0); + #end + } + + + public static inline function asArray(e:Dynamic):Array { + return Std.is(e, Array) ? untyped e : null; + } + + public static function AS(e:Dynamic, type:Dynamic):Dynamic { + return Std.is(e, type) ? e : null; + } + + public static macro function as(e:Expr, type:Expr):Expr { + return switch(type.expr) { + case EConst(CIdent("Dictionary")): macro AS3.asDictionary($e); + #if openfl + case EConst(CIdent("Object")): macro AS3.asObject($e); + #end + case EConst(CIdent("Float")): macro AS3.asFloat($e); + case EConst(CIdent("Bool")): macro AS3.asBool($e); + default: macro AS3.asClass($e, $type); + } + } + + public static inline function hasOwnProperty(o:Dynamic, field:String):Bool { + #if js + var tmp; + if(o == null) { + return false; + } else { + var tmp1; + if(untyped o.__properties__ && o.__properties__["get_" + field]) { + return true; + } else if (Reflect.hasField(o, field)) { + return true; + } else if (untyped o.prototype && untyped o.prototype.hasOwnProperty(field)) { + return true; + } else if (untyped o.__proto__ && untyped o.__proto__.hasOwnProperty(field)) { + return true; + } else { + return false; + } + } + #elseif flash + if (o == null) { + return null; + } else { + return untyped o.hasOwnProperty(field); + } + #else + if (o == null) { + return false; + } else { + return Type.getInstanceFields(Type.getClass(o)).indexOf(field) != -1; + } + #end + } + + /* AS3.string(null) == null but Std.string(null) == "null" */ + public static function string(o:Dynamic):String { + #if js + untyped { + if( o == null ) + return null;//not "null"! this is for another purposes + var t = __js__("typeof(o)"); + if( t == "function" && (js.Boot.isClass(o) || js.Boot.isEnum(o)) ) + t = "object"; + switch( t ) { + case "object": + if( __js__("o instanceof Array") ) { + if( o.__enum__ ) { + if( o.length == 2 ) + return o[0]; + var str = o[0]+"("; + for( i in 2...o.length ) { + if( i != 2 ) + str += "," + js.Boot.__string_rec(o[i],"\t"); + else + str += js.Boot.__string_rec(o[i],"\t"); + } + return str + ")"; + } + var l = o.length; + var i; + var str = "["; + for( i in 0...l ) + str += (if (i > 0) "," else "") + js.Boot.__string_rec(o[i],"\t"); + str += "]"; + return str; + } + var tostr; + try { + tostr = untyped o.toString; + } catch( e : Dynamic ) { + // strange error on IE + return "???"; + } + if( tostr != null && tostr != __js__("Object.toString") && __typeof__(tostr) == "function" ) { + var s2 = o.toString(); + //if( s2 != "[object Object]") + return s2; + } + var t:Class = Type.getClass(o); + return t == null ? Std.string(o) : "[object " + Type.getClassName(t) + "]"; + case "function": + return ""; + default: + return String(o); + } + } + #else + return o == null ? null : Std.string(o); + #end + } + + /** + * Converts a typed expression into an Int. + */ + macro public static function int(e:ExprOf, ?base:ExprOf):ExprOf { + return macro untyped ~~${e}; + } + + public static function defaultSort(a:Dynamic, b:Dynamic):Int { + return if (Std.is(a, String)) + defaultStringSort(a, b); + else if (Std.is(a, Int)) + defaultIntSort(a, b); + else + throw 'Unsupported'; + } + + public static function defaultStringSort(a:String, b:String):Int { + var min:Int = Std.int(Math.min(a.length, b.length)); + a = a.toLowerCase(); + b = b.toLowerCase(); + for (i in 0...min) { + var r:Int = a.charCodeAt(i) - a.charCodeAt(i); + if (r != 0) return r; + } + return 0; + } + + public static function defaultIntSort(a:Int, b:Int):Int { + return a - b; + } + +} \ No newline at end of file diff --git a/src/FastXML.hx b/src/FastXML.hx index d14dd28..5f55444 100644 --- a/src/FastXML.hx +++ b/src/FastXML.hx @@ -1,3 +1,4 @@ +import haxe.xml.Fast; private class NodeAccess implements Dynamic { var __x : Xml; @@ -30,7 +31,7 @@ private class AttribAccess implements Dynamic { throw "Cannot access document attribute "+name; var v = __x.get(name); if( v == null ) - throw __x.nodeName+" is missing attribute "+name; + return ""; return v; } @@ -76,7 +77,7 @@ private class NodeListAccess implements Dynamic { public function resolve( name : String ) : FastXMLList { var l = new Array(); - for( x in __x.elementsNamed(name) ) + for( x in __x.elementsNamed(name) ) l.push(new FastXML(x)); return new FastXMLList(l); } @@ -186,12 +187,12 @@ class FastXML { return 1; } - public function setAttribute(name:String, value:String) : Void { + public function setAttribute(name:String, value:Dynamic) : Void { if( x.nodeType == Xml.Document ) throw "Cannot access document attribute "+name; - x.set(name,value); + x.set(name,Std.string(value)); } - + public function toString() : String { return x.toString(); } @@ -201,6 +202,21 @@ class FastXML { return new FastXML(x.firstChild()); } + #if openfl + public static function parseByteArray(s:openfl.utils.ByteArray) : FastXML { + s.position = 0; + var ss:String = s.readMultiByte(s.length, null); + var x = Xml.parse(ss); + var lastNode:Xml = null; + for (e in x) { + if (e.nodeType == Xml.XmlType.Element) { + lastNode = e; + } + } + return new FastXML(lastNode); + } + #end + public static function filterNodes(a : FastXMLList, f : FastXML -> Bool) : FastXMLList { var rv = new Array(); for(i in a) diff --git a/src/FastXMLList.hx b/src/FastXMLList.hx index e42fedc..5e492fa 100644 --- a/src/FastXMLList.hx +++ b/src/FastXMLList.hx @@ -38,18 +38,15 @@ class FastXMLList { public function iterator() : Iterator { return l.iterator(); } - + public function length() : Int { return l.length; } - /** - * public function set(i:Int, v:FastXML) : FastXML { l[i] = v; return v; } - */ public function toString() : String { var s = ""; diff --git a/src/FileParser.hx b/src/FileParser.hx new file mode 100644 index 0000000..9b65eae --- /dev/null +++ b/src/FileParser.hx @@ -0,0 +1,78 @@ +package; +import as3hx.Config; +import as3hx.ParserUtils; +import haxe.io.Path; +import sys.FileSystem; + +/** + * ... + * @author + */ +class FileParser { + private var cfg:Config; + private var fileExtension:String; + public function new(cfg:Config, fileExtension:String) { + this.cfg = cfg; + this.fileExtension = fileExtension; + if (this.fileExtension.indexOf(".") != 0) this.fileExtension = "." + fileExtension; + } + + public function parseDirectory(src:String, excludes:List, handler:String->String->String->String->Void, relativeDestination:String = "/"):Void { + src = Path.normalize(src); + relativeDestination = Path.normalize(relativeDestination); + var subDirList = new Array(); + for (childName in FileSystem.readDirectory(src)) { + var childPath = Path.addTrailingSlash(src) + childName; + if (FileSystem.isDirectory(childPath)) { + subDirList.push(childName); + } else if (StringTools.endsWith(childName, fileExtension) && !isExcludeFile(excludes, childPath)) { + handler(src, childName, childPath, relativeDestination); + } + } + for (name in subDirList) { + parseDirectory((Path.addTrailingSlash(src) + name), excludes, handler, (Path.addTrailingSlash(relativeDestination) + ParserUtils.escapeName(name))); + } + } + + static function isExcludeFile(excludes: List, file: String):Bool { + var filePath:String = as3hx.Config.toPath(file); + return Lambda.filter(excludes, function (path) { + var excludePath:String = StringTools.replace(path, ".", "\\"); + var excludeIndex:Int = filePath.indexOf(excludePath); + var excludeEndIndex:Int = excludeIndex + excludePath.length; + if (excludeIndex > -1) { + if (excludeEndIndex < filePath.length) { + var nextCharCode:Int = filePath.charCodeAt(excludeEndIndex); + if (nextCharCode == ".".code || nextCharCode == "\\".code) { + return true; + } else { + return false; + } + } else { + return true; + } + } + return false; + }).length > 0; + } + + public static function ensureDirectoryExists(dir:String):Void { + var pathToCreate = []; + while (!FileSystem.exists(dir) && dir != '') { + var parts = dir.split("/"); + pathToCreate.unshift(parts.pop()); + dir = parts.join("/"); + } + for (part in pathToCreate) { + if (part == '') + continue; + dir += "/" + part; + try { + FileSystem.createDirectory(dir); + } catch (e : Dynamic) { + throw "unable to create dir: " + dir; + } + } + } + +} \ No newline at end of file diff --git a/src/Run.hx b/src/Run.hx index 7e8b891..33b845a 100644 --- a/src/Run.hx +++ b/src/Run.hx @@ -1,14 +1,209 @@ +import haxe.Json; using StringTools; -import as3hx.Writer; +import as3hx.As3.Program; +import as3hx.Config; import as3hx.Error; +import as3hx.Writer; import sys.FileSystem; import sys.io.File; using haxe.io.Path; class Run { - - static function errorString(e : Error) { + + static var errors:Array = []; + static var warnings:Map> = new Map(); + static var cfg:as3hx.Config; + static var writer:Writer; + static var currentDstPath:String; + static var files:Array = []; + static var mxmlRoot:String; + static var mxmlRel:String; + static var mxmlMain:String; + static var mxmlFiles:Array = []; + static var mxmlTmpPath:String = 'tmp/'; + static var mxmlGenPath:String = mxmlTmpPath + 'generated/'; + + public static function main():Void { + Sys.setCwd(Sys.args().pop()); + cfg = new as3hx.Config(); + if (cfg.useFullTyping) { + writer = new Writer(cfg); + } + var fileParser:FileParser = new FileParser(cfg, ".as"); + var mxmlParser:FileParser = new FileParser(cfg, ".mxml"); + var libExcludes:List = new List(); + for (libPath in cfg.libPaths) { + if (libPath == null) { + Sys.println("lib path cannot be null"); + } + fileParser.parseDirectory(libPath, libExcludes, parseLibFile); + } + for (i in 0...cfg.src.length) { + var src:String = cfg.src[i]; + var dst:String = cfg.dst[i]; + if (src == null) { + Sys.println("source path cannot be null"); + } + if (dst == null) { + Sys.println("destination path cannot be null"); + } + currentDstPath = Path.removeTrailingSlashes(Path.normalize(dst)); + fileParser.parseDirectory(src, cfg.excludePaths, parseSrcFile); + mxmlParser.parseDirectory(src, cfg.excludePaths, addMxmlToList); + } + if (mxmlFiles.length > 1) { + mxmlFiles.remove(mxmlMain); + var asconfig:String = "asconfig.json"; + var r:Int = if (FileSystem.exists(asconfig)) { + var flex:String = Sys.getEnv("FLEX_HOME"); + if (flex == null) flex = Sys.getEnv("FLEX"); + if (flex == null) exit(1, "FLEX home not set!"); + var asc:String = File.getContent(asconfig); + if (asc.indexOf("keep-generated-actionscript") == -1) exit(1, "keep-generated-actionscript not set in asconfig.json"); + var json = Json.parse(asc); + var output:String = json.compilerOptions.output; + mxmlTmpPath = output.substr(0, output.lastIndexOf("/") + 1); + mxmlGenPath = mxmlTmpPath + "generated/"; + mxmlRoot = cfg.src[0]; + Sys.command("asconfigc", ["--sdk", flex]); + } else { + Sys.command("mxmlc", [mxmlMain, "--output", mxmlTmpPath + "tmp.swf", "--keep-generated-actionscript"]); + } + if (r != 0) exit(r); + var mxmls:Array = [for (f in mxmlFiles) f.substr(mxmlRoot.length + 1)]; + var map:Map = [for (e in mxmls) + mxmlGenPath + e.substr(0, -5) + "-generated.as" => e.split("/").pop().substr(0, -5) + ".as"]; + cleanTmp(mxmlTmpPath, map); + renameTmp(mxmlTmpPath, map); + var prevDst:String = currentDstPath; + currentDstPath = currentDstPath + mxmlRel; + fileParser.parseDirectory(mxmlGenPath, cfg.excludePaths, parseSrcFile); + currentDstPath = prevDst; + } + + //loop(cfg.src, cfg.dst, cfg.excludePaths); + if (cfg.useFullTyping) { + writer.prepareTyping(); + if (cfg.useOpenFlTypes) { + for (f in files) { + writer.refineTypes(f.program); + } + for (f in files) { + writer.applyRefinedTypes(f.program); + } + } + writer.finishTyping(); + for (f in files) { + writeFile(f.name, f.program, cfg, f.f, f.src); + } + } + Sys.println(""); + Writer.showWarnings(warnings); + Sys.println(""); + if(errors.length > 0) { + Sys.println("ERRORS: These files were not written due to source parsing errors:"); + for(i in errors) + Sys.println(i); + } + exit(); + } + + static function exit(code:Int = 0, ?message:String):Void { + if (message != null) Sys.println(message); + #if neko + if (Sys.systemName() == 'Linux') Sys.sleep(1); + #end + Sys.exit(code); + } + + static function cleanTmp(path:String, ignore:Map):Void { + for (element in FileSystem.readDirectory(path)) { + if (FileSystem.isDirectory(path + element)) { + cleanTmp(path + element + '/', ignore); + } else { + var file:String = path + element; + if (!ignore.exists(file)) { + trace('Delete: $file'); + FileSystem.deleteFile(file); + } + } + } + } + + static function renameTmp(path:String, map:Map):Void { + for (element in FileSystem.readDirectory(path)) { + if (FileSystem.isDirectory(path + element)) { + renameTmp(path + element + '/', map); + } else { + var file:String = path + element; + if (map.exists(file)) { + trace('Rename: $file => ' + path + map[file]); + FileSystem.rename(file, path + map[file]); + } + } + } + } + + static function addMxmlToList(fileLocation:String, fileName:String, file:String, relativeDestination:String):Void { + if (mxmlRoot == null || fileLocation.length < mxmlRoot.length) { + mxmlRoot = fileLocation; + mxmlMain = file; + mxmlRel = relativeDestination; + } + mxmlFiles.push(file); + } + + static function parseLibFile(fileLocation:String, fileName:String, file:String, relativeDestination:String):Void { + Sys.println("import AS3 file: " + file); + var program = parseFile(fileLocation, fileName, file); + if (program == null) return; + writer.register(program); + } + + static function parseSrcFile(fileLocation:String, fileName:String, file:String, relativeDestination:String):Void { + Sys.println("source AS3 file: " + file); + var program = parseFile(fileLocation, fileName, file); + if (program == null) return; + var dst:String = currentDstPath + relativeDestination; + FileParser.ensureDirectoryExists(dst); + var resultPath = Path.addTrailingSlash(dst) + Writer.properCase(fileName.substr(0, -3), true) + ".hx"; + + if (cfg.useFullTyping) { + writer.register(program); + files.push(new FileEntry(program, resultPath, fileName, fileLocation)); + } else { + if (!cfg.useFullTyping) { + writer = new Writer(cfg); + } + writeFile(resultPath, program, cfg, fileName, fileLocation); + } + } + + static function parseFile(fileLocation:String, fileName:String, file:String):Program { + var p = new as3hx.Parser(cfg); + var content = File.getContent(file); + return try { + p.parseString(content, fileLocation, fileName); + } catch (e : Error) { + #if macro + File.stderr().writeString(file + ":" + p.tokenizer.line + ": " + errorString(e) + "\n"); + #end + if (cfg.errorContinue) { + errors.push("In " + file + "(" + p.tokenizer.line + ") : " + errorString(e)); + null; + } else { + #if neko + neko.Lib.rethrow("In " + file + "(" + p.tokenizer.line + ") : " + errorString(e)); + #elseif cpp + cpp.Lib.rethrow("In " + file + "(" + p.tokenizer.line + ") : " + errorString(e)); + null; + #end + } + } + } + + static function errorString(e : Error):String { return switch(e) { case EInvalidChar(c): "Invalid char '" + String.fromCharCode(c) + "' 0x" + StringTools.hex(c, 2); case EUnexpected(src): "Unexpected " + src; @@ -17,67 +212,23 @@ class Run { case EUnterminatedXML: "Unterminated XML"; } } - - static function loop(src:String, dst:String, excludes:List) { - if (src == null) { - Sys.println("source path cannot be null"); - } - if (dst == null) { - Sys.println("destination path cannot be null"); - } - src = src.normalize(); - dst = dst.normalize(); - var subDirList = new Array(); - var writer = new Writer(cfg); - for(f in FileSystem.readDirectory(src)) { - var srcChildAbsPath = src.addTrailingSlash() + f; - var dstChildAbsPath = dst.addTrailingSlash() + f; - if (FileSystem.isDirectory(srcChildAbsPath)) { - subDirList.push(f); - } else if(f.endsWith(".as") && !isExcludeFile(excludes, srcChildAbsPath)) { - var file = srcChildAbsPath; - Sys.println("source AS3 file: " + file); - var p = new as3hx.Parser(cfg); - var content = File.getContent(file); - var program = try p.parseString(content, src, f) catch(e : Error) { - #if macro - File.stderr().writeString(file + ":" + p.tokenizer.line + ": " + errorString(e) + "\n"); - #end - if(cfg.errorContinue) { - errors.push("In " + file + "(" + p.tokenizer.line + ") : " + errorString(e)); - continue; - } else { - #if neko - neko.Lib.rethrow("In " + file + "(" + p.tokenizer.line + ") : " + errorString(e)); - #elseif cpp - cpp.Lib.rethrow("In " + file + "(" + p.tokenizer.line + ") : " + errorString(e)); - null; - #end - } - } - var out = dst; - ensureDirectoryExists(out); - var name = out.addTrailingSlash() + Writer.properCase(f.substr(0, -3), true) + ".hx"; - Sys.println("target HX file: " + name); - var fw = File.write(name, false); - warnings.set(name, writer.process(program, fw)); - fw.close(); - if(cfg.postProcessor != "") { - postProcessor(cfg.postProcessor, name); - } - if(cfg.verifyGeneratedFiles) { - verifyGeneratedFile(f, src, name); - } - } + + static function writeFile(name:String, program:Program, cfg:Config, f:String, src:String):Void { + Sys.println("target HX file: " + name); + var fw = File.write(name, false); + warnings.set(name, writer.process(program, fw)); + fw.close(); + if(cfg.postProcessor != "") { + postProcessor(cfg.postProcessor, name); } - for (name in subDirList) { - loop((src.addTrailingSlash() + name), (dst.addTrailingSlash() + name), excludes); + if(cfg.verifyGeneratedFiles) { + verifyGeneratedFile(f, src, name); } } - static function postProcessor(?postProcessor:String = "", ?outFile:String = "") { + static function postProcessor(?postProcessor:String = "", ?outFile:String = ""):Void { if(postProcessor != "" && outFile != "") { - Sys.println('Running post-processor ' + postProcessor + ' on file: ' + outFile); + Sys.println("Running post-processor " + postProcessor + " on file: " + outFile); Sys.command(postProcessor, [outFile]); } } @@ -85,69 +236,29 @@ class Run { //if a .hx file with the same name as the .as file is found in the .as //file directory, then it is considered the expected output of the conversion //and is diffed against the actual output - static function verifyGeneratedFile(file:String, src:String, outFile:String) { + static function verifyGeneratedFile(file:String, src:String, outFile:String):Void { var test = src.addTrailingSlash() + Writer.properCase(file.substr(0, -3), true) + ".hx"; if (FileSystem.exists(test) && FileSystem.exists(outFile)) { Sys.println("expected HX file: " + test); var expectedFile = File.getContent(test); var generatedFile = File.getContent(outFile); if (generatedFile != expectedFile) { - Sys.println('Don\'t match generated file:' + outFile); - Sys.command('diff', [test, outFile]); + Sys.println("Don't match generated file:" + outFile); + Sys.command("diff", [test, outFile]); } } } +} - static function isExcludeFile(excludes: List, file: String) - return Lambda.filter(excludes, function (path) return as3hx.Config.toPath(file).indexOf(path.replace(".", "/")) > -1).length > 0; - - static var errors : Array = new Array(); - static var warnings : Map> = new Map(); - static var cfg : as3hx.Config; - - public static function main() { - cfg = new as3hx.Config(); - loop(cfg.src, cfg.dst, cfg.excludePaths); - Sys.println(""); - Writer.showWarnings(warnings); - Sys.println(""); - if(errors.length > 0) { - Sys.println("ERRORS: These files were not written due to source parsing errors:"); - for(i in errors) - Sys.println(i); - } - } - - static function ensureDirectoryExists(dir : String) { - var tocreate = []; - while (!FileSystem.exists(dir) && dir != '') - { - var parts = dir.split("/"); - tocreate.unshift(parts.pop()); - dir = parts.join("/"); - } - for (part in tocreate) - { - if (part == '') - continue; - dir += "/" + part; - try { - FileSystem.createDirectory(dir); - } catch (e : Dynamic) { - throw "unable to create dir: " + dir; - } - } +class FileEntry { + public var program(default, null):Program; + public var name(default, null):String; + public var f(default, null):String; + public var src(default, null):String; + public function new(program:Program, name:String, f:String, src:String):Void { + this.program = program; + this.name = name; + this.f = f; + this.src = src; } - - static var reabs = ~/^([a-z]:|\\\\|\/)/i; - public static function directory(dir : String, alt = ".") { - if (dir == null) - dir = alt; - if(dir.endsWith("/") || dir.endsWith("\\")) - dir = dir.substr(0, -1); - if(!reabs.match(dir)) - dir = Sys.getCwd() + dir; - dir = StringTools.replace(dir, "\\", "/"); - return dir; - } -} +} \ No newline at end of file diff --git a/src/Vector.hx b/src/Vector.hx new file mode 100644 index 0000000..8963262 --- /dev/null +++ b/src/Vector.hx @@ -0,0 +1,75 @@ +package; +import haxe.Constraints.Function; +/** + * ... + * @author xmi + */ +@:forward +@:arrayAccess +abstract Vector(Array) from Array to Array { + public inline function new(_length:Int = 0, fixed_ignored:Bool = false) { + this = []; + if (_length != 0) length = _length; + } + + public var fixed(never, set):Bool; + public inline function set_fixed(value:Bool):Bool { + return value; + } + + + public var length(get, set):Int; + public inline function get_length():Int { + return this.length; + } + public inline function set_length(value:Int):Int { + #if js + return untyped this.length = value; + #else + var l:Int = this.length; + if (value > l) { + while (l < value) { + this.push(null); + l++; + } + } else if (value < l) { + while (l < value) { + this.pop(); + } + } + return value; + #end + } + + public inline function insertAt(pos:Int, x:T):Void { + return this.insert(pos, x); + } + + public inline function removeAt(index:Int):T { + return this.splice(index, 1)[0]; + } + + public inline function concat(?v:Vector):Vector { + if (v == null) { + return this.copy(); + } else { + return this.concat(v); + } + } + + public static inline function ofArray(a:Array):Vector { + return untyped a; + } + + #if openfl + @:to public function toOpenFlVector():openfl.Vector { + return untyped new openfl.Vector(this.length, false, untyped this); + } + @:from public static function fromOpenFlVector(v:openfl.Vector):Vector { + return untyped v.__array; + } + @:to public function toOpenFlObject():openfl.utils.Object { + return untyped this; + } + #end +} \ No newline at end of file diff --git a/src/XError.hx b/src/XError.hx new file mode 100644 index 0000000..4f45ac1 --- /dev/null +++ b/src/XError.hx @@ -0,0 +1,40 @@ +package; +import js.Error; + +/** + * ... + * @author xmi + */ +#if js +class XError extends js.Error { + public function new(name:String, message:String = null) { + super(); + untyped Object.defineProperty(this, 'name', { + enumerable: false, + writable: false, + value: name + }); + + untyped Object.defineProperty(this, 'message', { + enumerable: false, + writable: true, + value: message || '' + }); + + if (untyped Error.hasOwnProperty('captureStackTrace')) { // V8 + untyped Error.captureStackTrace(this, XError); + } else { + untyped Object.defineProperty(this, 'stack', { + enumerable: false, + writable: false, + value: (new Error(message)).stack + }); + } + } +} + +#elseif flash +typedef XError = flash.errors.Error; +#else +typedef XError = Error; +#end \ No newline at end of file diff --git a/src/as3hx/As3.hx b/src/as3hx/As3.hx index 5144631..fbb5cf9 100644 --- a/src/as3hx/As3.hx +++ b/src/as3hx/As3.hx @@ -47,6 +47,7 @@ enum Expr { EMeta( m : Metadata ); ETypedExpr( e : Expr, t : Null ); EDelete( e : Expr ); + ENamespaceAccess( e : Expr, f : String ); ECondComp( v : String, e : Expr, e2 : Expr ); ENL( e : Expr); EImport(v : Array); @@ -82,16 +83,20 @@ typedef ClassField = { var condVars : Array; } -typedef ClassDef = { - var meta : Array; - var kwds : Array; - var imports : Array>; - var isInterface : Bool; - var name : String; - var fields : Array; - var implement : Array; - var extend : Null; - var inits : Array; +class ClassDef { + public function new():Void { + + } + public var meta : Array; + public var kwds : Array; + public var imports : Array>; + public var isInterface : Bool; + public var name : String; + public var typeParams : String; + public var fields : Array; + public var implement : Array; + public var extend : Null; + public var inits : Array; } typedef FunctionDef = { diff --git a/src/as3hx/CallbackRebuild.hx b/src/as3hx/CallbackRebuild.hx new file mode 100644 index 0000000..508e89e --- /dev/null +++ b/src/as3hx/CallbackRebuild.hx @@ -0,0 +1,50 @@ +package as3hx; +import as3hx.As3.Expr; +import as3hx.As3.Function; +import as3hx.As3.Program; +import as3hx.As3.T; +import as3hx.RebuildUtils.RebuildResult; + +/** + * ... + * @author + */ +class CallbackRebuild { + private var cfg:Config; + private var typer:Typer; + + public function new(cfg:Config, typer:Typer) { + this.cfg = cfg; + this.typer = typer; + } + + + public function apply(program:Program):Void { + RebuildUtils.rebuildProgram(program, cfg, typer, rebuild); + } + + private function rebuild(expr:Expr):RebuildResult { + switch (expr) { + case ECall(e, params) : + var args:Array = typer.getFunctionArgTypes(e); + if (args != null) { + for (i in 0...args.length) { + var a = args[i]; + if (a != null && params[i] != null) { + var t:String = typer.tstring(a); + if (t != null && t.indexOf("->") != -1) { + var ps:Array = t.split("->"); + if (ps[0] != "T") { + var pt:Array = ps.map(function(s:String):T { return TPath([typer.expandStringType(s)]); }); + typer.overrideExprType(params[i], TFunction(pt)); + } + } + } + } + } + default: + } + return null; + } + +} \ No newline at end of file diff --git a/src/as3hx/ClassFieldType.hx b/src/as3hx/ClassFieldType.hx new file mode 100644 index 0000000..5d78693 --- /dev/null +++ b/src/as3hx/ClassFieldType.hx @@ -0,0 +1,17 @@ +package as3hx; + +/** + * ... + * @author d.skvortsov + */ +class ClassFieldType +{ + public var name:String; + public var t:T; + public var host:String; + public var isStatic:Bool; + public function new() { + + } + +} \ No newline at end of file diff --git a/src/as3hx/CommonImports.hx b/src/as3hx/CommonImports.hx new file mode 100644 index 0000000..8ff5453 --- /dev/null +++ b/src/as3hx/CommonImports.hx @@ -0,0 +1,84 @@ +package as3hx; + +/** + * ... + * @author xmi + */ +class CommonImports +{ + public static inline var ObjectImport:String = "openfl.utils.Object"; + public static inline var ObjectType:String = "Object"; + + private static var imports:Map> = new Map>(); + public static function getImports(cfg:Config):Map { + if (imports.exists(cfg)) { + return imports.get(cfg); + } else { + var newImports:Map = createImports(cfg); + imports.set(cfg, newImports); + return newImports; + } + } + + private static function createImports(cfg:Config):Map { + var map:Map = new Map(); + var doNotImportClasses = [ + "Array", "Bool", "Boolean", "Class", "Date", + "Dynamic", "EReg", "Enum", "EnumValue", + "Float", "Map", "Int", "UInt", "IntIter", + "Lambda", "List", "Math", "Number", "Reflect", + "RegExp", "Std", "String", "StringBuf", + "StringTools", "Sys", "Type", "Void", + "Function", "XML", "XMLList" + ]; + + if (!cfg.useOpenFlTypes) { + doNotImportClasses.push("Object"); + } + + for(c in doNotImportClasses) { + //map.set(c, null); + map.set(c, c); + } + + if (!cfg.functionToDynamic) { + map.set("Function", "haxe.Constraints.Function"); + } + + if (cfg.useOpenFlTypes) { + //map.set("Vector", "openfl.Vector"); + //map.set("Object", "openfl.utils.Object"); + map.set("Object", ObjectImport); + map.set(ObjectType, ObjectImport); + } + + var topLevelErrorClasses = [ + "ArgumentError", "DefinitionError", "Error", + "EvalError", "RangeError", "ReferenceError", + "SecurityError", "SyntaxError", "TypeError", + "URIError", "VerifyError" + ]; + + for(c in topLevelErrorClasses) { + if (cfg.useOpenFlTypes) { + map.set(c, "openfl.errors." + c); + } else { + map.set(c, "flash.errors." + c); + } + } + + for(c in cfg.importTypes.keys()) { + map.set(c, cfg.importTypes.get(c)); + } + map.set("MD5", "MD5"); + map.set("Signal", "signals.Signal"); + map.set("ISignal", "signals.Signal"); + map.set("NativeSignal", "signals.NativeSignal"); + map.set("AGALMiniAssembler", "openfl.utils.AGALMiniAssembler"); + map.set("Base64", "haxe.crypto.Base64"); + + //import avm2.intrinsics.memory.Sf32; + //import avm2.intrinsics.memory.Si32; + return map; + } +} \ No newline at end of file diff --git a/src/as3hx/Compat.hx b/src/as3hx/Compat.hx index 579647c..00fba06 100644 --- a/src/as3hx/Compat.hx +++ b/src/as3hx/Compat.hx @@ -7,7 +7,7 @@ import haxe.macro.Context; using StringTools; /** - * Collection of functions that just have no real way to be compatible in Haxe + * Collection of functions that just have no real way to be compatible in Haxe */ class Compat { @@ -41,10 +41,18 @@ class Compat { } public static inline function setArrayLength(a:Array>, length:Int) { + #if js + untyped a.length = length; + #else if (a.length > length) a.splice(length, a.length - length); - else a[length - 1] = null; + else { + for (i in a.length...length) { + a.push(null); + } + } + #end } - + /** * Adds elements to and removes elements from an array. This method modifies the array without making a copy. * @param startIndex An integer that specifies the index of the element in the array where the insertion or @@ -64,54 +72,232 @@ class Compat { public static inline function arraySplice(a:Array, startIndex:Int, deleteCount:Int, ?values:Array):Array { var result = a.splice(startIndex, deleteCount); if(values != null) { - for(i in 0...values.length) { + for (i in 0...values.length) { a.insert(startIndex + i, values[i]); } } return result; } - + + public static function search(s:String, ereg:FlashRegExpAdapter):Int { + if (ereg.match(s)) { + return ereg.matchedPos().pos; + } else { + return -1; + } + } + + public static function searchEReg(s:String, ereg:EReg):Int { + if (ereg.match(s)) { + return ereg.matchedPos().pos; + } else { + return -1; + } + } + + public static function match(s:String, ereg:FlashRegExpAdapter, parenthesesBlockIndex:Int = 0):Array { + var matches:Array = []; + while (ereg.match(s)) { + matches.push(ereg.matched(parenthesesBlockIndex)); + s = ereg.matchedRight(); + } + return matches; + } + + public static function matchEReg(s:String, ereg:EReg, parenthesesBlockIndex:Int = 0):Array { + var matches:Array = []; + while (ereg.match(s)) { + matches.push(ereg.matched(parenthesesBlockIndex)); + s = ereg.matchedRight(); + } + return matches; + } + + public static function makeArgs(a1:Dynamic, a2:Dynamic, a3:Dynamic, a4:Dynamic, a5:Dynamic = null, a6:Dynamic = null, a7:Dynamic = null):Array { + if (a7 == null) { + if (a6 == null) { + if (a5 == null) { + if (a4 == null) { + if (a3 == null) { + if (a2 == null) { + if (a1 == null) { + return []; + } else { + return [a1]; + } + } else { + return [a1, a2]; + } + } else { + return [a1, a2, a3]; + } + } else { + return [a1, a2, a3, a4]; + } + } else { + return [a1, a2, a3, a4, a5]; + } + } else { + return [a1, a2, a3, a4, a5, a6]; + } + } else { + return [a1, a2, a3, a4, a5, a6, a7]; + } + } + + private static var _weights:Array; + public static function sortIndexedArray(weights:Array):Array { + var indices:Array = new Array(); + for (i in 0...weights.length) { + indices[i] = i; + } + _weights = weights; + indices.sort(sortWeights); + _weights = null; + return indices; + } + + private static function sortWeights(a:Int, b:Int):Int { + var d:Float = _weights[b] - _weights[a]; + return d > 0 ? 1 : (d < 0 ? -1 : 0); + } + macro public static function getFunctionLength(f) { switch(Context.follow(Context.typeof(f))) { case TFun(args, _): return @:pos(Context.currentPos()) macro $v{args.length}; - default: throw new Error("not a function", f.pos); + default: return macro 0;// throw new Error("not a function", f.pos); } } - - /** - * Converts a typed expression into a Float. - */ - macro public static function parseFloat(e:ExprOf):ExprOf { - var type = switch(Context.typeof(e)) { - case TAbstract(t, _) if(t.get().pack.length == 0): t.get().name; - case TInst(t, _) if(t.get().pack.length == 0): t.get().name; - case _: null; + + public static function castVector(p:Dynamic):Vector { + if (p == null || !Std.is(p, Array)) { + return null; } - return switch(type) { - case "Float" | "Int": macro ${e}; - case "String": macro Std.parseFloat(${e}); - case _: macro Std.parseFloat(Std.string(${e})); + return cast p; + } + + public static function filter(v:Vector, filterMethod:T->Int->Vector->Bool):Vector { + var r:Vector = new Vector(); + for (i in 0...v.length) { + if (filterMethod(v[i], i, v)) { + r.push(v[i]); + } } + return r; } - /** - * Converts a typed expression into an Int. - */ - macro public static function parseInt(e:ExprOf, ?base:ExprOf):ExprOf { - var type = switch(Context.typeof(e)) { - case TAbstract(t, _) if(t.get().pack.length == 0): t.get().name; - case TInst(t, _) if(t.get().pack.length == 0): t.get().name; - case _: null; + + #if (openfl && !macro) + public static inline function newByteArray():openfl.utils.ByteArray { + var ba:openfl.utils.ByteArray = new openfl.utils.ByteArray(); + ba.endian = openfl.utils.Endian.BIG_ENDIAN; + return ba; + } + + /*public static function castVector(p:Dynamic):openfl.Vector { + if (p == null) return null; + var c:Class = Type.getClass(p); + if (c == null) return null; + if (Type.getClassName(c).indexOf("openfl._Vector.") != 0) return null; + //var type:String = Type.getClassName(Type.getClass(p.data)); //oldopenfl + //} else if (TType == Function && type == "openfl._Vector.FunctionVector") { + return cast p; + }*/ + + /** T==null is Vector.<*>, otherwise T could be Abstract of Class */ + public static function isVector(p:Dynamic, T:Dynamic = null):Bool { + if (p == null) return null; + //var c:Class = Type.getClass(p); + //if (c == null) return false; + //if (Type.getClassName(c) != "openfl._Vector.AbstractVector") return false; + //if (Type.getClassName(c).indexOf("openfl._Vector.") != 0) return null; + if (!Std.is(p, Array)) return null; + //var v:openfl.Vector = cast p; + var v:Vector = cast p; + if (v.length > 0 && T != null && v[0] != null && !Std.is(v[0], T)) { + return false; } - return switch(type) { - case "Int": macro ${e}; - case "Float": macro Std.int(${e}); - case "String": macro @:privateAccess as3hx.Compat._parseInt(${e}, ${base}); - case _: macro Std.parseInt(Std.string(${e})); + return true; + } + + public static inline function each(obj:openfl.utils.Object):Iterator { + return new ObjectIterator(obj); + } + //public static function filter(v:openfl.Vector, filterMethod:T->Int->openfl.Vector->Bool):openfl.Vector { + //var r:openfl.Vector = new openfl.Vector(); + //for (i in 0...v.length) { + //if (filterMethod(v[i], i, v)) { + //r.push(v[i]); + //} + //} + //return r; + //} + + //public static inline function vectorSplice(a:openfl.Vector, startIndex:Int, deleteCount:Int, ?values:Array):openfl.Vector { + //var result = a.splice(startIndex, deleteCount); + //if(values != null) { + //for (i in 0...values.length) { + //a.insertAt(startIndex + i, values[i]); + //} + //} + //return result; + //} + #end + public static inline function vectorSplice(a:Vector, startIndex:Int, deleteCount:Int, ?values:Array):Vector { + var result = a.splice(startIndex, deleteCount); + if(values != null) { + for (i in 0...values.length) { + a.insertAt(startIndex + i, values[i]); + } } + return result; } - - static function _parseInt(s:String, ?base:Int):Null { + + public static inline function isClass(c:Dynamic):Bool { + return c != null && c.__name__ != null; + } + + public static inline function castClass(c:Dynamic):Class { + return isClass(c) ? untyped c : null; + } + + public static inline function getQualifiedClassName(o:Dynamic):String { + if (o == null) { + return null; + } else if (untyped o.__name__) { + return o.__name__; + } else { + var c:Class = Type.getClass(o); + if (c != null) { + return Type.getClassName(c); + } else { + return null; + } + } + //var c:Class = Type.getClass(o); + //if (c == null) { + //c = castClass(o); + //} + //if (c != null) { + //return Type.getClassName(c); + //} else { + //return null; + //} + } + + public static inline function getQualifiedSuperclassName(o:Dynamic):String { + var c:Class = Type.getClass(o); + if (c == null) { + c = castClass(o); + } + if (c != null) { + return Type.getClassName(Type.getSuperClass(c)); + } else { + return null; + } + } + + public static function parseInt(s:String, ?base:Int):Null { #if js if(base == null) base = s.indexOf("0x") == 0 ? 16 : 10; var v:Int = untyped __js__("parseInt")(s, base); @@ -124,18 +310,18 @@ class Compat { var BASE = "0123456789abcdefghijklmnopqrstuvwxyz"; if(base != null && (base < 2 || base > BASE.length)) return throw 'invalid base ${base}, it must be between 2 and ${BASE.length}'; - s = s.trim().toLowerCase(); - var sign = if(s.startsWith("+")) { + s = StringTools.trim(s).toLowerCase(); + var sign = if(StringTools.startsWith(s, "+")) { s = s.substring(1); 1; - } else if(s.startsWith("-")) { + } else if(StringTools.startsWith(s, "-")) { s = s.substring(1); -1; } else { 1; }; if(s.length == 0) return null; - if(s.startsWith('0x')) { + if(StringTools.startsWith(s, '0x')) { if(base != null && base != 16) return null; // attempting at converting a hex using a different base base = 16; s = s.substring(2); @@ -152,9 +338,25 @@ class Compat { #end } + /** + * Converts a typed expression into a Float. + */ + macro public static function parseFloat(e:ExprOf):ExprOf { + var type = switch(Context.typeof(e)) { + case TAbstract(t, _) if(t.get().pack.length == 0): t.get().name; + case TInst(t, _) if(t.get().pack.length == 0): t.get().name; + case _: null; + } + return switch(type) { + case "Float" | "Int": macro ${e}; + case "String": macro Std.parseFloat(${e}); + case _: macro Std.parseFloat(Std.string(${e})); + } + } + /** * Runs a function at a specified interval (in milliseconds). - * + * * Instead of using the setInterval() method, consider * creating a Timer object, with the specified interval, using 0 as the repeatCount * parameter (which sets the timer to repeat indefinitely).If you intend to use the clearInterval() method to cancel the @@ -184,7 +386,7 @@ class Compat { return -1; #end } - + /** * Cancels a specified setInterval() call. * @param id The ID of the setInterval() call, which you set to a variable, as in the following: @@ -201,10 +403,10 @@ class Compat { throw "Supported by version 3.3 or higher"; #end } - + /** * Runs a specified function after a specified delay (in milliseconds). - * + * * Instead of using this method, consider * creating a Timer object, with the specified interval, using 1 as the repeatCount * parameter (which sets the timer to run only once).If you intend to use the clearTimeout() method to cancel the @@ -234,7 +436,7 @@ class Compat { return -1; #end } - + /** * Cancels a specified setTimeout() call. * @param id The ID of the setTimeout() call, which you set to a variable, as in the following: @@ -251,7 +453,7 @@ class Compat { throw "Supported by version 3.3 or higher"; #end } - + /** * Runtime value of FLOAT_MAX depends on target platform */ @@ -273,7 +475,7 @@ class Compat { return 1.79e+308; #end } - + /** * Runtime value of FLOAT_MIN depends on target platform */ @@ -295,7 +497,7 @@ class Compat { return -1.79E+308; #end } - + /** * Runtime value of INT_MAX depends on target platform */ @@ -304,7 +506,8 @@ class Compat { #if flash return untyped __global__['int'].MAX_VALUE; #elseif js - return untyped __js__('Number.MAX_SAFE_INTEGER'); + //return untyped __js__('Number.MAX_SAFE_INTEGER'); + return 2147483647; #elseif cs return untyped __cs__('int.MaxValue'); #elseif java @@ -319,7 +522,7 @@ class Compat { return 2^31-1; #end } - + /** * Runtime value of INT_MIN depends on target platform */ @@ -328,7 +531,8 @@ class Compat { #if flash return untyped __global__['int'].MIN_VALUE; #elseif js - return untyped __js__('Number.MIN_SAFE_INTEGER'); + //return untyped __js__('Number.MIN_SAFE_INTEGER'); + return -2147483648; #elseif cs return untyped __cs__('int.MinValue'); #elseif java @@ -343,7 +547,7 @@ class Compat { return -2^31; #end } - + /** * Returns a string representation of the number in fixed-point notation. * Fixed-point notation means that the string will contain a specific number of digits @@ -375,13 +579,66 @@ class Compat { return s; #end } + + public static function toPrecision(n:Float, prec:Int):String { + n = Math.round(n * Math.pow(10, prec)); + var str = '' + n; + var len = str.length; + if(len <= prec){ + while(len < prec){ + str = '0' + str; + len++; + } + return '0.' + str; + } else{ + return str.substr(0, len - prec) + '.' + str.substr(len - prec); + } + } + + public static function toString(value:Int, base:Int = 10):String { + var BASE:String = "0123456789abcdefghijklmnopqrstuvwxyz"; + if(base < 2 || base > BASE.length) + throw 'invalid base $base, it must be between 2 and ${BASE.length}'; + if(base == 10 || value == 0) + return Std.string(value); + var buf:String = ""; + var abs:Int = value > 0 ? value : -value; + while(abs > 0) { + buf = BASE.charAt(abs % base) + buf; + abs = Std.int(abs / base); + } + return buf; + } + + /** returns timezone offset in minutes */ + public static function getTimezoneOffset():Int { + #if flash + return untyped new flash.Date().getTimezoneOffset(); + #elseif js + return untyped __js__("new Date().getTimezoneOffset()"); + #else + var now = Date.now(); + now = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0); + return 24 * 60 * Math.round(now.getTime() / 24 / 3600 / 1000) - Std.int(now.getTime() / 1000 / 60); + /* + //https://github.com/HaxeFoundation/haxe/issues/3268 + var a = DateTools.format(Date.fromTime(0), '%H:%M').split(':'); + var offset = -Std.parseInt(a[0]) * 60 + Std.parseInt(a[1]); // will care about DST (daylight saving time), but can fail with < UTC-1200 and > UTC+1200 + trace(offset); + + //https://github.com/waneck/geotools/issues/2 + trace ( (new Date(1970, 0, 1, 0, 0, 0).getTime() / 1000 / 60)) ); // may not work in Neko, do not care about DST (daylight saving time) + trace ( ((new Date(1970, 1, 1, 0, 0, 0).getTime() - 31 * 24 * 60 * 60 * 1000) / 1000 / 60) ); //should work in Neko too + */ + #end + } } #if (!flash && !js && (haxe_ver >= "3.3")) private class FlashTimerAdapter { - + public static var timers:Array = []; - + public static function setInterval(callback:Dynamic, milliseconds:Int, rest:Array):Int { var timer = new haxe.Timer(milliseconds); timers.push(timer); @@ -389,9 +646,9 @@ private class FlashTimerAdapter { timer.run = function() Reflect.callMethod(null, callback, rest); return id; } - + public static function clearInterval(id:Int) stopTimer(id); - + public static function setTimeout(callback:Dynamic, milliseconds:Int, rest:Array):Int { var timer = new haxe.Timer(milliseconds); timers.push(timer); @@ -402,15 +659,15 @@ private class FlashTimerAdapter { } return id; } - + public static function clearTimeout(id:Int) stopTimer(id); - + static function stopTimer(id:Int) { timers[id].stop(); timers[id] = null; } -} -#end + } + #end #if python @:pythonImport("sys") @@ -426,21 +683,38 @@ typedef Regex = flash.utils.RegExp; typedef Regex = FlashRegExpAdapter; class FlashRegExpAdapter { - - public function new(r:String, opt:String) { + + public function new(r:String, opt:String = '') { _ereg = new EReg(r, opt); _global = opt.indexOf("g") != -1; } - + + public var lastIndex(get, set):Int; + var _ereg:EReg; var _global:Bool; var _lastTestedString : String; var _restOfLastTestedString : String; - var _lastTestedStringProcessedSize = 0; - + var _lastIndex:Int = 0; + + private function get_lastIndex():Int { + return _lastIndex; + } + + private function set_lastIndex(value:Int):Int { + if (_lastIndex != value) { + if (_lastTestedString != null) { + _restOfLastTestedString = _lastTestedString.substr(value); + } + return _lastIndex = value; + } else { + return value; + } + } + /** * Performs a search for the regular expression on the given string str. - * + * * If the g (global) flag is not set for the regular * expression, then the search starts * at the beginning of the string (at index position 0); the search ignores @@ -451,34 +725,39 @@ class FlashRegExpAdapter { * of the end of the match. * @param str The string to search. * @return If there is no match, null; otherwise, an object with the following properties: - * + * * An array, in which element 0 contains the complete matching substring, and * other elements of the array (1 through n) contain substrings that match parenthetical groups * in the regular expression index — The character position of the matched substring within * the stringinput — The string (str) */ public function exec(str:String):Null> { - var testStr = _lastTestedString == str ? _restOfLastTestedString : str; + var testStr; + if (_lastTestedString == str) { + testStr = _restOfLastTestedString; + } else { + testStr = str; + } var matched = _ereg.match(testStr); var index = 0; if (_global) { _lastTestedString = str; if (matched) { var matchedLeftLength = _ereg.matchedLeft().length; - index = _lastTestedStringProcessedSize + matchedLeftLength; + index = _lastIndex + matchedLeftLength; _restOfLastTestedString = _ereg.matchedRight(); - _lastTestedStringProcessedSize += matchedLeftLength + _ereg.matched(0).length; + _lastIndex += matchedLeftLength + _ereg.matched(0).length; } else { _restOfLastTestedString = null; - _lastTestedStringProcessedSize = 0; + _lastIndex = lastIndex; } } return matched ? new FlashRegExpExecResult(str, _ereg, index).matches : null; } - + /** * Tests for the match of the regular expression in the given string str. - * + * * If the g (global) flag is not set for the regular expression, * then the search starts at the beginning of the string (at index position 0); the search ignores * the lastIndex property of the regular expression.If the g (global) flag is set for the regular expression, then the search starts @@ -489,23 +768,23 @@ class FlashRegExpAdapter { * @return If there is a match, true; otherwise, false. */ public function test(str:String):Bool return match(str); - + public function map(s:String, f:EReg-> String):String return _ereg.map(s, f); - + public function match(s:String):Bool return _ereg.match(s); - + public function matched(n:Int):String return _ereg.matched(n); - + public function matchedLeft():String return _ereg.matchedLeft(); - + public function matchedPos():{pos:Int, len:Int} return _ereg.matchedPos(); - + public function matchedRight():String return _ereg.matchedRight(); - + public function matchSub(s:String, pos:Int, len:Int = -1):Bool return _ereg.matchSub(s, pos, len); - + public function replace(s:String, by:String):String return _ereg.replace(s, by); - + public function split(s:String):Array return _ereg.split(s); } @@ -515,12 +794,15 @@ private class FlashRegExpExecResult { this.index = index; populateMatches(ereg); } - + public var index(default,null) : Int = 0; public var input(default,null) : String; public var matches(default,null) : Array; - + function populateMatches(ereg:EReg) { + #if js + matches = untyped ereg.r.m.slice(0); + #else matches = []; try { var group = 0; @@ -530,6 +812,28 @@ private class FlashRegExpExecResult { } } catch (ignored:Dynamic) { } + #end } } +#end + +#if openfl +class ObjectIterator { + private var object:openfl.utils.Object; + private var fields:Array; + private var i:Int; + private var l:Int; + public inline function new(object:openfl.utils.Object) { + this.fields = Reflect.fields(object); + this.object = object; + this.i = 0; + this.l = fields.length; + } + public inline function next():Dynamic { + return object[fields[i++]]; + } + public inline function hasNext():Bool { + return i < l; + } +} #end \ No newline at end of file diff --git a/src/as3hx/Config.hx b/src/as3hx/Config.hx index 80b6237..de1f015 100644 --- a/src/as3hx/Config.hx +++ b/src/as3hx/Config.hx @@ -1,6 +1,7 @@ + package as3hx; -import haxe.xml.Fast; +import haxe.xml.Access; import sys.FileSystem; import sys.io.File; import haxe.ds.StringMap; @@ -13,6 +14,8 @@ using StringTools; * @author Russell Weir */ class Config { + public var verbouseConditionalCompilationEnd:Bool = false; + /** Indent character(s) **/ public var indentChars : String; /** newline character **/ @@ -34,22 +37,38 @@ class Config { public var errorContinue : Bool; /** Write Dynamic for Function **/ public var functionToDynamic : Bool; + /** Allow full typing of available classes. App will firstly parse all classes into memory and then try to apply type info while writing all classes in second phase **/ + public var useFullTyping : Bool; /** getter function template **/ public var getterMethods : String; /** setter function template **/ public var setterMethods : String; /** getter/setter output style **/ public var getterSetterStyle : String; + /** top level package for classes defined in flash.* package in as3. 'flash' is default, but `openfl` or `nme` can be used for example **/ + public var flashTopLevelPackage : String; /** list of paths to exclude from parsing **/ public var excludePaths : List; /** Used only for test cases for compiler to ignore Sprite imports and extends **/ public var testCase : Bool; + /** Try to use openfl.Vector openfl.utils.Dictionary and openfl.utils.Object types **/ + public var useOpenFlTypes : Bool; + /** Replace varArgs with set of optional arguments converted to an Array **/ + public var replaceVarArgsWithOptionalArguments : Bool; /** conditional compilation variables **/ public var conditionalVars: List; + /** Compile time constants implementation class package path for CONFIG::VAR -> `compile.Time.Constants`.CONFIG_VAR**/ + public var conditionalCompilationConstantsClass : String; + /** Try fix local variable declarations. In haxe variable should be declared only once and before first usage. **/ + public var fixLocalVariableDeclarations : Bool; /** Transform Dictionary. to Map **/ public var dictionaryToHash : Bool; + /** if set to false `Dictionary\/*KeyType,ValueType*\/` notation will be used. By default Dictionary. notation is used **/ + public var useAngleBracketsNotationForDictionaryTyping : Bool; /** write inferred type information into output **/ public var debugInferredType : Bool; + /** replace local function declarations with variable definitions **/ + public var rebuildLocalFunctions : Bool; /** convert flexunit metadata and calls to munit form */ public var convertFlexunit : Bool; /** make all generated setter private */ @@ -69,26 +88,38 @@ class Config { */ public var postProcessor : String = ""; + public var arrayTypePath : String = "Vector"; + + /** + * a list of absolute or relative directory paths. + * as3 files from this paths are parsed as a source for type info + */ + public var libPaths : Array; + /** * a list of absolute or relative directory paths. * Haxe files are found in this path and added to a map * of imported types used for implicit imports used - * in converted code + * in converted code */ public var importPaths : Array; + /** + * list of as3 import paths that should not be included in haxe files but should be looked up through existing codebase + */ + public var importExclude : Array; + /** * A map where the key is the name fo a Haxe type - * and the value is its' fully qualified name, + * and the value is its' fully qualified name, * as found in one of the provided importPaths */ public var importTypes : StringMap; - /** source directory **/ - public var src : String; + public var src : Array; /** output directory **/ - public var dst : String; + public var dst : Array; var cfgFile : String; @@ -101,7 +132,7 @@ class Config { processImportPaths(); } - public function init() { + public function init():Void { var env = Sys.environment(); if (env.exists("AS3HX_CONFIG")) { cfgFile = env.get("AS3HX_CONFIG"); @@ -122,6 +153,9 @@ class Config { else return; cfgFile = toPath(home+"/.as3hx_config.xml"); + + src = []; + dst = []; } /** @@ -148,11 +182,11 @@ class Config { return s.substr(0, 1).toUpperCase() + s.substr(1); } - function processDefaultConfig() { + public function processDefaultConfig():Void { fromXmlString(defaultConfig()); } - function processEnvConfig() { + function processEnvConfig():Void { // $HOME/.as3hx_config.xml or env if(cfgFile != null) { if(!FileSystem.exists(cfgFile)) { @@ -167,7 +201,7 @@ class Config { } } - function processLocalConfig() { + function processLocalConfig():Void { if(FileSystem.exists("./.as3hx_config.xml")) { fromXmlString(File.getContent("./.as3hx_config.xml")); } @@ -177,7 +211,7 @@ class Config { * Store fuly qualified names of Haxe files found * at provided directories */ - function processImportPaths() { + function processImportPaths():Void { importTypes = new StringMap(); for(path in importPaths) { processImportPath(path, "", importTypes); @@ -215,7 +249,7 @@ class Config { } } - public static function usage() { + public static function usage():Void { var println = Sys.println; println("Usage: as3hx [options] sourceDir [outdir]"); println(" Options:"); @@ -232,7 +266,7 @@ class Config { println(" outdir\t\t : defaults to './out'"); } - function processCommandLine() { + function processCommandLine():Void { var args = Sys.args().copy(); #if !munit if (args.length == 0) { @@ -242,18 +276,10 @@ class Config { #else if (args.length == 0) return; #end - var last = new Path(args[args.length - 1]).toString(); - if (((StringTools.endsWith(last, "/") && last != "/") || StringTools.endsWith(last, "\\")) && !StringTools.endsWith(last, ":\\")) { - last = last.substr(0, last.length - 1); - } - if (FileSystem.exists(last) && FileSystem.isDirectory(last)) { - Sys.setCwd(last); - args.pop(); - } - var arg = ""; - while(true) { + var arg:String = null; + while (true) { arg = args.shift(); - switch(arg) { + switch (arg) { case "-help", "--help": usage(); Sys.exit(0); @@ -279,16 +305,40 @@ class Config { convertFlexunit = true; case "-dict2hash": dictionaryToHash = true; - default: + case "-libPath", "--libPath": + libPaths.push(args.shift()); + case null: break; + case _ if (arg.charAt(0) == "-"): + Sys.println("Unknown argument: " + arg); + usage(); + Sys.exit(1); + case _ if (src.length == 0): + src.push(directory(arg)); + case _ if (dst.length == 0): + dst.push(directory(arg, Sys.getCwd() + "out")); } } - if(arg == null) { + if (dst.length == 0) { + dst.push(directory(arg, Sys.getCwd() + "out")); + } + if (src == null) { usage(); Sys.exit(1); } - src = Run.directory(arg); - dst = Run.directory(args.shift(), "./out"); + } + + static var reabs:EReg = ~/^([a-z]:|\\\\|\/)/i; + + static function directory(dir : String, alt = "."):String { + if (dir == null) + dir = alt; + if(dir.endsWith("/") || dir.endsWith("\\")) + dir = dir.substr(0, -1); + if(!reabs.match(dir)) + dir = Sys.getCwd() + dir; + dir = StringTools.replace(dir, "\\", "/"); + return dir; } /* @@ -301,22 +351,23 @@ class Config { } */ - public function fromXmlString(s:String) { + public function fromXmlString(s:String):Void { var x = Xml.parse(s); - fromXml(new Fast(x.firstElement())); + fromXml(new Access(x.firstElement())); } - public function fromXml(f:Fast) { + public function fromXml(f:Access):Void { for(el in f.elements) { switch(el.name) { case "indentChars": setCharField(el, " "); case "newlineChars": setCharField(el, "\n"); case "bracesOnNewline": setBoolField(el, true); - case "spacesOnTypeColon": setBoolField(el, true); + case "spacesOnTypeColon": setBoolField(el, true); case "uintToInt": setBoolField(el, true); case "vectorToArray": setBoolField(el, true); case "guessCasts": setBoolField(el, true); case "functionToDynamic": setBoolField(el, false); + case "useFullTyping": setBoolField(el, false); case "getterMethods": setCharField(el, "get_%I"); case "setterMethods": setCharField(el, "set_%I"); case "getterSetterStyle": setCharField(el, "haxe", ["haxe","flash","combined"]); @@ -325,20 +376,29 @@ class Config { case "forcePrivateSetter": setBoolField(el, true); case "errorContinue": setBoolField(el, true); case "testCase": setBoolField(el, false); - case "verifyGeneratedFiles": setBoolField(el, false); + case "useOpenFlTypes": setBoolField(el, false); + case "replaceVarArgsWithOptionalArguments": setBoolField(el, false); + case "verifyGeneratedFiles":setBoolField(el, false); + case "flashTopLevelPackage":setCharField(el, "flash"); case "excludeList": setExcludeField(el, new List()); + case "libPaths": setLibPaths(el, new Array()); case "conditionalCompilationList": setConditionalVars(el, new List()); + case "conditionalCompilationConstantsClass":setCharField(el, ""); + case "fixLocalVariableDeclarations":setBoolField(el, true); case "dictionaryToHash": setBoolField(el, false); + case "rebuildLocalFunctions": setBoolField(el, true); + case "useAngleBracketsNotationForDictionaryTyping": setBoolField(el, true); case "useFastXML": setBoolField(el, true); - case "useCompat": setBoolField(el, true); - case "importPaths": setImportPaths(el, []); + case "useCompat": setBoolField(el, true); + case "importPaths": setImportPaths(el, []); + case "importExclude": setImportExclude(el, []); default: Sys.println("Unrecognized config var " + el.name); } } } - function setBoolField(f:Fast, defaultVal:Bool) { + function setBoolField(f:Access, defaultVal:Bool):Void { var val = defaultVal; if(f.has.value) { var c = f.att.value.toLowerCase().charAt(0); @@ -347,7 +407,7 @@ class Config { Reflect.setField(this, f.name, val); } - function setCharField(f:Fast, defaultVal:String, constrain:Array = null) { + function setCharField(f:Access, defaultVal:String, constrain:Array = null):Void { if(constrain == null) constrain = []; var val = (f.has.value) ? f.att.value : defaultVal; if(constrain.length > 0) { @@ -359,7 +419,7 @@ class Config { Reflect.setField(this, f.name, unescape(val)); } - function setExcludeField(f:Fast, defaultExcludes:List) { + function setExcludeField(f:Access, defaultExcludes:List):Void { excludePaths = defaultExcludes; for (file in f.nodes.path) { if (file.has.value) { @@ -368,7 +428,16 @@ class Config { } } - function setConditionalVars(f:Fast, defaultVars:List) { + function setLibPaths(f:Access, defaultLibPaths:Array):Void { + libPaths = defaultLibPaths; + for (dir in f.nodes.path) { + if (dir.has.value) { + libPaths.push(dir.att.value); + } + } + } + + function setConditionalVars(f:Access, defaultVars:List):Void { conditionalVars = defaultVars; for (conditionalVar in f.nodes.variable) { if (conditionalVar.has.value) { @@ -377,7 +446,7 @@ class Config { } } - function setImportPaths(f:Fast, defaultVars:Array) { + function setImportPaths(f:Access, defaultVars:Array):Void { importPaths = defaultVars; for (importPath in f.nodes.variable) { if (importPath.has.value) { @@ -386,6 +455,15 @@ class Config { } } + function setImportExclude(f:Access, defaultVars:Array):Void { + importExclude = defaultVars; + for (importPath in f.nodes.variable) { + if (importPath.has.value) { + importExclude.push(importPath.att.value); + } + } + } + public static function toPath(inPath:String):String { if (!isWindows()) return inPath; @@ -395,7 +473,7 @@ class Config { public static function isWindows() : Bool { var os = Sys.systemName(); - return (new EReg("window","i")).match(os); + return new EReg("window", "i").match(os); } static function escape(s:String):String { @@ -416,14 +494,15 @@ class Config { return ' - + - - + + + @@ -431,14 +510,25 @@ class Config { + + + - - - + + + + + + + + + + + '; } } diff --git a/src/as3hx/DictionaryRebuild.hx b/src/as3hx/DictionaryRebuild.hx new file mode 100644 index 0000000..b07ec5a --- /dev/null +++ b/src/as3hx/DictionaryRebuild.hx @@ -0,0 +1,266 @@ +package as3hx; +import as3hx.As3.ClassDef; +import as3hx.As3.Expr; +import as3hx.As3.Function; +import as3hx.As3.Program; +import as3hx.As3.T; +import as3hx.DictionaryRebuild.ComplexType; +import as3hx.RebuildUtils.RebuildResult; +import neko.Lib; + +class ComplexType { + public var types:Array; + public var name:String; + public function new(name:String, types:Array):Void { + this.name = name; + this.types = types; + } +} + +typedef DictionaryTypes = { + key:Array, + value:Array +} + +/** + * ... + * @author ... + */ +class DictionaryRebuild +{ + var classFieldDictionaryTypes : Map> = new Map>(); + var typer:Typer; + var cfg:Config; + + public function new(typer:Typer, cfg:Config) { + this.typer = typer; + this.cfg = cfg; + } + + function getUnderlyingIdent(expr:Expr):Expr { + switch(expr) { + case EArray(e, index): + return getUnderlyingIdent(e); + case EField(e, f): + return e; + case EIdent(v): + return expr; + default: + return null; + } + } + + + private function isOpenFlDictionaryType(s:String):Bool { + return s != null && (StringTools.startsWith(s, "Dictionary") || StringTools.startsWith(s, "openfl.utils.Dictionary")) && cfg.useOpenFlTypes; + } + + public function applyRefinedTypes(program:Program):Void { + for (i in 0...4) { + refineTypes(program); + } + } + + public function refineTypes(program:Program):Void { + var currentClass:String = null; + var pack:String = typer.getPackageString(program.pack); + + function refineArrayAccess(e1:Expr, index:Expr, valueType:String):Void { + var type1:String = typer.getExprType(e1); + var identExpr:Expr = getUnderlyingIdent(e1); + var identType:String = typer.getExprType(identExpr); + switch(e1) { + case EArray(e, i): + var newStringType:String = "Dictionary<" + typer.getExprType(index) + "," + valueType + ">"; + refineArrayAccess(e, i, newStringType); + return; + default: + } + if (type1 != null && (StringTools.startsWith(type1, "Dictionary") || StringTools.startsWith(type1, "openfl.utils.Dictionary"))) { + var baseType:String = null; + var field:String = null; + switch(e1) { + case EField(e2, f): + baseType = typer.getExprType(e2); + field = f; + case EIdent(s): + baseType = pack + currentClass; + field = s; + default: + } + if (field != null && baseType != null) { + if (typer.classes.exists(baseType)) { + var indexType:String = typer.getExprType(index); + var newStringType:String = foldStringType(field, typer.expandStringType(type1), typer.expandStringType(indexType), typer.expandStringType(valueType)); + if (newStringType != type1) { + typer.overrideIdentType(field, typer.expandType(TPath([newStringType]))); + } + } + } + } + } + var returnType:T = null; + function rebuild(expr:Expr):RebuildResult { + switch(expr) { + case EFunction(f, name): + typer.enterFunction(f, name); + var oldReturnType:T = returnType; + returnType = f.ret.t; + var re:Expr = RebuildUtils.rebuild(f.expr, rebuild); + f.ret.t = returnType; + returnType = oldReturnType; + typer.leaveFunction(); + if (re != null) { + return RebuildResult.RReplace(EFunction({args: f.args, varArgs: f.varArgs, ret:f.ret, expr:re}, name)); + } else { + return RebuildResult.RSkip; + } + case EReturn(e): + switch(e) { + case EArray(e, index): + refineArrayAccess(e, index, typer.tstring(returnType)); + RebuildUtils.rebuild(e, rebuild); + RebuildUtils.rebuild(index, rebuild); + return RebuildResult.RSkip; + case null: + default: + var newType:String = typer.getExprType(e); + if (isOpenFlDictionaryType(newType)) { + var oldType:String = typer.expandStringType(typer.tstring(returnType)); + newType = foldDictionaryType2(oldType, newType); + newType = typer.shortenStringType(newType); + if (oldType != newType) { + returnType = TPath([newType]); + } + } + } + case EVars(vars): + for (v in vars) { + switch(v.val) { + case ETypedExpr(e, t): + switch(e) { + case EArray(e1, index): + refineArrayAccess(e1, index, typer.tstring(v.t)); + RebuildUtils.rebuild(e1, rebuild); + RebuildUtils.rebuild(index, rebuild); + return RebuildResult.RSkip; + case null: + default: + } + case null: + default: + } + } + case EBinop("=", e1, e2, _): + switch(e1) { + case EArray(e1, index): + refineArrayAccess(e1, index, typer.getExprType(e2)); + RebuildUtils.rebuild(e1, rebuild); + RebuildUtils.rebuild(index, rebuild); + return RebuildResult.RSkip; + default: + } + switch(e2) { + case EArray(e2, index): + refineArrayAccess(e2, index, typer.getExprType(e1)); + RebuildUtils.rebuild(e2, rebuild); + RebuildUtils.rebuild(index, rebuild); + return RebuildResult.RSkip; + default: + } + case EArray(e1, index): + refineArrayAccess(e1, index, "Dynamic"); + default: + } + return null; + } + + typer.enterProgram(program); + for (d in program.defs) { + switch (d) { + case CDef(c): + typer.setImports(WriterImports.getImports(program, cfg, c), null); + typer.enterClass(pack + c.name, c); + currentClass = c.name; + for (field in c.fields) { + switch(field.kind) { + case FFun(f): + typer.enterFunction(f, field.name, c); + returnType = f.ret.t; + RebuildUtils.rebuild(f.expr, rebuild); + f.ret.t = returnType; + typer.leaveFunction(); + default: + } + } + case FDef(f): + returnType = f.f.ret.t; + RebuildUtils.rebuild(f.f.expr, rebuild); + default: + } + } + } + + function foldStringType(field:String, old:String, newKey:String, newValue:String):String { + var openIndex:Int = old.indexOf("<"); + if (openIndex == -1) { + return "Dictionary<" + newKey + "," + newValue + ">"; + } + var name:String = old.substr(0, openIndex); + var depth:Int = 0; + var splitIndex:Int = -1; + for (i in openIndex + 1...old.length) { + switch (old.charCodeAt(i)) { + case '<'.code: depth++; + case '>'.code: depth--; + case ','.code: + if (depth == 0) { + splitIndex = i; + break; + } + } + } + if (splitIndex != -1) { + var oldKey:String = old.substring(openIndex + 1, splitIndex); + var oldValue:String = old.substring(splitIndex + 1, old.length - 1); + newKey = foldDictionaryType2(oldKey, newKey); + newValue = foldDictionaryType2(oldValue, newValue); + } + return name + "<" + newKey + "," + newValue + ">"; + } + + function foldDictionaryType2(a:String, b:String):String { + if (a == b) return a; + //use as type param + if (a == "T") return a; + if (b == "T") return b; + + var aIsAny:Bool = a == "Dynamic" || a == "Object" || a == CommonImports.ObjectImport || a == null || a == "null"; + var bIsAny:Bool = b == "Dynamic" || b == "Object" || b == "openfl.utils.Object" || b== "" || b == null || b == "null"; + if (aIsAny && bIsAny) return "Dynamic"; + if (aIsAny) return b; + if (bIsAny) return a; + + if (a == "String") return a; + if (b == "String") return b; + + if ((a.indexOf("openfl.utils.Dictionary") == 0 || a.indexOf("Dictionary") == 0) && (b.indexOf("openfl.utils.Dictionary") == 0 || b.indexOf("Dictionary") == 0)) { + var newKey:String = foldDictionaryType2(Typer.getMapParam(a, 0), Typer.getMapParam(b, 0)); + var newValue:String = foldDictionaryType2(Typer.getMapParam(a, 1), Typer.getMapParam(b, 1)); + if (newKey == "null") newKey = "Dynamic"; + if (newValue == "null") newValue = "Dynamic"; + return "openfl.utils.Dictionary<" + newKey + "," + newValue + ">"; + } + if (typer.doImplements(a, b)) return b; + if (typer.doImplements(b, a)) return a; + return "Dynamic"; + } + + + /** it could be better if we would refine single value on occurance but not to collect all types */ + function refineStringType(types:Array, type:String):Void { + if (type != null) { + types.push(type); + } + } +} \ No newline at end of file diff --git a/src/as3hx/ForLoopRebuild.hx b/src/as3hx/ForLoopRebuild.hx new file mode 100644 index 0000000..6c306de --- /dev/null +++ b/src/as3hx/ForLoopRebuild.hx @@ -0,0 +1,406 @@ +package as3hx; + +import as3hx.As3.Expr; +import as3hx.RebuildUtils.RebuildResult; + +/** + * ... + * @author xmi + */ +class LoopPosition { + public function new() {} + public var expr:Expr; + public var num:Int; +} + +class ForLoopRebuild { + + private var loopsListPerLoopVar:Map> = new Map>(); + private var loopsListPerLoopVarStack:Array>> = []; + private var loopNumToReplace:Map = new Map(); + private var forLoopNum:Int = 0; + + public function new() {} + + private function rebuildLookUpPassArray(expressions:Array):Array { + var needUpdate:Bool = false; + var newExpressions:Array = []; + for (e in expressions) { + processLookUpPassExpr(e); + var re:Array = RebuildUtils.rebuildArray([e], rebuildLookUpPass); + if (re != null) { + for (r in re) { + newExpressions.push(r); + } + needUpdate = true; + } else { + newExpressions.push(e); + } + } + if (needUpdate) { + return newExpressions; + } else { + return null; + } + } + + public function replaceForLoopsWithWhile(expressions:Array):Array { + forLoopNum = 0; + + var re:Array = rebuildLookUpPassArray(expressions); + var rexpr:Expr = RebuildUtils.rebuild(EBlock(expressions), rebuildLookUpPass); + switch(rexpr) { + case null: + case EBlock(es): + expressions = es; + default: + } + + forLoopNum = 0; + re = RebuildUtils.rebuildArray(expressions, rebuildReplacePass); + if (re != null) { + expressions = re; + } + return expressions; + } + + private function openBlockContext():Void { + loopsListPerLoopVarStack.push(loopsListPerLoopVar); + loopsListPerLoopVar = new Map>(); + } + + private function closeBlockContext():Void { + var old:Map> = loopsListPerLoopVar; + loopsListPerLoopVar = loopsListPerLoopVarStack.pop(); + for (key in old.keys()) { + if (loopsListPerLoopVar.exists(key)) { + loopsListPerLoopVar.set(key, loopsListPerLoopVar.get(key).concat(old.get(key))); + } else { + loopsListPerLoopVar.set(key, old.get(key)); + } + } + } + + private function convertEForToEWhile(inits:Array, conds:Array, incrs:Array, expr:Expr):Array { + incrs = incrs.map(function(e) return ENL(e)); + function insertIncrementsBeforeContinue(e:Expr):RebuildResult { + switch(e) { + case EFunction(_, _): + return RebuildResult.RSkip; + case EContinue: + return RebuildResult.RReplace(EBlock( incrs.concat([ENL(e)]))); + default: + } + return null; + } + var r:Expr = RebuildUtils.rebuild(expr, insertIncrementsBeforeContinue); + if (r == null) { + r = expr; + } + + var condition:Expr; + if (conds.length == 0) { + condition = EIdent("true"); + } else { + condition = conds[0]; + for (i in 1...conds.length) { + condition = EBinop("&&", condition, conds[i], false); + } + } + + var whileBody:Array = switch(r) { + case EBlock(e): e; + default: [r]; + } + whileBody = whileBody.concat(incrs); + + var result:Array = []; + for (init in inits) { + result.push(init); + } + result.push(EWhile(condition, EBlock(whileBody), false)); + + return result; + } + + private static function getForLoopVariable(incrementExprs:Array):String { + if (incrementExprs.length < 1) return null; + switch(incrementExprs[0]) { + case null: return null; + case EBinop(_, e, _, _): + switch(e) { + case EIdent(s): return s; + default: + } + case EUnop(_, _, e): + switch(e) { + case EIdent(s): return s; + default: + } + default: + } + return null; + } + + private function rebuildReplacePass(expr:Expr):RebuildResult { + switch(expr) { + case EFor(inits, conds, incrs, e): + if (loopNumToReplace.exists(forLoopNum++)) { + var res:Array = convertEForToEWhile(inits, conds, incrs, e); + + var resRebuild:Array = RebuildUtils.rebuildArray(res, rebuildReplacePass); + if (resRebuild != null) { + res = resRebuild; + } + + return RebuildResult.RReplaceArray(res); + } + default: + } + return null; + } + + private function storeLoop(loopVariable:String, loop:LoopPosition):Void { + if (loopsListPerLoopVar.exists(loopVariable)) { + loopsListPerLoopVar.get(loopVariable).push(loop); + } else { + loopsListPerLoopVar.set(loopVariable, [loop]); + } + } + + private function dropLoops(loopVariable:String):Void { + if (loopsListPerLoopVar.exists(loopVariable)) { + loopsListPerLoopVar.remove(loopVariable); + } + } + + private function replaceLoops(loopVariable:String):Void { + if (loopsListPerLoopVar.exists(loopVariable)) { + for (f in loopsListPerLoopVar.get(loopVariable)) { + loopNumToReplace.set(f.num, true); + + } + loopsListPerLoopVar.remove(loopVariable); + } + } + + private function processLookUpPassExpr(expr:Expr):Void { + var loopVariablesAccess:Map = getOverwrittenIdents(expr, loopsListPerLoopVar); + for (loopVariable in loopVariablesAccess.keys()) { + var wasOverwritten:Bool = loopVariablesAccess.get(loopVariable); + if (wasOverwritten) { + dropLoops(loopVariable); + } else { + replaceLoops(loopVariable); + } + } + } + + private function rebuildLookUpPass(expr:Expr):RebuildResult { + switch(expr) { + case EFor(inits, conds, incrs, e): + if (!canUseForLoop(inits, conds, incrs, e)) { + var r:Expr = RebuildUtils.rebuild(e, rebuildLookUpPass); + if (r != null) e = r; + var res:Array = convertEForToEWhile(inits, conds, incrs, e); + return RebuildResult.RReplaceArray(res); + } else { + var loopVariable:String = getForLoopVariable(incrs); + var loop:LoopPosition = new LoopPosition(); + loop.expr = expr; + loop.num = forLoopNum++; + storeLoop(loopVariable, loop); + + var r:Expr = RebuildUtils.rebuild(e, rebuildLookUpPass); + if (r != null) { + return RebuildResult.RReplace(EFor(inits, conds, incrs, r)); + } else { + return RebuildResult.RSkip; + } + } + case EBlock(expressions): + openBlockContext(); + var newExpressions:Array = rebuildLookUpPassArray(expressions); + closeBlockContext(); + if (newExpressions != null) { + return RebuildResult.RReplace(EBlock(newExpressions)); + } else { + return RebuildResult.RSkip; + } + default: + } + return null; + } + + private static function canUseForLoop(inits:Array, conds:Array, incrs:Array, e:Expr):Bool { + if (inits.length == 0 || conds.length != 1 || incrs.length != 1) return false; + var loopVariable:String = getForLoopVariable(incrs); + var loopIdent:Expr = EIdent(loopVariable); + // if variable is not set up before loop, no FOR + switch(inits[inits.length - 1]) { + case EBinop("=", e1, e2, _): + if (!e1.equals(loopIdent)) { + return false; + } + case EVars(vars): + if (vars.length > 1) { + return false; + } + default: return false; + } + // if comparison is not `variable less then value`, no FOR + switch(conds[0]) { + case EBinop(op, e1, e2, _): + if (op == "<" || op == "<=") { + if (!e1.equals(loopIdent)) { + return false; + } + } else if (op == ">" || op == ">=") { + if (!e2.equals(loopIdent)) { + return false; + } + } else { + return false; + } + default: return false; + } + + // if variable is not incremented by 1, no FOR + switch(incrs[0]) { + case EUnop("++", _, e1): + if (!e1.equals(loopIdent)) { + return false; + } + case EBinop("+=", e1, e2, _): + if ( !e1.equals(loopIdent) || (!e2.equals(EConst(CInt("1"))) && !e2.equals(EConst(CFloat("1.0")))) ) { + return false; + } + case null: return false; + default: return false; + } + + // if variable is modified inside of loop, no FOR + if (checkIfUsesIdentForWriting(loopVariable, e, false)) { + return false; + } + + return true; + } + + private function getOverwrittenIdents(e:Expr, blockContext:Map>):Map { + var result:Map = new Map(); + for (key in loopsListPerLoopVar.keys()) { + if (checkIfUsesIdentValue(key, e)) { + result.set(key, false); + } else if (checkIfUsesIdentForWriting(key, e, true)) { + result.set(key, true); + } + } + return result; + } + + public static function checkIfUsesIdentValue(ident:String, expr:Expr):Bool { + var wasUsed:Bool = false; + function rebuild(e:Expr):RebuildResult { + switch(e) { + case EBlock(es): + for (e in es) { + if (checkIfUsesIdentValue(ident, e)) { + wasUsed = true; + break; + } else if (checkIfUsesIdentForWriting(ident, e, false)) { + break; + } + } + return RebuildResult.RSkip; + case EFor(inits, conds, incrs, e): + var uses:Bool = false; + var overwrites:Bool = false; + for (i in inits) { + if (checkIfUsesIdentValue(ident, i)) { + uses = true; + } else if (checkIfUsesIdentForWriting(ident, i, false)) { + overwrites = true; + } + } + if (overwrites && !uses) { + return RebuildResult.RSkip; + } + case EIdent(v): + if (v == ident) { + wasUsed = true; + return RebuildResult.RSkip; + } + case EVars(vars): + for (v in vars) { + if (v.name == ident) { + if (v.val != null) { + RebuildUtils.rebuild(v.val, rebuild); + } + return RebuildResult.RSkip; + } + } + case EBinop(op, e1, e2, _): + if (op == "=" && isIdent(e1, ident)) { + RebuildUtils.rebuild(e2, rebuild); + return RebuildResult.RSkip; + } + case EUnop(op, _, e): + if (isIdent(e, ident)) { + wasUsed = true; + return RebuildResult.RSkip; + } + default: + } + return null; + } + RebuildUtils.rebuild(expr, rebuild); + return wasUsed; + } + + public static function checkIfUsesIdentForWriting(ident:String, expr:Expr, definitelyOverwritten:Bool):Bool { + var result:Bool = false; + function rebuild(e:Expr):RebuildResult { + switch(e) { + case EIf(cond, e1, e2): + if (definitelyOverwritten) { + if (!result && checkIfUsesIdentForWriting(ident, e1, definitelyOverwritten) && checkIfUsesIdentForWriting(ident, e2, definitelyOverwritten)) { + result = true; + } + return RebuildResult.RSkip; + } + case EVars(vars): + for (v in vars) { + if (v.name == ident) { + result = true; + return RebuildResult.RSkip; + } + } + case EBinop(op, e1, _, _): + if (op.indexOf("=") != -1 && isIdent(e1, ident)) { + result = true; + return RebuildResult.RSkip; + } + case EUnop(op, _, e): + if (isIdent(e, ident)) { + result = true; + return RebuildResult.RSkip; + } + default: + } + return null; + } + RebuildUtils.rebuild(expr, rebuild); + return result; + } + + public static function isIdent(e:Expr, ident:String):Bool { + switch(e) { + case EParent(e): return isIdent(e, ident); + case EIdent(v): return v == ident; + default: + } + return false; + } + +} \ No newline at end of file diff --git a/src/as3hx/InheritanceTypeParser.hx b/src/as3hx/InheritanceTypeParser.hx new file mode 100644 index 0000000..0a3a90a --- /dev/null +++ b/src/as3hx/InheritanceTypeParser.hx @@ -0,0 +1,27 @@ +package as3hx; +import as3hx.As3.ClassDef; + +/** + * ... + * @author d.skvortsov + */ +class InheritanceTypeParser +{ + public static function getParent(typer:Typer, child:ClassDef):ClassDef { + switch (child.extend) { + case null: + case TPath(p): + var parentClassString:String = typer.getImportString(p, true); + var path:String = typer.resolveClassIdent(parentClassString); + if (path == null) { + path = WriterImports.getImport(parentClassString, cfg, classes, child, null, parentPath.substring(0, parentPath.lastIndexOf("."))); + } + if (path != null) { + parentPath = path; + return classDefs.get(path); + } + default: + } + return null; + } +} \ No newline at end of file diff --git a/src/as3hx/ParserUtils.hx b/src/as3hx/ParserUtils.hx index b4558c4..2d0d290 100644 --- a/src/as3hx/ParserUtils.hx +++ b/src/as3hx/ParserUtils.hx @@ -1,5 +1,6 @@ package as3hx; +import haxe.PosInfos; import as3hx.Tokenizer; import as3hx.Error; import as3hx.Parser; @@ -10,10 +11,15 @@ class ParserUtils { public static inline function escapeName(name:String):String { return switch(name) { case "cast": "__DOLLAR__cast"; + case "enum": "_enum"; + case "_enum": "__enum"; + case "__enum": "___enum"; + case "static": "_static"; + case "_static": "__static"; default: StringTools.replace(name, "$", "__DOLLAR__"); } } - + /** * Takes a token that may be a comment and returns * an array of tokens that will have the comments @@ -76,7 +82,7 @@ class ParserUtils { /** * Takes an expression e and adds the comment 'tk' to it - * as a trailing comment, iif tk is a TCommented, discarding + * as a trailing comment, if tk is a TCommented, discarding * whatever the comment target token is. */ public static function tailComment(e:Expr, tk:Token) : Expr { @@ -132,7 +138,7 @@ class ParserUtils { return t; } default: - return t; + return t; } } @@ -141,6 +147,7 @@ class ParserUtils { */ public static function removeNewLineExpr(e : Expr, removeComments : Bool = true) : Expr { return switch(e) { + case null: null; case ENL(e2): removeNewLineExpr(e2, removeComments); case ECommented(s,b,t,e2): if (removeComments) { @@ -152,17 +159,15 @@ class ParserUtils { } } - public static function unexpected(tk:Token) : Dynamic { - throw EUnexpected(Tokenizer.tokenString(tk)); - return null; + public static function unexpected(tk:Token, ?pos:PosInfos) : Dynamic { + return throw EUnexpected(Tokenizer.tokenString(tk) + '(${pos.fileName}:${pos.lineNumber})'); } /** - * In certain cases, a typedef will be generated + * In certain cases, a typedef will be generated * for a class attribute, for better type safety */ - public static function generateTypeIfNeeded(classVar : ClassField) - { + public static function generateTypeIfNeeded(classVar : ClassField) { //this only applies to static field attributes, as //they only define constants values if (!Lambda.has(classVar.kwds, "static")) { @@ -199,12 +204,12 @@ class ParserUtils { default: return null; } - - //if the arary is empty, type can't be defined + + //if the array is empty, type can't be defined if (arrayDecl.length == 0) { return null; } - + //return the type of an object field var getType:Expr->String = function(e) { switch (e) { @@ -215,12 +220,12 @@ class ParserUtils { case CFloat(v): return "Float"; case CString(v): - return "String"; + return "String"; } case EIdent(id): if (id == "true" || id == "false") { return "Bool"; - } + } return "Dynamic"; default: return "Dynamic"; @@ -228,7 +233,7 @@ class ParserUtils { } //Type declaration is only created for array of objects,. - //Type is retrieved from the first object fields, then + //Type is retrieved from the first object fields, then //all remaining objects in the array are check against this //type. If the type is different, then it is a mixed type //array and no type declaration should be created @@ -316,23 +321,36 @@ class ParserUtils { */ public static function opt2(tokenizer:Tokenizer, tk:Token, cmntOut:Array) : Bool { var t = tokenizer.token(); - var tu = ParserUtils.uncomment(t); - var trnl = ParserUtils.removeNewLine(tu); + var tu = ParserUtils.removeNewLine(t); Debug.dbgln(Std.string(t) + " to " + Std.string(tu) + " ?= " + Std.string(tk)); - if( ! Type.enumEq(trnl, tk) ) { + if( ! Type.enumEq(tu, tk) ) { tokenizer.add(t); return false; } switch(t) { - case TCommented(_,_,_): - cmntOut.push(ParserUtils.makeECommented(t, null)); + case TCommented(_, _, _): + cmntOut.push(ParserUtils.makeEmptyECommentedRecursive(t)); default: } return true; } + private static function makeEmptyECommentedRecursive(ctk:Token) : Expr { + return switch(ctk) { + case TCommented(s, b, t): + ECommented(s,b,false,makeEmptyECommentedRecursive(t)); + case TNL(t): + var r = makeEmptyECommentedRecursive(t); + // remove trailing new lines + r == null ? null : ENL(r); + default: + null; + } + } + public static function makeUnop(op:String, e:Expr):Expr { return switch(e) { + case ETernary(cond, e1, e2): ETernary(EUnop(op, true, cond), e1, e2); case EBinop(bop, e1, e2, n): EBinop(bop, makeUnop(op, e1), e2, n); default: EUnop(op, true, e); } diff --git a/src/as3hx/RebuildParentStaticFieldAccess.hx b/src/as3hx/RebuildParentStaticFieldAccess.hx new file mode 100644 index 0000000..c43a391 --- /dev/null +++ b/src/as3hx/RebuildParentStaticFieldAccess.hx @@ -0,0 +1,42 @@ +package as3hx; +import as3hx.As3.Expr; +import as3hx.As3.Program; +import as3hx.RebuildUtils.RebuildResult; + +/** + * ... + * @author + */ +class RebuildParentStaticFieldAccess { + private var cfg:Config; + private var typer:Typer; + private var program:Program; + + public function new(cfg:Config, typer:Typer) { + this.cfg = cfg; + this.typer = typer; + } + + public function apply(program:Program):Void { + this.program = program; + RebuildUtils.rebuildProgram(program, cfg, typer, rebuild); + } + + private function rebuild(expr:Expr):RebuildResult { + switch(expr) { + case EIdent(v): + var staticFieldHost:String = typer.getIsStaticField(v); + if (staticFieldHost != null) { + var a:Array = staticFieldHost.split("."); + typer.addImport(program, TPath(a)); + var staticFieldHostName:String = a[a.length - 1]; + return RebuildResult.RReplace(EField(EIdent(staticFieldHostName), v)); + } + default: + //if (staticFieldHost != null) { + //write(staticFieldHost + "."); + //} + } + return null; + } +} \ No newline at end of file diff --git a/src/as3hx/RebuildUtils.hx b/src/as3hx/RebuildUtils.hx new file mode 100644 index 0000000..d1d9b32 --- /dev/null +++ b/src/as3hx/RebuildUtils.hx @@ -0,0 +1,459 @@ +package as3hx; +import as3hx.As3.Expr; +import as3hx.As3.Function; +import as3hx.As3.Program; +import as3hx.As3.SwitchCase; +import as3hx.As3.SwitchDefault; +import as3hx.As3.T; + +enum RebuildResult { + RReplace( expr : Expr ); // replace current expression with provided expr + RSkip; // do not iterate through child expressions of current expr + RNull; // no actions needed for this expr, continue recursion + RReplaceArray( es : Array ); // replace current expression with group of expressions. This operation could be applied only to expression in an array + REmpty; // remove current expression. This operation could be applied only to expression in an array +} + +/* + * RebuildUtils methods can iterate all through provided Expr and it's parameters and somehow process all Exprs by provideed method + */ +class RebuildUtils +{ + public static function rebuildProgram(program:Program, cfg:Config, typer:Typer, rebuild:Expr->RebuildResult):Void { + for (d in program.defs) { + switch (d) { + case CDef(c): + var path:String = typer.getPackageString(program.pack) + c.name; + typer.setPackage(typer.getImportString(program.pack, false)); + typer.setImports(WriterImports.getImports(program, cfg, c), null); + typer.enterClass(path, c); + for (field in c.fields) { + switch(field.kind) { + case FFun(f): + typer.enterFunction(f, field.name, c); + var expr:Expr = RebuildUtils.rebuild(f.expr, rebuild); + if (expr != null) { + f.expr = expr; + } + rebuildArgs(f, rebuild); + typer.leaveFunction(); + case FVar(t, val): + var expr:Expr = RebuildUtils.rebuild(EBinop("=", EIdent(field.name), val, false), rebuild); + if (expr != null) { + // type could be changed while rebuilding + t = field.kind.getParameters()[0]; + switch(expr) { + case EBinop(_, _, val, _): + field.kind = FVar(t, val); + typer.overrideFieldType(path, field.name, typer.expandType(t)); + default: + } + } + default: + } + } + case FDef(f): + var expr:Expr = RebuildUtils.rebuild(f.f.expr, rebuild); + if (expr != null) { + f.f.expr = expr; + } + rebuildArgs(f.f, rebuild); + default: + } + } + } + + public static function rebuildArray(es:Array, rebuildMethod:Expr->RebuildResult):Array { + var needRebuild:Bool = false; + var rs:Array = new Array(); + for (i in 0...es.length) { + if (rebuildToArray(es[i], rebuildMethod, rs)) { + needRebuild = true; + } + } + if (needRebuild) { + return rs; + } else { + return null; + } + } + + public static function rebuild(e:Expr, rebuildMethod:Expr -> RebuildResult):Expr { + if (e == null) return null; + var r:RebuildResult = rebuildMethod(e); + switch(r) { + case RReplace(expr): return expr; + case RSkip: return null; + case null, RNull: + default: + } + switch(e) { + case ECommented(s, isBlock, isTail, e1): + var re = rebuild(e1, rebuildMethod); + if (re == null) { + return e; + } else { + return ECommented(s, isBlock, isTail, re); + } + case ENL(e1): + var re = rebuild(e1, rebuildMethod); + if (re == null) { + return e; + } else { + return ENL(re); + } + default: + return rebuildExprParams(e, rebuildMethod); + } + } + + public static function rebuildToArray(e:Expr, rebuildMethod:Expr->RebuildResult, output:Array):Bool { + var r:RebuildResult = rebuildMethod(e); + switch(r) { + case RReplace(expr): + output.push(expr); + return true; + case RReplaceArray(es): + for (expr in es) { + output.push(expr); + } + return true; + case REmpty: + return true; + case null, RNull: + switch(e) { + case ECommented(s, isBlock, isTail, e1): + if (e1 != null) { + var l:Int = output.length; + var needRebuild = rebuildToArray(e1, rebuildMethod, output); + var i:Int = output.length; + while (i-- > l + 1) { + output[i] = output[i]; + } + output[i] = ECommented(s, isBlock, isTail, output[i]); + return needRebuild; + } + case ENL(e1) : + if (e1 != null) { + var l:Int = output.length; + var needRebuild = rebuildToArray(e1, rebuildMethod, output); + var i:Int = output.length; + while (i-- > l) { + output[i] = ENL(output[i]); + } + return needRebuild; + } + default: + var expr:Expr = rebuildExprParams(e, rebuildMethod); + if (expr != null) { + output.push(expr); + return true; + } + } + case RSkip: + default: + } + output.push(e); + return false; + } + + private static function rebuildExprParams(e:Expr, rebuildMethod:Expr -> RebuildResult):Expr { + switch (e) { + case EFunction(f, name): + var rexpr = rebuild(f.expr, rebuildMethod); + if (rexpr == null) return null; + rebuildArgs(f, rebuildMethod); + return EFunction({args:f.args, varArgs:f.varArgs, ret:f.ret, expr:rexpr}, name); + case EBlock(es): + var r:Array = rebuildArray(es, rebuildMethod); + if (r == null) { + return null; + } else { + return EBlock(r); + } + case EArrayDecl(es): + var r:Array = rebuildArray(es, rebuildMethod); + if (r == null) { + return null; + } else { + return EArrayDecl(r); + } + case EForIn(e1, e2, e3): + var re1:Expr = rebuild(e1, rebuildMethod); + var re2:Expr = rebuild(e2, rebuildMethod); + var re3:Expr = rebuild(e3, rebuildMethod); + if (re1 != null || re2 != null || re3 != null) { + if (re1 == null) re1 = e1; + if (re2 == null) re2 = e2; + if (re3 == null) re3 = e3; + return EForIn(re1, re2, re3); + } else { + return null; + } + case EForEach(e1, e2, e3): + var re1:Expr = rebuild(e1, rebuildMethod); + var re2:Expr = rebuild(e2, rebuildMethod); + var re3:Expr = rebuild(e3, rebuildMethod); + if (re1 != null || re2 != null || re3 != null) { + if (re1 == null) re1 = e1; + if (re2 == null) re2 = e2; + if (re3 == null) re3 = e3; + return EForEach(re1, re2, re3); + } else { + return null; + } + case EWhile(e1, e2, e3): + var re1:Expr = rebuild(e1, rebuildMethod); + var re2:Expr = rebuild(e2, rebuildMethod); + if (re1 != null || re2 != null) { + if (re1 == null) re1 = e1; + if (re2 == null) re2 = e2; + return EWhile(re1, re2, e3); + } else { + return null; + } + case EIf(e1, e2, e3): + var re1:Expr = rebuild(e1, rebuildMethod); + var re2:Expr = rebuild(e2, rebuildMethod); + var re3:Expr = rebuild(e3, rebuildMethod); + if (re1 != null || re2 != null || re3 != null) { + if (re1 == null) re1 = e1; + if (re2 == null) re2 = e2; + if (re3 == null) re3 = e3; + return EIf(re1, re2, re3); + } else { + return null; + } + case EFor(e1, e2, e3, e4): + var re1:Array = rebuildArray(e1, rebuildMethod); + var re2:Array = rebuildArray(e2, rebuildMethod); + var re3:Array = rebuildArray(e3, rebuildMethod); + var re4:Expr = rebuild(e4, rebuildMethod); + if (re1 != null || re2 != null || re3 != null || re4 != null) { + if (re1 == null) re1 = e1; + if (re2 == null) re2 = e2; + if (re3 == null) re3 = e3; + if (re4 == null) re4 = e4; + return EFor(re1, re2, re3, re4); + } else { + return null; + } + case ETry(e, catches): //ETry( e : Expr, catches : Array<{ name : String, t : Null, e : Expr }> ) + var re:Expr = rebuild(e, rebuildMethod); + var needRebuild = false; + var rcatches:Array<{ name : String, t : Null, e : Expr }> = []; + for (c in catches) { + var rce:Expr = rebuild(c.e, rebuildMethod); + if (rce != null) { + needRebuild = true; + rcatches.push({ + name:c.name, + t:c.t, + e:rce + }); + } else { + rcatches.push(c); + } + } + if (re != null || needRebuild) { + if (re == null) re = e; + return ETry(re, rcatches); + } else { + return null; + } + case ESwitch(e, cases, def)://ESwitch( e : Expr, cases : Array, def : Null) + var re:Expr = rebuild(e, rebuildMethod); + var needRebuild = false; + var rcases:Array = []; + for (c in cases) { + var rc = rebuildSwitchCase(c, rebuildMethod); + if (rc == null) { + rcases.push(c); + } else { + rcases.push(rc); + needRebuild = true; + } + } + var rdef:SwitchDefault = null; + if (def != null) { + rdef = rebuildSwitchDefault(def, rebuildMethod); + } + if (re != null || rdef != null || needRebuild) { + if (re == null) re = e; + if (rdef == null) rdef = def; + return ESwitch(re, rcases, rdef); + } else { + return null; + } + case ENew(t, params): + var rparams:Array = rebuildArray(params, rebuildMethod); + if (rparams != null) { + if (rparams == null) rparams = params; + return ENew(t, rparams); + } else { + return null; + } + case ENamespaceAccess(e, f): + var re:Expr = rebuild(e, rebuildMethod); + if (re == null) return null; + return ENamespaceAccess(re, f); + case EArray(e, index): + var re:Expr = rebuild(e, rebuildMethod); + var rindex:Expr = rebuild(index, rebuildMethod); + if (re == null && index == null) return null; + if (re == null) re = e; + if (rindex == null) rindex = index; + return EArray(re, rindex); + case EField(e, f): + var re:Expr = rebuild(e, rebuildMethod); + if (re == null) return null; + return EField(re, f); + case ECall(e, params): + var re:Expr = rebuild(e, rebuildMethod); + var rparams:Array = rebuildArray(params, rebuildMethod); + if (re != null || rparams != null) { + if (re == null) re = e; + if (rparams == null) rparams = params; + return ECall(re, rparams); + } else { + return null; + } + case EUnop(op, prefix, e): + var re:Expr = rebuild(e, rebuildMethod); + if (re == null) return null; + return EUnop(op, prefix, re); + case EReturn(e): + var re:Expr = rebuild(e, rebuildMethod); + if (re == null) return null; + return EReturn(re); + case EParent(e): + var re:Expr = rebuild(e, rebuildMethod); + if (re == null) return null; + return EParent(re); + case ETernary(cond, e1, e2): + var rcond:Expr = rebuild(cond, rebuildMethod); + var re1:Expr = rebuild(e1, rebuildMethod); + var re2:Expr = rebuild(e2, rebuildMethod); + if (rcond != null || re1 != null || re2 != null) { + if (rcond == null) rcond = cond; + if (re1 == null) re1 = e1; + if (re2 == null) re2 = e2; + return ETernary(rcond, re1, re2); + } else { + return null; + } + case EBinop(op, e1, e2, newLineAfterOp): + var re1:Expr = rebuild(e1, rebuildMethod); + var re2:Expr = rebuild(e2, rebuildMethod); + if (re1 != null || re2 != null) { + if (re1 == null) re1 = e1; + if (re2 == null) re2 = e2; + return EBinop(op, re1, re2, newLineAfterOp); + } else { + return null; + } + case ETypedExpr(e, t): + var re:Expr = rebuild(e, rebuildMethod); + if (re == null) return null; + return ETypedExpr(re, t); + case EVars(vars): + if (vars.length == 1) { + var v = vars[0]; + if (v.val != null) { + var rval:Expr = rebuild(v.val, rebuildMethod); + if (rval == null) return null; + return EVars([{name:v.name, t:v.t, val:rval}]); + } + } + case EObject(fields): + var needRebuild:Bool = false; + var resultFields:Array<{ name : String, e : Expr }> = []; + for (i in 0...fields.length) { + var re:Expr = rebuild(fields[i].e, rebuildMethod); + if (re != null) { + resultFields.push({name:fields[i].name, e:re}); + needRebuild = true; + } else { + resultFields.push(fields[i]); + } + } + if (needRebuild) { + return EObject(resultFields); + } + case ECommented(a, b, c, e): + e = rebuild(e, rebuildMethod); + if (e == null) return null; + return ECommented(a, b, c, e); + default: + } + return null; + } + + private static function rebuildSwitchDefault(def:SwitchDefault, rebuildMethod:Expr->RebuildResult):SwitchDefault { + var el:Array = rebuildArray(def.el, rebuildMethod); + var meta:Array = rebuildArray(def.meta, rebuildMethod); + var vals:Array = null; + var before:SwitchCase = null; + if (Reflect.hasField(def, "vals") && def.vals != null) { + vals = rebuildArray(def.vals, rebuildMethod); + } + if (Reflect.hasField(def, "before") && def.before != null) { + before = rebuildSwitchCase(def.before, rebuildMethod); + } + if (el != null || meta != null || before != null || vals != null) { + if (el == null) el = def.el; + if (meta == null) meta = def.meta; + var rdef:SwitchDefault = { + el:el, + meta:meta + } + if (vals != null) { + rdef.vals = vals; + } else if (def.vals != null) { + rdef.vals = def.vals; + } + if (before != null) { + rdef.before = before; + } else if (def.before != null) { + rdef.before = def.before; + } + return rdef; + } else { + return null; + } + } + + private static function rebuildSwitchCase(c:SwitchCase, rebuildMethod:Expr->RebuildResult):SwitchCase { + var rval:Expr = rebuild(c.val, rebuildMethod); + var rel:Array = rebuildArray(c.el, rebuildMethod); + var rmeta:Array = rebuildArray(c.meta, rebuildMethod); + if (rval != null || rel != null || rmeta != null) { + if (rval == null) rval = c.val; + if (rel == null) rel = c.el; + if (rmeta == null) rmeta = c.meta; + return { + val: rval, + el: rel, + meta: rmeta + } + } else { + return null; + } + } + + private static function rebuildArgs(f:Function, rebuildMethod:Expr->RebuildResult):Void { + //Array<{ name : String, t : Null, val : Null, exprs:Array }> + for (v in f.args) { + switch(v.val) { + case null: + case EConst(_): + case EIdent("null"): + default: + var rv:Expr = rebuild(v.val, rebuildMethod); + if (rv != null) { + v.val = rv; + } + } + } + } +} \ No newline at end of file diff --git a/src/as3hx/SignalRebuild.hx b/src/as3hx/SignalRebuild.hx new file mode 100644 index 0000000..7c53099 --- /dev/null +++ b/src/as3hx/SignalRebuild.hx @@ -0,0 +1,276 @@ +package as3hx; +import as3hx.As3.Definition; +import as3hx.As3.Program; +import as3hx.As3.T; +import as3hx.As3.Expr; +import as3hx.RebuildUtils.RebuildResult; + +/** + * ... + * @author ... + */ +class SignalRebuild +{ + private var cfg:Config; + private var typer:Typer; + + private var currentField:String = null; + private var currentFieldType:T; + + private var succededReplace:Bool = false; + + private static var toCleanUp:Array = []; + + public function new(cfg:Config, typer:Typer) { + this.cfg = cfg; + this.typer = typer; + } + + public function apply(program:Program):Void { + var t1:Float = haxe.Timer.stamp(); + RebuildUtils.rebuildProgram(program, cfg, typer, rebuildSignals); + var t2:Float = haxe.Timer.stamp(); + rebuildParameters(program); + var t3:Float = haxe.Timer.stamp(); + refineVariableTypes(program); + var t4:Float = haxe.Timer.stamp(); + var name:String = "null"; + switch(program.defs[0]) { + case CDef(c):name = c.name; + default: + } + //trace(name + " " + (t2 - t1) + " " + (t3 - t2) + " " + (t4 - t3)); + } + + public function refineVariableTypes(program:Program):Void { + RebuildUtils.rebuildProgram(program, cfg, typer, rebuildVariableTypes); + } + + private function isSignalType(s:String):Bool { + return s != null && (s.indexOf("Signal<") == 0 || s.indexOf("signals.Signal<") == 0); + } + + private function isNativeSignalType(s:String):Bool { + return s != null && (s.indexOf("NativeSignal") == 0 || s.indexOf("signals.NativeSignal") == 0); + } + + private function rebuildVariableTypes(expr:Expr):RebuildResult { + switch (expr) { + case ECall(e, params) : + switch(e) { + case EField(e, field): + var s:String = typer.getExprType(e); + if (field == "add" || field == "addOnce" || field == "remove") { + if (isSignalType(s)) { + var p:Array = []; + var types:String = s.substring(s.indexOf("<") + 1, s.length - 1); + if (types == "T" || types == "Type" || types == "T->Void" || types == "Type->Void") return null; + var split:Array = types.split("->"); + if (split.length == 1) { + split.push("Void"); + } + for (s in split) { + p.push(typer.expandType(TPath([s]))); + } + typer.overrideExprType(params[0], TFunction(p)); + } else if (isNativeSignalType(s)) { + var p:Array = [TPath(["Dynamic"]), TPath(["Void"])]; + typer.overrideExprType(params[0], TFunction(p)); + } + } else if (field == "dispatch") { + if (isSignalType(s)) { + var p:Array = []; + var types:String = s.substring(s.indexOf("<") + 1, s.length - 1); + if (types == "T" || types == "Type" || types == "T->Void" || types == "Type->Void") return null; + if (types == "Void") types = "Void->Void"; + for (s in types.split("->")) { + //p.push(TPath([s])); + p.push(typer.expandType(TPath([s]))); + } + var rparams:Array = []; + for (i in 0...params.length) { + rparams.push(ETypedExpr(params[i], p[i])); + } + return RebuildResult.RReplace(ECall(EField(e, field), rparams)); + } + } + default: + } + default: + } + return null; + } + + private function tryOverrideType(name:String, t:T):Void { + typer.overrideIdentType(name, typer.expandType(t)); + } + + public static function cleanup(cfg:Config, typer:Typer):Void { + var s:SignalRebuild = new SignalRebuild(cfg, typer); + for (i in 0...5) { + var prevToCleanUp:Array = toCleanUp.copy(); + toCleanUp = []; + var list:Array = []; + for (p in prevToCleanUp) { + s.apply(p); + if (p.defs.length > 0) { + switch(p.defs[0]) { + case CDef(c): + list.push(p.pack + "." + c.name); + default: + } + } + } + neko.Lib.println("SignalRebuild cleanUp " + prevToCleanUp.length + " left: " + list); + if (toCleanUp.length == 0) { + neko.Lib.println("SignalRebuild cleanUp complete"); + break; + } + } + } + + private function rebuildParameters(program:Program):Void { + for (d in program.defs) { + switch (d) { + case CDef(c): + var path:String = (program.pack.length > 0 ? program.pack.join(".") + "." : "") + c.name; + typer.enterClass(path, c); + for (field in c.fields) { + currentField = field.name; + switch(field.kind) { + case FVar(t, val): + case FFun(f): + typer.enterFunction(f, field.name, c); + currentFieldType = f.ret.t; + switch (currentFieldType) { + case null: + case TPath(pathArray): + var p:String = pathArray.join("."); + if (p == "Signal" || p == "org.osflash.signals.Signal" || p == "idv.cjcat.signals.Signal" || p == "signals.Signal") { + succededReplace = false; + var expr:Expr = RebuildUtils.rebuild(f.expr, rebuildParams); + if (!succededReplace) { + if (toCleanUp.indexOf(program) == -1) { + toCleanUp.push(program); + } + } + } + if (p == "ISignal" || p == "org.osflash.signals.ISignal" || p == "idv.cjcat.signals.ISignal" || p == "signals.ISignal") { + succededReplace = false; + var expr:Expr = RebuildUtils.rebuild(f.expr, rebuildParamsInterface); + if (!succededReplace) { + if (toCleanUp.indexOf(program) == -1) { + toCleanUp.push(program); + } + } + } + default: + } + typer.leaveFunction(); + default: + } + } + case FDef(f): + //var expr:Expr = RebuildUtils.rebuild(f.f.expr, rebuildParams); + //if (expr != null) { + //f.f.expr = expr; + //} + default: + } + } + } + + private function rebuildParams(expr:Expr):RebuildResult { + switch(expr) { + case EReturn(e): + if (e != null) { + var t:String = typer.getExprType(e); + if (t != null && t != "null" && t != "Signal") { + succededReplace = true; + tryOverrideType(currentField, TPath([t])); + } + } + default: + } + return null; + } + + private function rebuildParamsInterface(expr:Expr):RebuildResult { + switch(expr) { + case EReturn(e): + if (e != null) { + var t:String = typer.getExprType(e); + if (t != null && t != "null" && t != "Signal") { + succededReplace = true; + t = StringTools.replace(t, "", "Void>"); + tryOverrideType(currentField, TPath([StringTools.replace(t, "Signal", "ISignal")])); + } + } + default: + } + return null; + } + + private function rebuildSignals(expr:Expr):RebuildResult { + switch(expr) { + case EBinop("=", e1, e2, newLineAfterOp): + var types = getNewSignalType(typer.getExprType(e1), typer.getExprType(e2)); + if (types != null) { + var t:T = TPath([types]); + switch(e1) { + case EIdent(v): + var variableType:T = typer.expandType(t); + if (typer.getExprType(e1) == "ISignal") { + types = StringTools.replace(types, "", "Void>"); + variableType = TPath([StringTools.replace(typer.expandStringType(types), "Signal", "ISignal")]); + } + tryOverrideType(v, variableType); + default: + } + return RebuildResult.RReplace(EBinop("=", e1, ENew(TPath([typer.shortenStringType(types)]), []), newLineAfterOp)); + } + case EVars(vars): + for (v in vars) { + if (v.val == null) continue; + + var ts:String = typer.tstring(v.t); + var types = getNewSignalType(ts, typer.getExprType(v.val)); + if (types != null) { + var a:Array = [types]; + //var a:Array = ["Signal<" + types + ">"]; + var t:T = TPath(a); + if (ts == "ISignal") { + a[0] = StringTools.replace(a[0], "", "Void>"); + a[0] = "I" + a[0]; + } + tryOverrideType(v.name, t); + v.t = truncateFullPath(types); + } + } + default: + } + return null; + } + + private function truncateFullPath(complexType:String):T { + var delimiters:Array = []; + var types:Array> = Typer.getTypeParams(complexType, delimiters); + var shortTypes:Array = []; + for (p in types) { + shortTypes.push(p[p.length - 1]); + } + var s:String = shortTypes[0] + "<" + shortTypes[1]; + for (i in 2...shortTypes.length) { + s += "->" + shortTypes[i]; + } + return TPath([s + ">"]); + } + + private function getNewSignalType(oldType:String, newType:String):String { + if (newType != null && newType.indexOf("Signal<") == 0 && typer.shortenStringType(oldType) != newType) { + return newType; + } else { + return null; + } + } +} \ No newline at end of file diff --git a/src/as3hx/Tokenizer.hx b/src/as3hx/Tokenizer.hx index 6ef4f72..4690093 100644 --- a/src/as3hx/Tokenizer.hx +++ b/src/as3hx/Tokenizer.hx @@ -144,7 +144,7 @@ class Tokenizer { char = nextChar(); } switch( char ) { - case 'x'.code: + case 'x'.code, 'X'.code: if( buf.toString() == "0" ) { do { buf.addChar(char); @@ -155,7 +155,7 @@ class Tokenizer { } pushBackChar(char); return TConst(CInt(buf.toString())); - case 'e'.code: + case 'e'.code | "E".code: if( buf.toString() == '.' ) { pushBackChar(char); return TDot; @@ -251,6 +251,7 @@ class Tokenizer { var old = line; var contents : String = "/*"; try { + char = null; while( true ) { while( char != "*".code ) { if( char == "\n".code ) { diff --git a/src/as3hx/Typer.hx b/src/as3hx/Typer.hx new file mode 100644 index 0000000..6bd4fd5 --- /dev/null +++ b/src/as3hx/Typer.hx @@ -0,0 +1,1802 @@ +package as3hx; + +import as3hx.As3.ClassDef; +import as3hx.As3.ClassField; +import as3hx.As3.Expr; +import as3hx.As3.Function; +import as3hx.As3.FunctionDef; +import as3hx.As3.Program; +import as3hx.As3.T; +import as3hx.RebuildUtils.RebuildResult; + +/** + * AS3 typing allows multiple equal variable type declarations in one method and opens new variable context only inside function, not in any {} block + */ +class Typer { + + public var classes(default,null) : Map> = new Map>(); + public var classDefs(default,null) : Map = new Map(); + public var classChildren(default,null):Map> = new Map>(); + public var currentPath(default, null):String = null; + var cfg:Config; + var classesByPackages:Map> = new Map>(); + var parentStaticFields : Map = new Map(); + var funDefs : Map = new Map(); + var classPrograms : Map = new Map(); + var staticContext : Map = new Map(); + var context : Map = new Map(); + var contextStack : Array> = []; + var functionStack : Array = []; + var functionStackName : Array = []; + var pack:String = null; + var importsMap : Map; + var classPacks:Map = new Map(); + var _hostClassRef:Reference = new Reference(); + + public function new(cfg:Config) { + this.cfg = cfg; + } + + public function getContextClone(relativeLevel:Int = 0):Map { + var context:Map; + if (relativeLevel < 0 && -relativeLevel <= contextStack.length) { + context = contextStack[contextStack.length + relativeLevel]; + } else { + context = this.context; + } + var clone:Map = new Map(); + for (v in context.keys()) { + clone.set(v, context.get(v)); + } + for (v in staticContext.keys()) { + clone.set(v, context.get(v)); + } + return clone; + } + + public function isExistingIdent(ident:String):Bool { + switch(ident) { + case "as3hx.Compat", "Std", "true", "false", "encodeURI", "decodeURI", "escape", "unescape", "encodeURIComponent", "decodeURIComponent": return true; + default: + } + ident = getModifiedIdent(ident); + if (context.exists(ident)) { + return true; + } else if (staticContext.exists(ident)) { + return true; + } else if (importsMap != null && importsMap.exists(ident)) { + return true; + } + return false; + } + + private function getQualifiedClassName(shortName:String):String { + if (classes.exists(shortName)) return shortName; + if (importsMap != null && importsMap.exists(shortName)) return importsMap.get(shortName); + return ""; + } + + public function getThisIdentType(ident:String):String { + return contextStack.length > 0 ? contextStack[0].get(ident) : null; + } + + public function getIsInterface(e:Expr):Bool { + var type:String = getExprType(e, true); + if (type != null && classDefs.exists(type)) { + return classDefs[type].isInterface; + } else { + return false; + } + } + + public function doImplements(a:String, b:String):Bool { + var ca:ClassDef = classDefs[a]; + var cb:ClassDef = classDefs[b]; + if (cb == null) return false; + while (ca != null) { + if (ca == cb) return true; + if (cb.isInterface) { + for (t in ca.implement) { + switch(t) { + case TPath(t): + if (t[t.length - 1] == cb.name) return true; + if (t[t.length - 1].indexOf("." + cb.name) != -1) return true; + default: + } + } + } + var classPackage:String = classPacks.get(ca); + var parentPath:String = getPathByType(ca.extend, ca, null, classPackage); + if (parentPath == null) break; + ca = classDefs[parentPath]; + } + return false; + } + + public function getField(v:String, hostClass:Reference, c:ClassDef = null):ClassField { + if (c == null) { + c = classDefs[currentPath]; + } + while (c != null) { + for (f in c.fields) { + hostClass.value = c; + if (f.name == v) return f; + } + if (c.extend == null) break; + var classPackage:String = classPacks.get(c); + var parentPath:String = getPathByType(c.extend, c, null, classPackage); + if (parentPath != null) { + c = classDefs[parentPath]; + } else { + break; + } + } + return null; + } + + public function getFunctionArgTypes(e:Expr):Array { + var c:ClassDef = null; + var f:ClassField = null; + switch (e) { + case EIdent(v): + c = classDefs[currentPath]; + f = getField(v, _hostClassRef, c); + case EField(e, field): + var t:String = getExprType(e); + if (t != null) { + c = classDefs[t]; + f = getField(field, _hostClassRef, c); + } + default: + } + if (f != null) { + c = _hostClassRef.value; + switch(f.kind) { + case FFun(f): + var r:Array = []; + for (i in 0...f.args.length) { + var a = f.args[i]; + if (a != null && a.t != null) { + r.push(expandType(a.t, c)); + } else { + r.push(null); + } + } + return r; + default: + } + } + return null; + } + + public function isVariable(ident:String):Bool { + var isConstructor:Bool = classDefs[currentPath] != null && classDefs[currentPath].name == ident; + return !isConstructor && (context.exists(ident) || staticContext.exists(ident)); + } + + public function getExprType(e:Expr, isFieldAccess:Bool = false):Null { + switch (e) { + case null: return null; + case ETypedExpr(e2, t): return tstring(t); + case EField(e2, f): + var t2 = null; + switch(e2) { + case EIdent("super"): + var c:ClassDef = classDefs[currentPath]; + if (c != null && c.extend != null) { + var classPackage:String = classPacks.get(c); + var parentPath:String = getPathByType(c.extend, c, null, classPackage); + if (parentPath != null && classDefs[parentPath] != null) { + t2 = parentPath; + } + } + case EIdent("this"): + return contextStack.length > 0 ? contextStack[0].get(f) : context.get(f); + default: + } + if (t2 == null) { + t2 = getExprType(e2, true); + } + if (t2 != null && (t2.indexOf("Array<") == 0 || t2.indexOf("Vector<") == 0 || t2.indexOf(cfg.arrayTypePath + "<") == 0)) { + switch(f) { + case "length": return "Int"; + case "join": return "String->String"; + case "sort" : return "Function->Void"; + } + } + switch (t2) { + case "Dynamic", CommonImports.ObjectType, CommonImports.ObjectImport: + switch(f) { + case "hasOwnProperty": return "String->Bool"; + } + return "Dynamic"; + case "Reflect": + switch(f) { + case "hasField": return "Dynamic->String->Bool"; + case "field": return "Dynamic->String->Dynamic"; + case "setField": return "Dynamic->String->Dynamic->Void"; + case "compareMethods": return "Dynamic->Dynamic->Bool"; + } + case "Signal","signals.Signal": + switch(f) { + case "add": return "Function->Void"; + case "addOnce": return "Function->Void"; + case "remove": return "Function->Void"; + } + case "as3hx.Compat": + switch(f) { + case "parseInt": return "Float->Int"; + case "setTimeout": return "Dynamic->Int->Int"; + case "getQualifiedClassName": return "Dynamic->String"; + } + case "AS3": + switch(f) { + case "string": return "Dynamic->String"; + case "int": return "Dynamic->Int"; + case "parseInt": return "Dynamic->Int"; + case "hasOwnProperty": return "Dynamic->String->Bool"; + } + case "Date": + switch(f) { + case "getTime": return "Void->Float"; + case "now": return "Void->Date"; + case "time": return "Float"; + } + case "Math": + switch(f) { + case "ceil", "floor", "round": return "Float->Int"; + case "max", "min": return "Float->Float->Float"; + case "random": return "Void->Float"; + default: return "Float->Float"; + } + case "Std": + switch(f) { + case "int": return "Float->Int"; + case "is": return "Dynamic->Dynamic->Bool"; + case "parseFloat": return "String->Float"; + case "parseInt": return "String->Int"; + case "random": return "Int->Int"; + case "string": return "Dynamic->String"; + default: + } + case "StringTools": + return switch(f) { + case "replace": return "String->String->String->String"; + default: return "String"; + } + case "String": + return switch(f) { + case "length": return "Int"; + case "charCodeAt": return "Int->Int"; + case "indexOf": return "String->Int"; + case "lastIndexOf": return "String->Int"; + case "split": return "String->Array"; + case "substr", "substring": return "Int->Int->String"; + case "replace": return "String->String->String"; + case "toLowerCase", "toUpperCase", "toString", "toLocaleLowerCase", "toLocaleUpperCase": return "Void->String"; + case "fromCharCode": return "Int->String"; + default: return "String"; + } + case "FastXMLNodeAccess": return "FastXML"; + case "FastXMLNodeListAccess": return "Void->FastXMLList"; + case "FastXMLAttribAccess": return "String"; + case "FastXMLHasAttribAccess": return "Bool"; + case "FastXMLHasNodeAccess": return "Bool"; + case "FastXML": + return switch(f) { + case "copy": return "Void->FastXML"; + case "node":"FastXMLNodeAccess"; + case "nodes":"FastXMLNodeListAccess"; + case "att":"FastXMLAttribAccess"; + case "has":"FastXMLHasAttribAccess"; + case "hasNode":"FastXMLHasNodeAccess"; + case "name":"Void->String"; + case "innerData":"String"; + case "innerHTML":"String"; + case "descendants": "String->FastXMLList"; + //case "descendants", "nodes": "FastXMLList"; + //case "node": "FastXML"; + //case "length": "Int"; + case _: "FastXMLList"; + } + case "FastXMLList": + switch(f) { + case "copy": return "Void->FastXMLList"; + case "length": return "Int"; + case "descendants": return "String->FastXMLList"; + case "get": return "Int->FastXML"; + default: return "FastXMLList"; // will be transformed to .descendants() + } + default: + if (t2 != null && t2.indexOf("Dictionary<") == 0) { + switch(f) { + case "exists": return "Dynamic->Bool"; + } + } + } + if (t2 != null) { + var index:Int = t2.indexOf("<"); + var typeParam:String = null; + if (index != -1) { + typeParam = t2.substring(index + 1, t2.length - 1); + t2 = t2.substr(0, index); + } + var cl:String = resolveClassIdent(t2); + if (context.exists(t2)) { + t2 = context.get(t2); + } else if (staticContext.exists(t2)) { + t2 = staticContext.get(t2); + } + if (importsMap != null && importsMap.exists(t2)) { + var t:String = importsMap.get(t2); + if (t != null) t2 = t; + } + if (t2.indexOf(".") != -1) { + var t2Array:Array = t2.split("."); + t2 = getImportString(t2Array, true); + } + if (classes.exists(t2)) { + var t:String = classes.get(t2).get(f); + if (t != null) { + var typeParams:String = classDefs[t2].typeParams; + var index:Int = t.indexOf("<"); + if (index != -1) { + var resultTypeParam = t.substring(index + 1, t.length - 1); + if (resultTypeParam == typeParams) { + t = t.substring(0, index + 1) + typeParam + ">"; + } + } else if (t.indexOf(typeParams) != -1) { + var delimiters:Array = []; + var instanceTypeParams:Array> = getTypeParams(t, delimiters); + var hasChange:Bool = false; + for (tt in instanceTypeParams) { + if (tt.length == 1 && tt[0] == typeParams) { + tt[0] = typeParam; + hasChange = true; + } + } + if (hasChange) { + t = reuniteTypeParams(instanceTypeParams, delimiters); + } + } + return t; + } + } + } + return null; + case ECall(e, params): + /* handling of as3 typing calls */ + switch(e) { + case EIdent("Object"): return getExprType(params[0]); + case EIdent("Number"): return "Float"; + case EIdent("String"): return "String"; + case EIdent("int"): return "Int"; + case EIdent("uint"): return "Int"; + case EIdent("getQualifiedClassName"): return "String"; + case EVector(t): return "Vector<" + tstring(t) + ">"; + case EField(e, "instance"): + switch(e) { + case EIdent("Std"): + return getExprType(params[1]); + default: + } + case EField(e, "splice"): + return 'Array'; // todo get array type + default: + } + var t:String = getExprType(e); + if (t != null) { + var li:Int = t.lastIndexOf("->"); + if (li != -1) { + return t.substr(li + 2); + } + } + case EIdent(s): + switch (s) { + case "as3hx.Compat": return "as3hx.Compat"; + case "Std": return "Std"; + case "true", "false": return "Bool"; + case "encodeURI", "decodeURI", "escape", "unescape", "encodeURIComponent", "decodeURIComponent": return "String->String"; + case "this": return currentPath; + case "trace": return "Dynamic->Void"; + case "super": + var c:ClassDef = classDefs[currentPath]; + if (c != null && c.extend != null) { + var classPackage:String = classPacks.get(c); + var parentPath:String = getPathByType(c.extend, c, null, classPackage); + if (parentPath != null && classDefs[parentPath] != null) { + c = classDefs[parentPath]; + for (f in c.fields) { + if (f.name == c.name) { + switch(f.kind) { + case FFun(f): + var pack:Array = classPrograms[c].pack; + var constructorReturnType:T = TPath([pack.length > 0 ? pack + "." + c.name : c.name]); + return "Dynamic->" + tstring(getFunctionType(f, constructorReturnType)); + default: + } + } + } + } + } + return "Void->Dynamic"; + default: + } + //var ss:String = s; + var ident:Bool = false; + s = getModifiedIdent(s); + if (context.exists(s)) { + s = context.get(s); + ident = true; + } else if (staticContext.exists(s)) { + s = staticContext.get(s); + ident = true; + } + if (s != null && importsMap != null && importsMap.exists(s)) { + var t:String = importsMap.get(s); + s = t == null ? s : t; + if (!ident && !isFieldAccess) return "Class"; + } + return s; + case EVars(vars) if(vars.length == 1): return tstring(vars[0].t); + case EArray(n, _): + var tn:String = getExprType(n); + if (tn != null) { + if (StringTools.startsWith(tn, "Array")) { + return tn.substring(6, tn.lastIndexOf(">")); + } else if (StringTools.startsWith(tn, cfg.arrayTypePath)) { + return expandStringType(tn.substring(cfg.arrayTypePath.length + 1, tn.lastIndexOf(">"))); + } else if (StringTools.startsWith(tn, "Vector")) { + return expandStringType(tn.substring(7, tn.lastIndexOf(">"))); + } else if (StringTools.startsWith(tn, "Dictionary") || StringTools.startsWith(tn, "openfl.utils.Dictionary")) { + var bothTypes:String = tn.substring(tn.indexOf("<") + 1, tn.lastIndexOf(">")); + var commaPosition:Int = -1; + var level:Int = 0; + for (i in 0...bothTypes.length) { + var char:Int = bothTypes.charCodeAt(i); + if (char == "<".code) { + level++; + } else if (char == ">".code) { + level--; + } else if (char == ",".code) { + if (level == 0) { + commaPosition = i; + break; + } + } + } + if (commaPosition != -1) { + return bothTypes.substr(commaPosition + 1); + } + } else if (tn == "Object" || tn == CommonImports.ObjectType || tn == CommonImports.ObjectImport) { + return "Dynamic"; + } + } + return tn; + case EArrayDecl(a): + var type:String = null; + for (e in a) { + var t:String = getExprType(e); + if (type != t) { + if (type == null) { + type = t; + } else { + type = null; + break; + } + } + } + if (type == null) { + return "Array"; + } else { + return "Array<" + type + ">"; + } + case EUnop(_, _, e2): return getExprType(e2); + case EParent(e): return getExprType(e); + case ETernary(cond, e1, e2): return getExprType(e1); + case EVector(t): return "Vector<" + tstring(t) + ">"; + case EBinop(op, e1, e2, _) : + switch(op) { + case "=" : return getExprType(e1); + case "as": return getExprType(e2, true); + case "/" : return "Float"; + case "is" | "in" | "||" | "&&" | "!=" | "!==" | "==" | "===" | ">" | ">=" | "<" | "<=": return "Bool"; + default: + var t1:String = getExprType(e1); + var t2:String = getExprType(e2); + if (op == "+" && t1 == "String" || t2 == "String") return "String"; + if (t1 != "Float" && t1 != "Dynamic" && t2 != "Float" && t2 != "Dynamic") return "Int"; + return "Float"; + } + case ENew(t, params): + switch(t) { + case TComplex(e): + var pack:Array = getPackString(e); + if (pack != null) { + t = TPath(pack); + } + default: + } + switch(t) { + case TPath(p): + if (p[p.length - 1] == "Signal") { + var types:Array = []; + for (param in params) { + switch(param) { + case EVector(t): + types.push(tstring(TVector(t))); + case EIdent(v): + v = getModifiedIdent(v); + var fullPath:String = resolveClassIdent(v); + types.push(tstring(TPath([fullPath == null ? v : fullPath]))); + //types.push(tstring(TPath([fullPath]))); + //types.push(tstring(TPath([v]))); + default: + } + } + types.push("Void"); + return "Signal<" +types.join("->") + ">"; + } else if (p[p.length - 1] == "Dictionary" && cfg.useOpenFlTypes) { + return "Dictionary"; + } + default: + } + return tstring(t); + case ECondComp(_, e, e2): + return getExprType(e); + case ENL(e): + return getExprType(e); + case EObject(fl): + return CommonImports.ObjectType; + case EBlock(es): + if (es.length == 1) { + return getExprType(es[0]); + } + //case EFunction(f, _): + //return tstring(getFunctionType(f, null)); + case EConst(c): + return switch(c) { + case CInt(_): "Int"; + case CFloat(_): "Float"; + case CString(_): "String"; + } + case ERegexp(_, _): return getRegexpType(); + default: + } + return null; + } + + inline function isBooleanOp(s:String):Bool return switch(s) { + case "||" | "&&" | "!=" | "!==" | "==" | "===": true; + case _: false; + } + + public function getModifiedIdent(s : String) : String { + return switch(s) { + case "int": "Int"; + case "uint": cfg.uintToInt ? "Int" : "UInt"; + case "Number": "Float"; + case "Boolean": "Bool"; + case "Function": cfg.functionToDynamic ? "Dynamic" : s; + case "Object": cfg.useOpenFlTypes ? CommonImports.ObjectType : "Dynamic"; + case "undefined": "null"; + //case "Error": cfg.mapFlClasses ? "flash.errors.Error" : s; + case "XML": "FastXML"; + case "XMLList": "FastXMLList"; + case "NaN":"Math.NaN"; + case "Dictionary": cfg.dictionaryToHash ? "haxe.ds.ObjectMap" : s; + case "decodeURI": "StringTools.urlDecode"; + case "encodeURI": "StringTools.urlEncode"; + case "decodeURIComponent": "StringTools.urlDecode"; + case "encodeURIComponent": "StringTools.urlEncode"; + case "escape": "StringTools.htmlEscape"; + case "unescape": "StringTools.htmlUnescape"; + //case "QName": cfg.mapFlClasses ? "flash.utils.QName" : s; + default: s; + }; + } + + public static function tstringStatic(t:T) : String { + if(t == null) return null; + return switch(t) { + case TStar: "Dynamic"; + case TVector(t): "Vector<" + tstringStatic(t) + ">"; + case TPath(p): + var c = p.join("."); + return c; + default: t + ""; + } + } + + public function tstring(t:T, isNativeGetSet:Bool = false, fixCase:Bool = true) : String { + if(t == null) return null; + return switch(t) { + case TStar: "Dynamic"; + case TVector(t): cfg.vectorToArray ? "Array<" + tstring(t) + ">" : "Vector<" + tstring(t) + ">"; + case TPath(p): + var c = p.join("."); + return switch(c) { + case "Array" : "Array"; + case "Boolean" : "Bool"; + case "Class" : "Class"; + case "int" : "Int"; + case "Number" : "Float"; + case "uint" : cfg.uintToInt ? "Int" : "UInt"; + case "void" : "Void"; + case "Function" : cfg.functionToDynamic ? "Dynamic" : c; + case CommonImports.ObjectType : isNativeGetSet ? "{}" : (cfg.useOpenFlTypes ? CommonImports.ObjectType : "Dynamic"); + case "XML" : cfg.useFastXML ? "FastXML" : "Xml"; + case "XMLList" : cfg.useFastXML ? "FastXMLList" : "Iterator"; + case "RegExp" : cfg.useCompat ? "as3hx.Compat.Regex" : "flash.utils.RegExp"; + //default : fixCase ? properCase(c, true) : c; + default : + if (cfg.importExclude.indexOf(c) != -1) { + c = p[p.length - 1]; + } else { + if (fixCase) c = properCase(c, true); + } + var s:String = c; + if (s.indexOf("<") != -1) { + var delimiters:Array = new Array(); + var t:Array> = getTypeParams(s, delimiters); + s = getModifiedIdent(t[0].join(".")); + for (i in 1...t.length) { + s += delimiters[i - 1] + getModifiedIdent(t[i].join(".")); + } + if (delimiters.length == t.length) { + s += delimiters[delimiters.length - 1]; + } + } + s; + } + case TComplex(e): return getExprType(e); // not confirmed + case TDictionary(k, v): (cfg.dictionaryToHash ? "haxe.ds.ObjectMap" : "Dictionary") + "<" + tstring(k) + ", " + tstring(v) + ">"; + case TFunction(p): p.map(function(it) { + var s:String = tstring(it); + if (s != null && s.indexOf("->") != -1) { + return "(" + s + ")"; + } else { + return s; + } + }).join("->"); + } + } + + public function addGlobalFunction(path:String, f:FunctionDef):Void { + var contextRoot = contextStack.length > 0 ? contextStack[0] : context; + funDefs[f.name] = path; + //contextRoot.set(f.name, getFunctionType(f.f)); + } + + public function hasGlobalFunction(name:String):String { + return funDefs.get(name); + } + + public function addProgram(p:Program):Void { + var pack:String = getImportString(p.pack, false); + var path:String = p.pack.length > 0 ? pack + "." : ""; + var packMap:Map; + if (classesByPackages.exists(pack)) { + packMap = classesByPackages.get(pack); + } else { + packMap = new Map(); + classesByPackages.set(pack, packMap); + } + for (d in p.defs) { + switch(d) { + case CDef(c): addClass(p, pack, path + c.name, c); + packMap.set(c.name, path + c.name); + classPrograms.set(c, p); + case FDef(f): addGlobalFunction(path + f.name, f); + case NDef(n): + default: + } + } + } + + public function addClass(p:Program, pack:String, path:String, c:ClassDef):Void { + var classMap:Map = new Map(); + if (!cfg.useFullTyping) { + parseClassFields(p, pack, path, c, classMap); + } + classes[path] = classMap; + classDefs[path] = c; + classPacks.set(c, pack); + } + + public function parseParentClasses():Void { + for (path in classes.keys()) { + var c:ClassDef = classDefs[path]; + parseClassFields(classPrograms[c], classPacks[c], path, c, classes[path]); + } + for (path in classes.keys()) { + parseParentClass(path); + } + } + + private function parseParentClass(path:String):Map { + var classMap:Map = classes.get(path); + if (!classMap.exists("%childrenComplete")) { + var c:ClassDef = classDefs.get(path); + if (c.isInterface) { + for (t in c.implement) { + parseOneParentClass(t, path, c, classMap); + } + } else if (c.extend != null) { + parseOneParentClass(c.extend, path, c, classMap); + } + classMap.set("%childrenComplete", ""); + } + return classMap; + } + + private function parseOneParentClass(parentT:T, path:String, c:ClassDef, classMap:Map):Void { + var classPackage:String = classPacks.get(c); + var parentPath:String = getPathByType(parentT, c, null, classPackage); + if (parentPath != null) { + if (!classChildren.exists(parentPath)) { + classChildren.set(parentPath, [path]); + } else { + classChildren.get(parentPath).push(path); + } + var parentClassMap:Map = parseParentClass(parentPath); + for (field in parentClassMap.keys()) { + classMap.set(field, parentClassMap.get(field)); + } + } + } + + public function getPathByType(t:T, hostClass:ClassDef, packageArray:Array = null, packageString:String = null):String { + switch (t) { + case null: + case TPath(p): + if (p.length == 1 && p[0].indexOf(".") != -1) { + p = p[0].split("."); + } + var parentClassString:String = getImportString(p, true); + if (parentClassString.indexOf("<") != -1) { + parentClassString = parentClassString.substr(0, parentClassString.indexOf("<")); + } + var path:String; + if (parentClassString.indexOf(".") == -1) { + path = resolveClassIdent(parentClassString); + if (path == null) { + if (packageString == null && packageArray != null) { + packageString = getImportString(packageArray, false); + } + path = WriterImports.getImport(parentClassString, cfg, classes, hostClass, null, packageString); + } + } else { + path = parentClassString; + } + if (path != null && classes.exists(path)) { + return path; + } + default: + } + return null; + } + + public function getClassConstructorTypes(path:String):Array { + var c:String = path; + if (context.exists(path)) { + c = context.get(path); + } else if (staticContext.exists(path)) { + c = staticContext.get(path); + } else if (importsMap != null && importsMap.exists(path)) { + var t:String = importsMap.get(path); + c = t == null ? path : t; + } + var def:ClassDef = classDefs.get(c); + if (def != null) return getConstructorType(def); + return null; + } + + public function getIsStaticField(ident:String):String { + if (!parentStaticFields.exists(ident) || context.exists(ident)) { + return null; + } + return parentStaticFields.get(ident); + } + + private function isLocalIdent(v:String):Bool { + if (contextStack.length > 0 && context.exists(v) && !contextStack[0].exists(v)) { + return true; + } + for (i in 1...contextStack.length) { + if (contextStack[i].exists(v)) { + return true; + } + } + return false; + } + + public function overrideExprType(expr:Expr, t:T):Void { + switch (expr) { + case null: + case EIdent(v): + if (isLocalIdent(v)) { + overrideLocalType(v, t); + } else { + overrideIdentType(v, t); + } + case EField(e, f): + var cName:String = getExprType(e); + if (cName != null) { + overrideIdentType(f, t, classDefs[cName]); + } else { + trace("null expr type for " + e + "." + f); + } + default: + } + } + + public function overrideLocalType(v:String, t:T):Void { + var index:Int = functionStack.length - 1; + while (index >= 0) { + var f:Function = functionStack[index]; + + var found:Bool = false; + + var short:T = shortenType(t); + for (arg in f.args) { + if (arg.name == v) { + arg.t = short; + if (functionStackName[index] != null) { + overrideExprType(EIdent(functionStackName[index]), getFunctionType(f, null)); + } + found = true; + } + } + if (!found) { + found = overrideFunctionLocalType(f, v, short); + } + if (found) { + for (i in index...contextStack.length) { + contextStack[i].set(v, tstring(t)); + } + context.set(v, tstring(t)); + } + index--; + } + } + + public function overrideFunctionLocalType(f:Function, v:String, t:T):Bool { + //trace("override local type " + currentPath + "." + v + " : " + t + " (UNIMPLEMENTED"); + //t = shortenType(t); + //for (arg in f.args) { + //if (arg.name == v) { + //arg.t = t; + //return true; + //} + //} + var replaced:Bool = false; + function lookUpForTyping(expr:Expr):RebuildResult { + switch(expr) { + case EVars(vars): + for (variable in vars) { + if (variable.name == v) { + variable.t = t; + replaced = true; + } + } + case EFunction(fun, name): + if (name == v) { + replaced = true; + switch(t) { + case TFunction(p): + overrideFunctionParams(fun, p); + default: + trace("UNCOMPATEABLE TYPE : " + t + " with function " + name); + } + } + // Stop parsing this branch. We are not interested in variables in another scope + return RebuildResult.RSkip; + default: + } + return null; + } + RebuildUtils.rebuild(f.expr, lookUpForTyping); + return replaced; + } + + public function overrideFieldType(path:String, name:String, t:T, overrideInParents:Bool = true):Void { + if (currentPath == path) { + context.set(name, tstring(t)); + } + var c:ClassDef = classDefs[path]; + var classMap:Map = classes[path]; + if (classMap == null) return; + classMap.set(name, tstring(t)); + if (overrideInParents && c.extend != null) { + var classPackage:String = classPacks.get(c); + var parentPath:String = getPathByType(c.extend, c, null, classPackage); + if (parentPath != null) { + var parentClassMap:Map = classes.get(parentPath); + if (parentClassMap.exists(name)) { + overrideFieldType(parentPath, name, t); + return; + } + } + } + var children:Array = classChildren[path]; + if (children != null) { + for (childPath in children) { + overrideFieldType(childPath, name, t, false); + } + } + } + + public static function reuniteTypeParams(t:Array>, delimiters:Array):String { + var s:String = t[0].join("."); + for (i in 1...t.length) { + s += delimiters[i - 1] + t[i].join("."); + } + if (delimiters.length == t.length) { + s += delimiters[delimiters.length - 1]; + } + return s; + } + + public static function getTypeParams(type:String, delimiters:Array = null):Array> { + var result:Array> = new Array>(); + var name:String = ""; + var inDelimiter:Bool = false; + for (i in 0...type.length) { + var c:Int = type.charCodeAt(i); + var newInDelimiter:Bool; + switch(c) { + case "<".code, ">".code, "-".code, ",".code, " ".code, "(".code, ")".code: + newInDelimiter = true; + default: + newInDelimiter = false; + } + if (newInDelimiter != inDelimiter) { + inDelimiter = newInDelimiter; + if (name.length > 0) { + if (inDelimiter) { + result.push(name.split(".")); + } else { + if (delimiters != null) { + delimiters.push(name); + } + } + name = ""; + } + } + name += String.fromCharCode(c); + } + if (inDelimiter) { + if (delimiters != null) { + delimiters.push(name); + } + } else { + result.push(name.split(".")); + } + return result; + } + + public function expandStringType(stringType:String, c:ClassDef = null):String { + if (stringType == null) return null; + return expandStringTypeToArray(stringType, c).join("."); + } + + public function expandType(t:T, c:ClassDef = null):T { + switch (t) { + case null: + if (c != null) trace("null T for class ", c.name); + return null; + case TVector(t): + return TVector(expandType(t, c)); + case TFunction(p): + var pr:Array = []; + for (pi in p) { + pr.push(expandType(pi, c)); + } + return TFunction(pr); + case TPath(p): + var lastWord:String = p[p.length - 1]; + return TPath(expandStringTypeToArray(lastWord, c)); + default: return t; + } + } + + private function expandStringTypeToArray(stringType:String, c:ClassDef = null):Array { + var delimiters:Array = new Array(); + var t:Array> = getTypeParams(stringType, delimiters); + var s:String; + var r:Array; + var l:Int = 1; + if (t[0].length == 1) { + if (l == 1) { + r = getFullTypeName(t[0][0], c).split("."); + s = r.pop(); + } else { + s = getFullTypeName(t[0][0], c); + r = []; + } + } else { + if (l == 1) { + r = t[0]; + s = r.pop(); + } else { + s = t[0].join("."); + r = []; + } + } + for (i in 1...t.length) { + var fullType:String = ''; + if (t[i].length == 1) { + fullType = getFullTypeName(t[i][0], c); + } else { + fullType = t[i].join("."); + } + s += delimiters[i - 1] + fullType; + } + if (delimiters.length == t.length) { + s += delimiters[delimiters.length - 1]; + } + r.push(s); + return r; + } + + public function shortenTypeAndImport(c:ClassDef, t:T):T { + if (c != null) { + addTypeImports(classPrograms[c], t); + } + return shortenType(t); + } + + public function shortenType(t:T):T { + switch (t) { + case TFunction(p): + var pr = []; + for (i in 0...p.length) { + pr.push( shortenType(p[i]) ); + } + return TFunction(pr); + case TDictionary(k, v): + return TDictionary(shortenType(k), shortenType(v)); + case TPath(p): + var s:String = p[p.length - 1]; + var delimiters:Array = new Array(); + var t:Array> = getTypeParams(s, delimiters); + var s:String = t[0][t[0].length - 1]; + for (i in 1...t.length) { + s += delimiters[i - 1] + t[i][t[i].length - 1]; + } + if (delimiters.length == t.length) { + s += delimiters[delimiters.length - 1]; + } + return TPath([s]); + default: return t; + } + } + + public function shortenStringType(type:String):String { + var delimiters:Array = []; + var t:Array> = getTypeParams(type, delimiters); + var s:String = t[0][t[0].length - 1]; + for (i in 1...t.length) { + s += delimiters[i - 1] + t[i][t[i].length - 1]; + } + if (delimiters.length == t.length) { + s += delimiters[delimiters.length - 1]; + } + return s; + } + + private function addTypeImports(program:Program, t:T):Void { + switch(t) { + case TDictionary(k, v): + addTypeImports(program, k); + addTypeImports(program, v); + case TPath(p): + var i:String; + if (p.length == 1) { + p = p[0].split("."); + t = TPath(p); + } + i = getImportString(p, true); + if (i.indexOf(">") != -1) { + for (t in getTypeParams(i)) { + if (t.length != 1 || t[0] != "Void" || t[0] != "Dynamic") { + addImport(program, TPath(t)); + } + } + } else { + addImport(program, t); + } + default: + addImport(program, t); + } + } + + public function overrideIdentType(name:String, t:T, c:ClassDef = null):Void { + if (c == null) { + if (classDefs.exists(currentPath)) { + c = classDefs.get(currentPath); + overrideFieldType(currentPath, name, t); + } + } else { + overrideFieldType(getClassPath(c), name, t); + } + if (c != null) { + var f:ClassField = getField(name, _hostClassRef, c); + c = _hostClassRef.value; + if (f == null) return; + switch(f.kind) { + case FVar(oldT, val): + t = shortenTypeAndImport(c, t); + f.kind = FVar(t, val); + case FFun(fun): + if (f.kwds.indexOf("get") == -1 && f.kwds.indexOf("set") == -1) { + switch(t) { + case TFunction(p): + overrideFunctionParams(fun, p, c); + default: + t = shortenTypeAndImport(c, t); + fun.ret.t = t; + } + } else if (fun.ret.t != null) { + t = shortenTypeAndImport(c, t); + fun.ret.t = t; + } else if (fun.args.length != 0) { + t = shortenTypeAndImport(c, t); + fun.args[0].t = t; + } + default: + } + } + } + + private function overrideFunctionParams(fun:Function, types:Array, c:ClassDef = null):Void { + fun.ret.t = types[types.length - 1]; + var expressionsToInsert:Array = []; + for (i in 0...types.length - 1) { + var arg = fun.args[i]; + if (arg == null) continue; + var t:T = types[i]; + if (!typesAreEqual(shortenType(t), arg.t)) { + t = shortenTypeAndImport(c, t); + var oldT:T = arg.t; + var oldName:String = arg.name; + arg.t = t; + arg.name = "__" + oldName; + var e:Expr = EIdent(arg.name); + var ts:String = tstring(oldT); + if (ts != "Dynamic") { + e = EBinop("as", e, EIdent(ts), false); + } + expressionsToInsert.push(ENL(EVars([{name:oldName, t:oldT, val:e}]))); + for (j in 0...arg.exprs.length) { + var e:Expr = arg.exprs[j]; + switch(e) { + case EIdent(v): + arg.exprs[j] = EIdent(arg.name); + case ETypedExpr(e, oldT): + arg.exprs[j] = ETypedExpr(e, types[i]); + default: + } + } + } + } + if (expressionsToInsert.length > 0) { + unshiftIntoFunction(fun, expressionsToInsert); + } + } + + private function unshiftIntoFunction(f:Function, toInsert:Array):Void { + var doInsert:Expr -> Expr = null; + doInsert = function(e) { + if(e == null) return null; + switch(e) { + case EBlock(ex): + if (ex.length == 0) return doInsert(ex[0]); + if (ex.length > 0) { + ex[0] = ENL(ex[0]); + } + ex = toInsert.concat(ex); + return EBlock(ex); + case ENL(ex): return doInsert(ex); + case EObject(fl) if (fl.length == 0): return EBlock(toInsert); + default: + toInsert.push(ENL(e)); + return EBlock(toInsert); + } + } + f.expr = doInsert(f.expr); + } + + private function typesAreEqual(a:T, b:T):Bool { + return tstring(a) == tstring(b); + } + + private function getClassPath(c:ClassDef):String { + var pack:String = classPacks[c]; + if (pack != null && pack.length > 0) { + return pack + "." + c.name; + } else { + return c.name; + } + } + + public function getPackageString(i : Array):String { + if (i.length == 0) { + return ""; + } else if (i[0] == "flash") { + i[0] = cfg.flashTopLevelPackage; + return i.join(".") + "."; + } else { + return Writer.properCaseA(i, false).join(".") + "."; + } + } + + public function getImportString(i : Array, hasClassName:Bool):String { + if (i[0] == "flash") { + i[0] = cfg.flashTopLevelPackage; + return i.join("."); + } else { + return Writer.properCaseA(i, hasClassName).join("."); + } + } + + function resolveClassIdent(ident:String):String { + if (classes.exists(ident)) { + return ident; + } else if (importsMap != null && importsMap.exists(ident)) { + var t:String = importsMap.get(ident); + if (t != null && classes.exists(t)) { + return t; + } + } + return null; + } + + public function setPackage(pack:String):Void { + this.pack = pack; + } + + public function enterProgram(program:Program):Void { + setPackage(getImportString(program.pack, false)); + } + + public function enterClass(path:String, c:ClassDef):Void { + currentPath = path; + //parentStaticFields = new Map(); + staticContext = new Map(); + var classMap:Map; + classMap = classes.get(path); + var ic:ClassDef = c; + var parentPath:String = path; + if (ic.extend != null) { + //classMap = new Map(); + while (ic != null && ic.extend != null) { + var packageString:String = parentPath.substring(0, parentPath.lastIndexOf(".")); + if (classPrograms[ic] == null) { + ic = null; + break; + } + var path:String = getPathByType(ic.extend, ic, classPrograms[ic].pack, packageString); + if (path != null) { + parentPath = path; + ic = classDefs.get(path); + if (ic == null) break; + parseStaticFields(parentPath, ic); + } else { + ic = null; + break; + } + //switch (ic.extend) { + //case null: + //case TPath(p): + //var parentClassString:String = getImportString(p, true); + //var path:String = resolveClassIdent(parentClassString); + //if (path == null) { + //path = WriterImports.getImport(parentClassString, cfg, classes, ic, null, parentPath.substring(0, parentPath.lastIndexOf("."))); + //} + //if (path != null) { + //parentPath = path; + //ic = classDefs.get(path); + //if (ic == null) break; + //parseStaticFields(parentPath, ic); + ////var childClassMap:Map = classes.get(path); + ////if (childClassMap != null) { + ////for (key in childClassMap.keys()) { + ////classMap.set(key, childClassMap.get(key)); + ////} + ////} + //} else { + //ic = null; + //break; + //} + //default: + //} + } + } else { + //classMap = classes.get(path); + } + parseStaticFields(path, c, true); + var classMapCopy:Map = new Map(); + if (classMap != null) { + for (key in classMap.keys()) { + if (!staticContext.exists(key)) { + classMapCopy.set(key, classMap.get(key)); + } + } + } + classMap = classMapCopy; + //if (classMap == null) { + //classMap = new Map(); + //} + //for (key in staticContext.keys()) { + //if (classMap.exists(key)) { + //classMap.remove(key); + //} + //} + context = classMap; + if (contextStack.length > 0) { + contextStack[contextStack.length - 1] = context; + } + } + + public function getFullTypeName(ident:String, c:ClassDef = null):String { + //switch(s) { + //case "as3hx.Compat": return "as3hx.Compat"; + //case "Std": return "Std"; + //case "true", "false": return "Bool"; + //case "encodeURI", "decodeURI", "escape", "unescape": return "String->String"; + //default: + //} + //s = getModifiedIdent(s); + if (ident == "null") return ident; + if (context.exists(ident)) { + return context.get(ident); + } else if (staticContext.exists(ident)) { + return staticContext.get(ident); + } else if (importsMap != null && importsMap.exists(ident)) { + var t:String = importsMap.get(ident); + return t == null ? ident : t; + } else if (classes.exists(ident)) { + return ident; + } else if (cfg.useOpenFlTypes && ident == "Dictionary") { + return "openfl.utils.Dictionary"; + } + var parentPath:String; + if (c == null) { + c = classDefs[currentPath]; + parentPath = currentPath; + } else { + parentPath = getClassPath(c); + var fullTypeName:String = WriterImports.getImport(ident, cfg, classes, c, classPrograms[c], parentPath); + if (fullTypeName != null) return fullTypeName; + } + while (c.extend != null) { + parentPath = getPathByType(c.extend, c, null, parentPath.substr(0, parentPath.lastIndexOf("."))); + if (parentPath != null && classes.exists(parentPath)) { + c = classDefs.get(parentPath); + var fullTypeName:String = WriterImports.getImport(ident, cfg, classes, c, classPrograms[c], parentPath); + if (fullTypeName != null) return fullTypeName; + } else break; + } + if (ident == "null" || ident == "this") return ident; + //trace("UNKNOWN IDENT " + ident); + return ident; + } + + public function getFullTypeNameDescription(ident:String):String { + //switch(s) { + //case "as3hx.Compat": return "as3hx.Compat"; + //case "Std": return "Std"; + //case "true", "false": return "Bool"; + //case "encodeURI", "decodeURI", "escape", "unescape": return "String->String"; + //default: + //} + //s = getModifiedIdent(s); + if (context.exists(ident)) { + trace(1, context.get(ident)); + return context.get(ident); + } else if (staticContext.exists(ident)) { + trace(2, staticContext.get(ident)); + return staticContext.get(ident); + } else if (importsMap != null && importsMap.exists(ident)) { + var t:String = importsMap.get(ident); + trace(3, t); + return t == null ? ident : t; + } else if (classes.exists(ident)) { + trace(4, ident); + return ident; + } else if (cfg.useOpenFlTypes && ident == "Dictionary") { + return "openfl.utils.Dictionary"; + } + var c:ClassDef = classDefs[currentPath]; + var parentPath:String = currentPath; + while (c.extend != null) { + parentPath = getPathByType(c.extend, c, null, parentPath.substr(0, parentPath.lastIndexOf("."))); + if (parentPath != null && classes.exists(parentPath)) { + c = classDefs.get(parentPath); + var fullTypeName:String = WriterImports.getImport(ident, cfg, classes, c, classPrograms[c], parentPath); + trace(5, fullTypeName); + if (fullTypeName != null) return fullTypeName; + } else break; + } + trace(6, ident); + if (ident == "null" || ident == "this") return ident; + //trace("UNKNOWN IDENT " + ident); + return ident; + } + + public function setImports(importsMap:Map, imported:Array):Void { + this.importsMap = new Map(); + for (key in importsMap.keys()) { + var fullPath:String = importsMap.get(key); + if (fullPath != null) { + this.importsMap.set(key, fullPath); + } + } + var packMap:Map = this.classesByPackages.get(pack); + if (packMap != null) { + for (key in packMap.keys()) { + this.importsMap.set(key, packMap.get(key)); + } + } + + if (imported != null) { + for (key in imported) { + var i:Int = key.lastIndexOf("."); + this.importsMap.set(key.substr(i + 1), key); + } + } + } + + public function enterFunction(f:Function, name:String, c:ClassDef = null):Void { + openContext(f, name, c); + for (arg in f.args) { + context.set(arg.name, tstring(arg.t)); + } + if (f.varArgs != null) { + context.set(f.varArgs, "Array"); + } + var localTyping:Map = new Map(); + function lookUpForTyping(expr:Expr):RebuildResult { + switch(expr) { + case EBinop("=", EIdent(v), e2, _): + if (localTyping.exists(v)) { + if (localTyping.get(v) == null) { + context.set(v, resolveArrayType(context.get(v), getExprType(e2))); + } else { + localTyping.set(v, resolveArrayType(localTyping.get(v), getExprType(e2))); + } + } else { + localTyping.set(v, getExprType(e2)); + } + case EForEach(ev, e, block): + var etype:String = getExprType(e); + var isMap:Bool = etype != null && (etype.indexOf("Map") == 0 || etype.indexOf("Dictionary") == 0 || etype.indexOf("openfl.utils.Dictionary") == 0); + var isVector:Bool = etype != null && (etype.indexOf("Array") == 0 || etype.indexOf("Vector") == 0 || etype.indexOf(cfg.arrayTypePath) == 0); + var isXml:Bool = etype == "FastXML" || etype == "FastXMLList"; + switch(ev) { + case EVars(vars): + if(vars.length == 1 && vars[0].val == null) { + var type:String; + if (isMap) { + type = Typer.getMapParam(etype, 1); + if (type == null) { + type = "Dynamic"; + } + } else if (isVector) { + type = getVectorParam(etype); + } else if (isXml) { + type = etype; + } else { + type = "Dynamic"; + } + context.set(vars[0].name, type); + var re2:Expr = RebuildUtils.rebuild(e, lookUpForTyping); + var re3:Expr = RebuildUtils.rebuild(block, lookUpForTyping); + if (re2 != null || re3 != null) { + if (re2 == null) re2 = e; + if (re3 == null) re3 = block; + return RebuildResult.RReplace(EForEach(ev, re2, re3)); + } else { + return RebuildResult.RSkip; + } + } + default: + } + case EForIn(ev, e, block): + var etype:String = getExprType(e); + var isMap:Bool = etype != null && (etype.indexOf("Map") == 0 || etype.indexOf("Dictionary") == 0 || etype.indexOf("openfl.utils.Dictionary") == 0); + var isArray:Bool = etype != null && (etype.indexOf("Array<") == 0 || etype.indexOf("Vector<") == 0 || etype.indexOf(cfg.arrayTypePath + "<") == 0); + switch(ev) { + case EVars(vars): + if(vars.length == 1 && vars[0].val == null) { + var type:String; + if (isMap) { + type = getMapIndexType(etype); + if (type == null) { + type = "Dynamic"; + } + } else if (isArray) { + type = "Int"; + } else { + type = "String"; + } + context.set(vars[0].name, type); + var re2:Expr = RebuildUtils.rebuild(e, lookUpForTyping); + var re3:Expr = RebuildUtils.rebuild(block, lookUpForTyping); + if (re2 != null || re3 != null) { + if (re2 == null) re2 = e; + if (re3 == null) re3 = block; + return RebuildResult.RReplace(EForIn(ev, re2, re3)); + } else { + return RebuildResult.RSkip; + } + } + default: + } + case EVars(vars): + for (v in vars) { + var t:String; + switch(v.val) { + case ETypedExpr(e, _): + t = getExprType(e); + default: + t = getExprType(v.val); + } + t = resolveArrayType(tstring(v.t), t); + + if (localTyping.get(v.name) != null) { + t = resolveArrayType(t, localTyping.get(v.name)); + localTyping.set(v.name, null); + } + context.set(v.name, t); + } + case EFunction(f, name): + if (name != null) { + context.set(name, tstring(getFunctionType(f))); + } + // Stop parsing this branch. We are not interested in variables in another scope + return RebuildResult.RSkip; + default: + } + return null; + } + RebuildUtils.rebuild(f.expr, lookUpForTyping); + } + + private static var arrayDynamicIdent:String = "Array"; + private static var arrayIdent:String = "Array<"; + private function resolveArrayType(a:String, b:String):String { + if (a == null || (a == arrayDynamicIdent && b != null && b.indexOf(arrayIdent) == 0 && a != b)) { + return b; + } else { + return a; + } + } + + public function leaveFunction():Void { + closeContext(); + } + + function parseStaticFields(path:String, c:ClassDef, remove:Bool = false):Void { + for (field in c.fields) { + if (!isStatic(field)) continue; + + var type:String = null; + switch(field.kind) { + case FVar(t, val): + type = tstring(t); + case FFun(f): + if (isSetter(field)) { + type = tstring(f.args[0].t); + } else if (isGetter(field)) { + type = tstring(f.ret.t); + } else { + var constructorReturnType:T = field.name == c.name ? TPath([c.name]) : null; + type = tstring(getFunctionType(f, constructorReturnType)); + } + default: + } + if (remove) { + parentStaticFields.remove(field.name); + staticContext.remove(field.name); + } else { + parentStaticFields.set(field.name, path); + staticContext.set(field.name, type); + } + } + } + + function parseClassFields(p:Program, pack:String, path:String, c:ClassDef, map:Map):Void { + var imports:Map = WriterImports.getImports(p, cfg, c); + this.pack = pack; + this.currentPath = path; + setImports(imports, null); + for (field in c.fields) { + var type:T = null; + switch(field.kind) { + case FVar(t, val): + type = t; + case FFun(f): + if (isSetter(field)) { + type = f.args[0].t; + } else if (isGetter(field)) { + type = f.ret.t; + } else { + var constructorReturnType:T = field.name == c.name ? TPath([pack.length > 0 ? pack + "." + c.name : c.name]) : null; + type = getFunctionType(f, constructorReturnType); + } + default: + } + if (type != null) { + map.set(field.name, tstring(expandType(type, c))); + } + } + } + + inline function getRegexpType():String return cfg.useCompat ? "as3hx.Compat.Regex" : "flash.utils.RegExp"; + + inline function isGetter(c:ClassField):Bool return Lambda.has(c.kwds, "get"); + + inline function isSetter(c:ClassField):Bool return Lambda.has(c.kwds, "set"); + + inline function isStatic(c:ClassField):Bool return Lambda.has(c.kwds, "static"); + + /** + * Opens a new context for variable typing + */ + function openContext(f:Function = null, name:String = null, cl:ClassDef = null):Void { + var c = new Map(); + for(k in context.keys()) + c.set(k, context.get(k)); + contextStack.push(context); + if (f != null) { + functionStack.push(f); + functionStackName.push(name); + } + context = c; + } + + /** + * Closes the current variable typing context + */ + function closeContext():Void { + context = contextStack.pop(); + if (functionStack.length > 0) { + functionStack.pop(); + functionStackName.pop(); + } + } + + function hasImport(program:Program, path:Array):Bool { + for (p in program.imports) { + if (p.length == path.length) { + var samePath:Bool = true; + for (i in 0...p.length) { + if (p[i] != path[i]) { + samePath = false; + break; + } + } + if (samePath) { + return true; + } + } + } + return false; + } + + public function addImport(program:Program, t:T):Void { + if (program.typesSeen.indexOf(t) != -1) return; + program.typesSeen.push(t); + switch(t) { + case TPath(path): + if (path.length == 1 && (CommonImports.getImports(cfg).exists(path[0]) || path[0].indexOf(".") == -1)) return; + if (!hasImport(program, path)) { + program.imports.push(path); + } + default: + } + } + + function getConstructorType(c:ClassDef):Array { + var args:Array = []; + for (f in c.fields) { + if (f.name == c.name) { + switch(f.kind) { + case FFun(f): + for (arg in f.args) { + args.push(arg.t); + } + default: + } + } + } + return args; + } + + public function getMapIndexType(s:String):String { + var startIndex:Int = s.indexOf("<"); + if (startIndex == -1) return null; + var commaIndex:Int = s.indexOf(","); + if (commaIndex == -1) return null; + return s.substring(startIndex + 1, commaIndex); + } + + public function getPackString(expr:Expr):Array { + switch(expr) { + case EIdent(s): + return isExistingIdent(s) ? null : [s]; + case EField(e, f): + var s:Array = getPackString(e); + if (s != null) { + s.push(f); + } + return s; + default: return null; + } + } + + public static function getFunctionType(f:Function, constructorReturnType:T = null):T { + var t = f.args.map(function(it) return it.t); + if(f.varArgs != null) t.push(TPath(["Array"])); + if (t.length == 0) t.push(TPath(["Void"])); + if (constructorReturnType != null) { + t = [constructorReturnType]; + } else { + t.push(f.ret.t); + } + return TFunction(t); + } + + public static function properCase(pkg:String, hasClassName:Bool):String { + var openIndex:Int = pkg.indexOf("<"); + if (openIndex == -1) { + return Writer.properCase(pkg, hasClassName); + } else { + return Writer.properCase(pkg.substr(0, openIndex), hasClassName) + pkg.substr(openIndex); + } + } + + public static function getVectorParam(s:String):String { + var openIndex:Int = s.indexOf("<"); + if (openIndex == -1) return "Dynamic"; + var closeIndex:Int = s.lastIndexOf(">"); + if (closeIndex == -1) return "Dynamic"; + return s.substring(openIndex + 1, closeIndex); + } + public static function getMapParam(s:String, index:Int):String { + var openIndex:Int = s.indexOf("<"); + if (openIndex == -1) return "Dynamic"; + var splitIndex:Int = -1; + var depth:Int = 0; + for (i in openIndex + 1...s.length) { + switch (s.charCodeAt(i)) { + case '<'.code: depth++; + case '>'.code: depth--; + case ','.code: + if (depth == 0) { + splitIndex = i; + break; + } + } + } + if (splitIndex != -1) { + if (index == 0) { + return s.substring(openIndex + 1, splitIndex); + } else if (index == 1) { + return s.substring(splitIndex + 1, s.length - 1); + } else { + return "Dynamic"; + } + } else { + return "Dynamic"; + } + } +} + +class Reference { + public var value:T; + public function new() { } +} \ No newline at end of file diff --git a/src/as3hx/VarExprFix.hx b/src/as3hx/VarExprFix.hx new file mode 100644 index 0000000..72eb5bb --- /dev/null +++ b/src/as3hx/VarExprFix.hx @@ -0,0 +1,305 @@ +package as3hx; +import as3hx.As3.Expr; +import as3hx.As3.Function; +import as3hx.RebuildUtils.RebuildResult; +import neko.Lib; + +/** + * ... + * @author xmi + */ +class VarExprFix +{ + private var cfg:Config; + + public function new(cfg:Config){ + this.cfg = cfg; + } + + public function apply(f:Function, es:Array, typer:Typer):Array { + return new VarExprFixImplementation(cfg, f, es, typer).getResult(); + } +} + + +private class VarExprFixImplementation +{ + private static inline var FIRST_USE_IN_HEADER:Int = -1; + private static inline var FIRST_USE_IN_VAR:Int = -2; + + var cfg:Config; + var typer:Typer; + var result:Array; + + var f:Function; + var map:Map; + var localVar:Map = new Map(); + var firstUse:Map = new Map(); + var firstUseVarsByLine:Map> = new Map>(); + var blockVars:Array = new Array(); + var restrictedVars:Array = new Array(); + var varStatementsToReplace:Array = new Array(); + var localFunctionVars:Map; + var hasVarsToInsert:Bool = false; + var inLocalFunction:Bool = false; + var line:Int = 0; + var depth:Int = 0; + + public function new(cfg:Config, f:Function, es:Array, typer:Typer):Void { + this.cfg = cfg; + this.f = f; + this.typer = typer; + + map = typer.getContextClone(0); + + for (v in map.keys()) { + firstUse.set(v, FIRST_USE_IN_HEADER); + localVar.set(v, false); + } + for (arg in f.args) { + firstUse.set(arg.name, FIRST_USE_IN_HEADER); + localVar.set(arg.name, true); + } + if (f.varArgs != null && !cfg.replaceVarArgsWithOptionalArguments) { + firstUse.set(f.varArgs, FIRST_USE_IN_HEADER); + localVar.set(f.varArgs, true); + } + es = rebuildByLine(es, rebuildMethodLookForVars); + if (varStatementsToReplace.length > 0) { + es = rebuildByLine(es, rebuildMethodCleanUp); + } + if (hasVarsToInsert) { + es = rebuildByLine(es, rebuildMethodInsertVars); + } + result = es; + } + + public function getResult():Array { + return result; + } + + function rebuildByLine(es:Array, rebuildMethod:Expr->RebuildResult):Array { + var needRebuild:Bool = false; + var rs:Array = new Array(); + for (i in 0...es.length) { + line = i; + if (RebuildUtils.rebuildToArray(es[i], rebuildMethod, rs)) { + needRebuild = true; + } + } + if (needRebuild) { + return rs; + } else { + return es; + } + } + + function rebuildMethodCleanUp(e:Expr):RebuildResult { + switch(e) { + case EVars(vars/*Array<{ name : String, t : Null, val : Null }>*/): + var newVars:Array = []; + var hasChange:Bool = false; + for (vr in vars) { + var v:String = vr.name; + if (varStatementsToReplace.indexOf(v) != -1 && (!firstUseVarsByLine.exists(line) || firstUseVarsByLine.get(line).indexOf(v) == -1)) { + hasChange = true; + setFirstUseLine(v, line); + if (vr.val != null) { + newVars.push(EBinop("=", EIdent(v), vr.val, false)); + } + } else { + newVars.push(EVars([vr])); + } + } + if (hasChange) { + return RebuildResult.RReplaceArray(newVars); + } + case EFunction(f, name): + return RebuildResult.RSkip; + default: + } + return null; + } + + function rebuildMethodGetLocalFunctionVars(e:Expr):RebuildResult { + switch (e) { + case EFunction(f, name): return RebuildResult.RSkip; + case EVars(vars): + for (vr in vars) { + localFunctionVars.set(vr.name, true); + } + return RebuildResult.RSkip; + default: return null; + } + } + + function rebuildMethodLookForVars(e:Expr):RebuildResult { + switch(e) { + case EFor(inits, conds, incrs, e): + if (inLocalFunction) return null; + depth++; + var oldBlockVars:Array = blockVars; + blockVars = new Array(); + + var rinits:Array = RebuildUtils.rebuildArray(inits, rebuildMethodLookForVars); + var rconds:Array = RebuildUtils.rebuildArray(conds, rebuildMethodLookForVars); + var rincrs:Array = RebuildUtils.rebuildArray(incrs, rebuildMethodLookForVars); + var re:Expr = null; + switch(e) { + case EBlock(es): + var res:Array = RebuildUtils.rebuildArray(es, rebuildMethodLookForVars); + if (res != null) re = EBlock(res); + default: + re = RebuildUtils.rebuild(e, rebuildMethodLookForVars); + } + for (v in blockVars) { + restrictedVars.push(v); + } + blockVars = oldBlockVars; + depth--; + if (rinits == null && rconds == null && rincrs == null && re == null) { + return RebuildResult.RSkip; + } else { + if (rinits == null) rinits = inits; + if (rconds == null) rconds = conds; + if (rincrs == null) rincrs = incrs; + if (re == null) re = e; + return RebuildResult.RReplace(EFor(rinits, rconds, rincrs, re)); + } + case EBlock(es): + if (inLocalFunction) return null; + depth++; + var oldBlockVars:Array = blockVars; + blockVars = new Array(); + var r:Array = RebuildUtils.rebuildArray(es, rebuildMethodLookForVars); + for (v in blockVars) { + restrictedVars.push(v); + } + blockVars = oldBlockVars; + depth--; + if (r == null) { + return RebuildResult.RSkip; + } else { + return RebuildResult.RReplace(EBlock(r)); + } + case EIdent(v): + if (v != null) { + if (inLocalFunction && localFunctionVars.exists(v)) { + return RebuildResult.RSkip; + } + if (!localVar.exists(v)) { + if (!inLocalFunction && v == "arguments") { + var arguments:Array = []; + for (arg in f.args) { + arguments.push(EIdent(arg.name)); + } + return RebuildResult.RReplace(ECommented("/*arguments*/", true, true, EArrayDecl(arguments))); + } + return null; + } + if (!firstUse.exists(v) || firstUse.get(v) == FIRST_USE_IN_HEADER) { + firstUse.set(v, line); + setFirstUseLine(v, line); + } else if (restrictedVars.indexOf(v) != -1) { + varStatementsToReplace.push(v); + restrictedVars.remove(v); + } + } + case EFunction(f, name): + if (name != null) { + map.set(name, "Function"); + } + var oldInLocalFunction = inLocalFunction; + inLocalFunction = true; + localFunctionVars = new Map(); + RebuildUtils.rebuild(f.expr, rebuildMethodGetLocalFunctionVars); + var rexpr = RebuildUtils.rebuild(f.expr, rebuildMethodLookForVars); + inLocalFunction = oldInLocalFunction; + if (rexpr == null) return RebuildResult.RSkip; + return RebuildResult.RReplace(EFunction({args:f.args, varArgs:f.varArgs, ret:f.ret, expr:rexpr}, name)); + case EVars(vars/*Array<{ name : String, t : Null, val : Null }>*/): + if (inLocalFunction) { + return RebuildResult.RSkip; + } + var newVars:Array = []; + var hasChange:Bool = false; + for (vr in vars) { + var v:String = vr.name; + if (!localVar.exists(v)) continue; + if (firstUse.exists(v) && firstUse.get(v) != FIRST_USE_IN_HEADER) { + hasChange = true; + if (vr.val != null) { + newVars.push(EBinop("=", EIdent(v), vr.val, false)); + } + if (restrictedVars.indexOf(v) != -1) { + varStatementsToReplace.push(v); + restrictedVars.remove(v); + } + } else { + if (depth > 0) { + blockVars.push(v); + } + newVars.push(EVars([vr])); + } + firstUse.set(v, FIRST_USE_IN_VAR); + localVar.set(v, true); + map.set(v, typer.tstring(vr.t)); + } + if (hasChange) { + return RebuildResult.RReplaceArray(newVars); + } + default: + } + return null; + } + + function rebuildMethodInsertVars(e:Expr):RebuildResult { + if (firstUseVarsByLine.exists(line)) { + var res:Array = null; + for (name in firstUseVarsByLine.get(line)) { + if (firstUse.get(name) != FIRST_USE_IN_VAR) continue; + var type:String = map.get(name); + if (type == null || type == "Function") continue; + var defaultValue:Expr = switch(type) { + case "int", "Int", "UInt": + EConst(CInt("0")); + case "Float": + EConst(CInt("Math.NaN")); + default: + EIdent("null"); + } + if (res == null) res = []; + res.push(ENL(EVars([ { name:name, t:TPath([type]), val:defaultValue } ]))); + } + if (res != null && res.length > 0) { + var te:Expr = e; + var newLines:Int = 0; + while (true) { + switch(te) { + case ENL(e): + te = e; + newLines++; + default: + break; + } + } + while (newLines-- > 1) { + res[0] = ENL(res[0]); + } + res.push(ENL(te)); + return RebuildResult.RReplaceArray(res); + } + } + return RebuildResult.RSkip; + } + + + private function setFirstUseLine(varName:String, line:Int):Void { + if (firstUseVarsByLine.exists(line)) { + firstUseVarsByLine.get(line).push(varName); + } else { + firstUseVarsByLine.set(line, [varName]); + hasVarsToInsert = true; + } + } +} \ No newline at end of file diff --git a/src/as3hx/Writer.hx b/src/as3hx/Writer.hx index 66b085d..fba8a46 100644 --- a/src/as3hx/Writer.hx +++ b/src/as3hx/Writer.hx @@ -1,6 +1,8 @@ package as3hx; +import haxe.io.BytesOutput; import as3hx.As3; +import as3hx.RebuildUtils.RebuildResult; import haxe.io.Output; using Lambda; @@ -22,8 +24,8 @@ typedef CaseDef = { * @author Franco Ponticelli * @author Russell Weir */ -class Writer -{ +class Writer { + var lvl : Int; var o : Output; var cfg : Config; @@ -31,157 +33,185 @@ class Writer var loopIncrements : Array; var varCount : Int; // vars added for ESwitch or EFor initialization var isInterface : Bool; // set if current class is an interface - var context : Map; - var contextStack : Array>; + var isExtends : Bool; var inArrayAccess : Bool; var inEField : Bool; var inE4XFilter : Bool; var inLvalAssign : Bool; // current expr is lvalue in assignment (expr = valOfSomeSort) var rvalue : Expr; + var functionReturnType : T; var typeImportMap : Map; var lineIsDirty : Bool; // current line contains some non-whitespace/indent characters + var pendingTailComment : String; // one line comment that needs to be written at the end of line var genTypes : Array; //typedef generated while parsing var imported : Array; // store written imports to prevent duplicated var pack : Array; // stores the haxe file package + var generatedTypesWritten:Bool; + var validVariableNameEReg:EReg; + var typer:Typer; + var dictionaries:DictionaryRebuild; - public function new(config:Config) - { + public function new(config:Config) { this.lvl = 0; this.cfg = config; this.varCount = 0; - this.context = new Map(); - this.contextStack = new Array(); this.inArrayAccess = false; this.inEField = false; this.inE4XFilter = false; this.inLvalAssign = false; this.lineIsDirty = false; + this.pendingTailComment = null; - this.typeImportMap = new Map(); this.genTypes = []; this.imported = []; + this.typer = new Typer(config); + this.dictionaries = new DictionaryRebuild(typer, config); - var doNotImportClasses = [ - "Array", "Bool", "Boolean", "Class", "Date", - "Dynamic", "EReg", "Enum", "EnumValue", - "Float", "Map", "Int", "IntIter", - "Lambda", "List", "Math", "Number", "Reflect", - "RegExp", "Std", "String", "StringBuf", - "StringTools", "Sys", "Type", "Void", - "Function", "Object", "XML", "XMLList" - ]; - for(c in doNotImportClasses) { - this.typeImportMap.set(c, null); - } - if(!cfg.functionToDynamic) typeImportMap.set("Function", "haxe.Constraints.Function"); + this.validVariableNameEReg = new EReg("^[a-zA-Z_$][0-9a-zA-Z_$]*$", ""); + } - var topLevelErrorClasses = [ - "ArgumentError", "DefinitionError", "Error", - "EvalError", "RangeError", "ReferenceError", - "SecurityError", "SyntaxError", "TypeError", - "URIError", "VerifyError" - ]; + public function prepareTyping():Void { + typer.parseParentClasses(); + } - for(c in topLevelErrorClasses) { - this.typeImportMap.set(c, "flash.errors." + c); - } + public function refineTypes(p:Program):Void { + dictionaries.refineTypes(p); + new SignalRebuild(cfg, typer).apply(p); + new RebuildParentStaticFieldAccess(cfg, typer).apply(p); + } - for(c in cfg.importTypes.keys()) { - this.typeImportMap.set(c, cfg.importTypes.get(c)); - } + public function applyRefinedTypes(p:Program):Void { + dictionaries.applyRefinedTypes(p); + new SignalRebuild(cfg, typer).apply(p); + new CallbackRebuild(cfg, typer).apply(p); } - /** - * Opens a new context for variable typing - */ - function openContext() { - var c = new Map(); - for(k in context.keys()) - c.set(k, context.get(k)); - contextStack.push(context); - context = c; + public function finishTyping():Void { + SignalRebuild.cleanup(cfg, typer); } - /** - * Closes the current variable typing copntext - */ - function closeContext() { - context = contextStack.pop(); + public function register(p:Program):Void { + typer.addProgram(p); } function formatComment(s:String, isBlock:Bool):String { if(!isBlock) { return s; } - var r = new EReg("^(" + cfg.indentChars + ")+", "mg"); - return StringTools.ltrim(r.replace(s,indent())); + var a:Array = s.split("\n"); + var spacesInTab:Int = 4; + a[0] = StringTools.ltrim(a[0]); + var n:Int = 999; + var indent:String = this.indent(); + for (i in 1...a.length) { + var p:Int = countWhitespaces(a[i]); + if (p < n) n = p; + } + for (i in 1...a.length) { + a[i] = indent + consumeWhitespaces(a[i], n); + } + return a.join("\n"); } - function formatBlockBody(expr:Expr):Array { - var result = []; - var f:Expr->Void = null; - f = function(e) { - if(e == null) return; - switch(e) { - case EBlock(ex): - for(i in 0...ex.length) { - if (i == 0) f(ex[0]); - else result.push(ex[i]); - } - case ECommented(s,b,t,e): - // catch comments before else blocks - writeNL(); - writeIndent(s); - result.push(ENL(e)); - case ENL(ex): f(ex); - case EObject(fl) if(fl.empty()): - default: result.push(ENL(e)); + private function countWhitespaces(s:String):Int { + var n:Int = 0; + var i:Int = 0; + do { + if (s.indexOf("\t", i) == i) { + n++; + i++; + } else if (s.indexOf(" ", i) == i) { + n++; + i += 4; + } else { + break; + } + } while (true); + return n; + } + + private function consumeWhitespaces(s:String, n:Int):String { + var i:Int = 0; + while (n > 0) { + if (s.indexOf("\t", i) == i) { + n--; + i++; + } else if (s.indexOf(" ", i) == i) { + n--; + i += 4; + } else { + break; } } - f(expr); + return s.substr(i); + } + + function formatBlockBodyRecursive(e:Expr, result:Array):Void { + switch(e) { + case null: + case EBlock(ex): + for(i in 0...ex.length) { + if (i == 0) formatBlockBodyRecursive(ex[0], result); + else result.push(ex[i]); + } + case ENL(ex): + if (ex != null) { + formatBlockBodyRecursive(ex, result); + } else { + result.push(e); + } + case EObject(fl) if(fl.empty()): + default: result.push(ENL(e)); + } + } + + function formatBlockBody(expr:Expr):Array { + var result = []; + formatBlockBodyRecursive(expr, result); return result; } inline function getColon():String return cfg.spacesOnTypeColon ? " : " : ":"; - function writeComments(comments : Array) { + function writeComments(comments : Array):Void { for(c in comments) { switch(c) { - case ECommented(s,b,t,e): - writeComment(indent() + formatComment(s,b)); - if (e != null) { - switch (e) { - case ECommented(_): - writeComments([e]); - default: - throw "Unexpected " + e + " in comments"; - } - } + case null: + case ECommented(s, b, t, e): + writeExpr(c); + //writeComment(formatComment(s,b), b); + //if (e != null) { + //switch (e) { + //case ECommented(_): + //writeComments([e]); + //case ENL(_): + //writeComments([e]); + //default: + //throw "Unexpected " + e + " in comments"; + //} + //} case EImport(i): writeImport(i); case ENL(e): - writeNL(); + writeExpr(c); + //writeNL(); + //writeComments([e]); default: throw "Unexpected " + c + " in header"; } } } - function writePackage(pack : Array) - { - if (pack.length > 0) - { + function writePackage(pack : Array):Void { + if (pack.length > 0) { writeLine("package " + properCaseA(pack,false).join(".") + ";"); writeNL(); } } - function writeImports(imports : Array>) - { - if (imports.length > 0) - { - var imported = []; //holds already written types to prevent duplicates - for(i in imports) { + function writeImports(imports : Array>):Void { + if (imports.length > 0) { + for (i in imports) { writeImport(i); writeNL(); } @@ -189,9 +219,17 @@ class Writer } } - function writeImport(i : Array) - { - var type = properCaseA(i, true).join("."); + function writeImport(i : Array):Void { + var type = typer.getImportString(i, true); + if (cfg.importExclude != null && cfg.importExclude.indexOf(type) != -1) { + var short:String = type.substr(type.lastIndexOf(".") + 1); + var full:String = typeImportMap[short]; + if (full != null) { + type = full; + } else { + return; + } + } if (!Lambda.has(this.imported, type)) { //prevent duplicate import write("import " + type + ";"); imported.push(type); @@ -201,35 +239,47 @@ class Writer } } - function writeAdditionalImports(defPackage : Array, allTypes : Array, definedTypes : Array) - { + function writeAdditionalImports(defPackage : Array, allTypes : Array, definedTypes : Array):Void { // We don't want to import any type that is defined within // this file, so add each of those to the type import map // first. - for(d in definedTypes) { + for (d in definedTypes) { typeImportMap.set(d, null); } // Now convert each seen type enum into the corresponding // type import string. var uniqueTypes = new Map(); - for(t in allTypes) { + for (t in allTypes) { var importType = istring(t); if(importType != null) { if(!typeImportMap.exists(importType)) { typeImportMap.set(importType, null); } else { + //var type = typer.getImportString(i, true); + //if (cfg.importExclude != null && cfg.importExclude.indexOf(type) != -1) { + uniqueTypes.set(importType, true); } + } else { + var full:String = tstring(t); + var short:String = typer.shortenStringType(full); + if (!typeImportMap.exists(short)) { + if (short != full) { + typeImportMap.set(short, full); + uniqueTypes.set(short, true); + } + } } } // Now look up each type import string in the type import // map. - var addnImports = new Array(); + var addnImports:Array = []; for(u in uniqueTypes.keys()) { - if(typeImportMap.exists(u)) { - u = typeImportMap.get(u); + if (typeImportMap.exists(u)) { + var nu:String = typeImportMap.get(u); + u = u == nu ? null : nu; } else { u = properCaseA(defPackage, false).concat([u]).join("."); } @@ -247,14 +297,12 @@ class Writer } } - function writeDefinitions(defs : Array) - { + function writeDefinitions(defs : Array):Void { for(d in defs) writeDefinition(d); } - function writeDefinition(def : Definition) - { + function writeDefinition(def : Definition):Void { switch(def) { case CDef(c): writeClassDef(c); @@ -263,7 +311,7 @@ class Writer } } - function writeMetaData(data:Array) { + function writeMetaData(data:Array):Void { if(data == null) return; @@ -273,8 +321,10 @@ class Writer switch(d) { case EMeta(_): writeExpr(d); - case ECommented(s,b,t,e): - writeExpr(d); + case ECommented(s, b, t, e): + if (!t) { + writeExpr(d); + } case ENL(e): writeNL(); writeIndent(); @@ -295,10 +345,14 @@ class Writer } } - function writeClassDef(c : ClassDef) - { + function writeClassDef(c : ClassDef):Void { writeMetaData(c.meta); + if (!generatedTypesWritten) { + writeGeneratedTypes(genTypes); + generatedTypesWritten = true; + } + var buf = new StringBuf(); this.isInterface = c.isInterface; @@ -308,10 +362,14 @@ class Writer buf.add(c.isInterface ? "interface " : "class "); - buf.add(properCase(c.name,true)); + buf.add(properCase(c.name, true)); + if (c.typeParams != null) { + buf.add("<" + c.typeParams + ">"); + } var parents = []; - if (null != c.extend) { + isExtends = null != c.extend; + if (isExtends) { parents.push((isInterface ? "implements " : "extends ") + tstring(c.extend)); } for (i in c.implement) @@ -320,10 +378,17 @@ class Writer buf.add(" " + parents.join(" ")); buf.add(openb()); write(buf.toString()); + writeNL(); lvl++; + var path:String = (pack.length > 0 ? pack.join(".") + "." : "") + c.name; + typer.setPackage(path.substr(0, path.lastIndexOf("."))); + typer.setImports(typeImportMap, imported); + typer.enterClass(path, c); + // process properties writeProperties(c); + writeNL(); // process fields writeFields(c); @@ -333,39 +398,92 @@ class Writer writeInits(c); lvl--; + writeNL(); write(closeb()); //close conditional compilation block if needed writeECondCompEnd(getCondComp(c.meta)); } - function writeProperties(c : ClassDef) - { - var p = []; - var h = new Map(); - var getOrCreateProperty = function(name, t, stat) + var propertyMap = new Map(); + function tryAddProperty(field:ClassField, f:Function):Void { + var isGetter:Bool = this.isGetter(field.kwds); + var isSetter:Bool = !isGetter && this.isSetter(field.kwds); + if (isGetter || isSetter) { - var property = h.get(name); + var property = propertyMap.get(field.name); if (property == null) { property = { - name : name, + name : field.name, get : "never", set : "never", - ret : t, - sta : stat, - pub : false, + ret : isGetter ? f.ret.t : f.args[0].t, + sta : isStatic(field.kwds), + internal : isInternal(field.kwds), + pub : isPublic(field.kwds), getMeta : null, setMeta : null }; - p.push(property); - h.set(name, property); - context.set(name, tstring(t, true)); + propertyMap.set(field.name, property); + } + if (isGetter) + { + property.get = "get"; + property.setMeta = field.meta; + } + if (isSetter) + { + property.set = "set"; + property.setMeta = field.meta; } - return property; } + } + + function tryWritePropertyVar(field:ClassField, c:ClassDef):Void { + if (!propertyMap.exists(field.name)) + return; + + var property = propertyMap.get(field.name); + propertyMap.remove(field.name); + //for insterface, func prototype will be removed, + //so write meta on top of properties instead + if (c.isInterface) { + writeMetaData(property.setMeta); + writeMetaData(property.getMeta); + } + + if (property.internal) + writeAllow(); + if(cfg.getterSetterStyle == "combined") + write("#if !flash "); + if (property.pub) { + write("public "); + } + else { + if (! isInterface) { + write("private "); + } + } + if (property.sta) + write("static "); + write("var " + property.name + "(" + property.get + ", " + property.set + ")"); + writeVarType(property.ret); + if(cfg.getterSetterStyle == "combined") + writeNL("; #end"); + else { + if (c.isInterface) { //if interface, newline handled be metadata + write(";"); + } + else { + writeNL(";"); + writeIndent(); + } + } + } + function writeProperties(c : ClassDef):Void { for (field in c.fields) { switch(field.kind) @@ -373,163 +491,225 @@ class Writer case FFun( f ): if (isOverride(field.kwds)) continue; - if (isGetter(field.kwds)) - { - var getterDirective : String = "get"; // haxe 3 - // haxe 2: cfg.makeGetterName(field.name); - - var property = getOrCreateProperty(field.name, f.ret.t, isStatic(field.kwds)); - property.getMeta = field.meta; - if (isPublic(field.kwds)) - { - property.get = getterDirective; - property.pub = true; - } else { - property.get = getterDirective; - } - } - else if (isSetter(field.kwds)) - { - var setterDirective : String = "set"; // haxe 3 - // haxe 2: cfg.makeSetterName(field.name); - - var property = getOrCreateProperty(field.name, f.args[0].t, isStatic(field.kwds)); - property.setMeta = field.meta; - if (isPublic(field.kwds)) - { - property.set = setterDirective; - property.pub = true; - } else { - property.set = setterDirective; - } - } + tryAddProperty(field, f); default: continue; } } + } - if (p.length > 0) { - writeNL(); + //if (p.length > 0) { + //writeNL(); + //} +// + //if(cfg.getterSetterStyle == "haxe" || cfg.getterSetterStyle == "combined") { + //for (property in p) + //{ + //writeIndent(); + ////for insterface, func prototype will be removed, + ////so write meta on top of properties instead + //if (c.isInterface) { + //writeMetaData(property.setMeta); + //writeMetaData(property.getMeta); + //} +// + //if (property.internal) + //writeAllow(); + //if(cfg.getterSetterStyle == "combined") + //write("#if !flash "); + //if (property.pub) { + //write("public "); + //} + //else { + //if (! isInterface) { + //write("private "); + //} + //} + //if (property.sta) + //write("static "); + //write("var " + property.name + "(" + property.get + ", " + property.set + ")"); + //writeVarType(property.ret); + //if(cfg.getterSetterStyle == "combined") + //writeNL("; #end"); + //else { + //if (c.isInterface) { //if interface, newline handled be metadata + //write(";"); + //} + //else { + //writeNL(";"); + //} + //} + //} + //} + //if (c.isInterface) { + //writeNL(); + //} + //} + + function writeFields(c : ClassDef):Void { + if (c.isInterface) { + for (field in c.fields) { + writeField(field, c); + } + return; } - if(cfg.getterSetterStyle == "haxe" || cfg.getterSetterStyle == "combined") { - for (property in p) - { - writeIndent(); - //for insterface, func prototype will be removed, - //so write meta on top of properties instead - if (c.isInterface) { - writeMetaData(property.setMeta); - writeMetaData(property.getMeta); - } - - if(cfg.getterSetterStyle == "combined") - write("#if !flash "); - if (property.pub) { - write("public "); - } - else { - if (! isInterface) { - write("private "); - } - } - if (property.sta) - write("static "); - write("var " + property.name + "(" + property.get + ", " + property.set + ")"); - writeVarType(property.ret); - if(cfg.getterSetterStyle == "combined") - writeNL("; #end"); - else { - if (c.isInterface) { //if interface, newline handled be metadata - write(";"); + var constructor:Function = null; + var constructorFieldInits:Array = []; + var hasConstructor:Bool = false; + var needConstructor:Bool = false; + + for (field in c.fields) { + switch (field.kind) { + case FFun ( f ): + if (!hasConstructor && field.name == c.name) { + constructor = f; + hasConstructor = true; } - else { - writeNL(";"); + case FVar(t, val): + if (val != null) { + var usingInstanceFields:Bool = false; + var rval = RebuildUtils.rebuild(val, function(e) { + switch(e) { + case EIdent(s): + if (s == "this") { + usingInstanceFields = true; + } else { + for (field in c.fields) { + if (s == field.name && s != c.name) { + if (!field.kwds.has("static")) { + usingInstanceFields = true; + return RebuildResult.RReplace(EField(EIdent("this"), s)); + } + break; + } + } + } + default: + } + return RebuildResult.RNull; + }); + if (usingInstanceFields) { + if (rval == null) rval = val; + constructorFieldInits.push(ENL(EBinop("=", EField(EIdent("this"), field.name), rval, false))); + field.kind = FVar(t, null); + } } - } - - context.set(property.name, tstring(property.ret)); + default: + } + if (!needConstructor && !field.kwds.has("static")) { + needConstructor = true; } } - if (c.isInterface) { - writeNL(); + + if (hasConstructor && constructorFieldInits.length > 0) { + switch(constructor.expr) { + case EBlock(e): + constructorFieldInits = constructorFieldInits.concat(e); + default: + constructorFieldInits.push(constructor.expr); + } + constructor.expr = EBlock(constructorFieldInits); } - } - function writeFields(c : ClassDef) - { - for (field in c.fields) + for (field in c.fields) { writeField(field, c); - if(c.isInterface) - return; + } - if(!Lambda.exists(c.fields, - function(field:ClassField) { - switch(field.kind) { - case FFun( f ): - if (field.name == c.name) - return true; - default: - } - return false; - } - )) - { - addWarning("Required constructor was added for member var initialization"); + if (needConstructor && !hasConstructor) { writeNL(); writeNL(); writeIndent(); + if (isMXInternal(c.kwds)) + write("@:ns('mx_internal')"); if (isInternal(c.kwds)) { writeAllow(); write("private "); - } - else { + } else { write("public "); } - writeConstructor({ + if (c.extend != null) { + constructorFieldInits.push(ENL(ECall(EIdent("super"), []))); + } + var f:Function = { args : [], varArgs : null, ret : null, - expr : EBlock(((null != c.extend) ? [ENL(ECall(EIdent("super"),[]))] : [])) - }, c.extend != null); + expr : EBlock(constructorFieldInits) + } + typer.enterFunction(f, c.name, c); + writeConstructor(f, c.extend != null); + typer.leaveFunction(); } } - function writeField(field : ClassField, c : ClassDef) - { + function writeField(field : ClassField, c : ClassDef):Void { var isGet : Bool = isGetter(field.kwds); var isSet : Bool = isSetter(field.kwds); var isFun : Bool = switch(field.kind) {case FFun(_): true; default: false;}; //if writing an Interface, get/set field will be added //as a property instead of func - if ((isGet || isSet) && c.isInterface) + if ((isGet || isSet) && c.isInterface) { + tryWritePropertyVar(field, c); return; + } - writeMetaData(field.meta); - - var start = function(name:String, isFlashNative:Bool=false, isConstructor=false) { - if((isGet || isSet) && cfg.getterSetterStyle == "combined") { - writeNL(isFlashNative ? "#if flash" : "#else"); - //writeNL(""); + var namespaceMetadata:Array = null; + if (isFun) { + switch(field.kind) { + case FFun(f): + typer.enterFunction(f, field.name, c); + default: + }; + } + var lookUpForNamespaces = function(e:Expr):RebuildResult { + switch(e) { + case ENamespaceAccess(e, f): + var type:String = typer.getExprType(e, true); + if (type == null) return null; + if (typeImportMap.exists(type)) { + var fullType:String = typer.getFullTypeName(type); + if (fullType != null) { + type = fullType; + } + } + if (namespaceMetadata == null) { + namespaceMetadata = [type]; + } else if (namespaceMetadata.indexOf(type) == -1) { + namespaceMetadata.push(type); + } + default: } + return null; + } - if(isFlashNative) { - if(isGet) { - write("@:getter("); - write(name); - write(") "); - } else if(isSet) { - write("@:setter("); - write(name); - write(") "); + var start = function(name:String, isFlashNative:Bool=false, isConstructor=false):Bool { + if (isGet || isSet) { + tryWritePropertyVar(field, c); + if (cfg.getterSetterStyle == "combined") { + writeNL(isFlashNative ? "#if flash" : "#else"); } - if((isGet || isSet) && isProtected(field.kwds)) { - write("@:protected "); + if (isFlashNative) { + if(isGet) { + write("@:getter("); + write(name); + write(") "); + } else if(isSet) { + write("@:setter("); + write(name); + write(") "); + } + if (isProtected(field.kwds)) { + write("@:protected "); + } } } if(isFinal(field.kwds)) write("@:final "); + if (namespaceMetadata != null) + for (m in namespaceMetadata) + write("@:access(" + m + ") "); if((isConstructor && isInternal(c.kwds)) || (!isInterface && isInternal(field.kwds))) writeAllow(); if(isOverride(field.kwds)) @@ -537,18 +717,19 @@ class Writer //coner-case, constructor of internal AS3 class is set to private in //Haxe with a meta allowing access from same package - if(isConstructor && isInternal(c.kwds)) { - write("private "); + if (isMXInternal(field.kwds)) { + write("@:ns('mx_internal') "); } - else if(isPublic(field.kwds)) { + if (isConstructor && isInternal(c.kwds)) { + write("private "); + } else if(isPublic(field.kwds)) { if(!(isGet && cfg.forcePrivateGetter) //check if forced private getter && !(isSet && cfg.forcePrivateSetter)) //check if forced private setter write("public "); else if(!isInterface) { write("private "); } - } - else if(!isInterface) { + } else if(!isInterface) { write("private "); } //check wheter the field is an AS3 constants, which can be inlined in Haxe @@ -567,43 +748,61 @@ class Writer //static non-inlined field would prevent Haxe compilation switch(val) { case EConst(c): write("inline "); + return true; default: } default: } } } + return false; } switch(field.kind) { case FVar(t, val): - start(field.name, false); - write("var " + getModifiedIdent(field.name)); - if(!isStatic(field.kwds) && isConst(field.kwds)) write("(default, never)"); + writeMetaData(field.meta); + var isInlined:Bool = start(field.name, false); + write("var " + typer.getModifiedIdent(field.name)); + if (!isInlined && isConst(field.kwds)) { + if (val != null) { + write("(default, never)"); + } else { + write("(default, null)"); // constants that depends on class fields will be initialized in constructor, so they are not actually constants + } + } var type = tstring(t); //check wether a specific type was defined for this array if(isArrayType(type)) { for (genType in this.genTypes) { if (field.name == genType.fieldName) { - t = TVector(TPath([genType.name])); + t = (TPath(["Array<" + genType.name + ">"])); } } } writeVarType(t); - context.set(field.name, tstring(t)); + + if (val == null) { + if (type == "Int") val = EConst(CInt("0")); + else if (type == "Bool") val = EIdent("false"); + } //initialise class property if(val != null) { write(" = "); lvl++; //extra indenting if init is on multiple lines - writeExpr(val); + writeETypedExpr(val, t); lvl--; } write(";"); case FFun( f ): + writeNL(); + writeMetaData(field.meta); + RebuildUtils.rebuild(f.expr, lookUpForNamespaces); if (field.name == c.name) { start("new", false, true); + typer.enterFunction(f, c.name, c); writeConstructor(f, c.extend != null); + typer.leaveFunction(); } else { var ret = f.ret; var name = if (isGetter(field.kwds)) { @@ -612,18 +811,20 @@ class Writer ret.t = f.args[0].t; cfg.makeSetterName(field.name); //"set" + ucfirst(field.name); } else { - getModifiedIdent(field.name); + typer.getModifiedIdent(field.name); } if(isGetter(field.kwds) || isSetter(field.kwds)) { // write flash native if( cfg.getterSetterStyle == "flash" || cfg.getterSetterStyle == "combined") { start(field.name, true); writeFunction(f, isGetter(field.kwds), isSetter(field.kwds), true, name, ret); + writeNL(); } // write haxe version if( cfg.getterSetterStyle == "haxe" || cfg.getterSetterStyle == "combined") { start(field.name, false); writeFunction(f, isGetter(field.kwds), isSetter(field.kwds), false, name, ret); + writeNL(); } if(cfg.getterSetterStyle == "combined") { writeNL("#end"); @@ -631,13 +832,17 @@ class Writer } } else { start(name, false); - writeFunction(f, isGetter(field.kwds), isSetter(field.kwds), false, name, ret); + writeFunction(f, isGetter(field.kwds), isSetter(field.kwds), false, name, ret, field.meta); + writeNL(); } } case FComment: - //writeComments(field.meta); - null; + writeComments(field.meta); + } + + if (isFun) { + typer.leaveFunction(); } //if this field is not wrapped in conditional compilation, @@ -678,8 +883,7 @@ class Writer * Return a new array containing all the conditional * compilation constants from the provided array */ - function getCondComp(exprs : Array) : Array - { + function getCondComp(exprs : Array) : Array { var condComps = []; for (expr in exprs) { @@ -696,8 +900,7 @@ class Writer * Return a new array containing all the conditional * compilation expressions from the provided array */ - function getECondComp(exprs : Array) : Array - { + function getECondComp(exprs : Array) : Array { var condComps = []; for (expr in exprs) { @@ -715,12 +918,10 @@ class Writer * contains a conditional compilation expr for the * condComp compilation constant */ - function hasCondComp(condComp : String, exprs : Array) : Bool - { - for (expr in exprs) - { + function hasCondComp(condComp : String, exprs : Array) : Bool { + for (expr in exprs) { switch (expr) { - case ECondComp(v,e,e2): + case ECondComp(v, e, e2): if (condComp == v) { return true; } @@ -734,22 +935,18 @@ class Writer * Write closing statement ("#end") for conditional * conpilation if any */ - function writeECondCompEnd(condComps : Array) : Void - { + function writeECondCompEnd(condComps : Array) : Void { for (i in 0...condComps.length) { - if (i == 0) { - writeNL(); - writeIndent("#end // "); - } else { - write(" && "); + writeNL(); + writeIndent("#end"); + if (cfg.verbouseConditionalCompilationEnd) { + write(" // " + condComps[i]); } - write(condComps[i]); } } - function writeArgs(args : Array<{ name : String, t : Null, val : Null, exprs : Array }>, ?varArgs:String) - { - if(varArgs != null) { + function writeArgs(args : Array<{ name : String, t : Null, val : Null, exprs : Array }>, varArgs:String = null, functionExpressions:Array) { + if(varArgs != null && !cfg.replaceVarArgsWithOptionalArguments) { var varArg = { name:varArgs, t:TPath(["Array"]), @@ -771,6 +968,19 @@ class Writer for (arg in args) //for each method argument { + if (arg.val != null) { + switch (arg.val) { + case EField(_, _), EIdent("NaN"): + arg.t = TPath(["Null<" + tstring(arg.t) + ">"]); + functionExpressions.unshift(ENL( + EIf(EBinop("==", EIdent(arg.name), EIdent("null"), false), + EBinop("=", EIdent(arg.name), arg.val, false) + ) + )); + arg.val = EIdent("null"); + default: + } + } for (expr in arg.exprs) //for each expression within that argument { switch (expr) { @@ -794,12 +1004,11 @@ class Writer write(s); } case ETypedExpr(_, t): - writeVarType(t); - context.set(arg.name, tstring(arg.t)); + writeVarType(arg.t); if(arg.val != null) { write(" = "); switch(tstring(t)) { - case "Int" if(needCastToInt(arg.val)): + case "Int" if (needCastToInt(arg.val)): switch(arg.val) { case EConst(_ => CFloat(f)): var index = f.indexOf('.'); @@ -822,27 +1031,50 @@ class Writer case ENL(_): //newline if (pendingComma) { pendingComma = false; - write(","); + write(", "); } writeNL(); writeIndent(); case ECommented(s,b,t,e): // comment among arguments - if (pendingComma) { - pendingComma = false; - write(","); - } - writeComment(s); + writeComment(s, b); default: } } } + + if (cfg.replaceVarArgsWithOptionalArguments) { + // Adding workaround for (...params:Array) + if (varArgs != null) { + var varArgsNum:Int = 7; + var argNum:Int = args.length; + for (i in 1...varArgsNum + 1) { + if (argNum++ > 0) write(", "); + write('$varArgs$i:Dynamic = null'); + } + + if (functionExpressions.length > 0) { + functionExpressions[0] = ENL(functionExpressions[0]); + } + + var callArgs:Array = []; + for (i in 1...varArgsNum + 1) { + callArgs.push(EIdent(varArgs + i)); + } + functionExpressions.unshift(ENL(EVars([{ + name:varArgs, + t:TPath(["Array"]), + val:ECall(EIdent("as3hx.Compat.makeArgs"), callArgs) + }]))); + } + } + lvl -= 2; return fst; } - function writeConstructor(f:Function, isSubClass:Bool) { + function writeConstructor(f:Function, isSubClass:Bool):Void { //add super if missing, as it is mandatory in Haxe for subclasses if (isSubClass && !constructorHasSuper(f.expr)) { switch(f.expr) { @@ -852,30 +1084,34 @@ class Writer } } write("function new("); - writeArgs(f.args, f.varArgs); - writeCloseStatement(); var es = formatBlockBody(f.expr); + writeArgs(f.args, f.varArgs, es); + writeCloseStatement(); + es = WriterUtils.moveFunctionDeclarationsToTheTop(es); + es = WriterUtils.replaceForLoopsWithWhile(es); + if (cfg.fixLocalVariableDeclarations) { + es = new VarExprFix(cfg).apply(f, es, typer); + } writeExpr(EBlock(es)); + writeNL(); } /** * Wether constructor method has a super() call */ - function constructorHasSuper(?expr : Expr) : Bool - { + function constructorHasSuper(?expr : Expr) : Bool { if (expr == null) return false; - return switch(expr) { - case ECall(EIdent("super"), _): true; - case EBlock(exprs): - for (expr in exprs) { - if (constructorHasSuper(expr)) { - return true; - } - } - false; - case ENL(expr): constructorHasSuper(expr); - default: false; + var hasSuper:Bool = false; + function rebuildHasSuper(e:Expr):RebuildResult { + switch(e) { + case EIdent("super"): + hasSuper = true; + default: + } + return null; } + RebuildUtils.rebuild(expr, rebuildHasSuper); + return hasSuper; } inline function writeEContinue():BlockEnd { @@ -890,21 +1126,36 @@ class Writer return result; } - function writeFunction(f : Function, isGetter:Bool, isSetter:Bool, isNative:Bool, ?name : Null, ?ret : FunctionRet) { + function writeFunction(f : Function, isGetter:Bool, isSetter:Bool, isNative:Bool, ?name : Null, ?ret : FunctionRet, ?meta:Array):Void { + var oldFunctionReturnType:T = functionReturnType; + functionReturnType = f.ret.t; + + // ensure the function body is in a block + var es = f.expr != null ? formatBlockBody(f.expr) : []; + write("function"); if(name != null) write(" " + name); + if (meta != null) { + for (e in meta) { + switch(e) { + case ECommented(s, b, t, e): + if (t) { + writeComment(s, b); + } + default: + } + } + } write("("); - writeArgs(f.args, f.varArgs); + writeArgs(f.args, f.varArgs, es); write(")"); // return type if (ret == null) ret = f.ret; writeFunctionReturn(ret, isGetter, isSetter, isNative); - // ensure the function body is in a block - var es = f.expr != null ? formatBlockBody(f.expr) : []; - var formatExpr:Expr->(Expr->Expr)->Expr = null; - var formatBlock:Array->(Expr->Expr)->Array = function(exprs, getResult) { + var formatExpr:Expr -> (Expr -> Expr) -> Expr = null; + var formatBlock:Array -> (Expr -> Expr) -> Array = function(exprs, getResult) { for(i in 0...exprs.length) { exprs[i] = formatExpr(exprs[i], getResult); } @@ -927,50 +1178,71 @@ class Writer default: e; } } - if(isIntType(tstring(ret.t))) { + if (isIntType(tstring(ret.t))) { formatBlock(es, function(?e) return e != null && needCastToInt(e) ? getCastToIntExpr(e) : e); } // haxe setters must return the provided type - if(isSetter && !isNative && f.args.length == 1) { + if (isSetter && !isNative && f.args.length == 1) { var result = EIdent(f.args[0].name); - formatBlock(es, function(e) return result); + formatBlock(es, function(e) { + if (e != null) { + throw "Unexpected return value: " + e + " in as3 setter"; + } + return result; + }); es.push(ENL(EReturn(result))); } - writeStartStatement(); + es = WriterUtils.moveFunctionDeclarationsToTheTop(es); + es = WriterUtils.replaceForLoopsWithWhile(es); + if (cfg.fixLocalVariableDeclarations) { + es = new VarExprFix(cfg).apply(f, es, typer); + } + if (!isInterface) + writeStartStatement(); writeExpr(EBlock(es)); + // writeNL(); + functionReturnType = oldFunctionReturnType; } /** * Write the returned typed of a function and all * comments and newline until opening bracket */ - function writeFunctionReturn(ret:FunctionRet, isGetter : Bool, isSetter : Bool, isNative : Bool) { + function writeFunctionReturn(ret:FunctionRet, isGetter : Bool, isSetter : Bool, isNative : Bool):Void { //write return type if(isNative) { if(isGetter) writeVarType(ret.t, "{}", true); if(isSetter) writeVarType(null, "Void", true); } else - writeVarType(ret.t,null,false); + writeVarType(ret.t, null, false); //write comments after return type for (expr in ret.exprs) { switch (expr) { - case ECommented(s,b,t,e): - writeComment(s); + case ECommented(s, b, t, e): + writeComment(s, b); default: } } } - inline function writeEReturn(?e:Expr) { + inline function writeInComment(p:Dynamic):Void { + write("/* " + p + " */"); + } + + inline function writeEReturn(?e:Expr):Void { write("return"); if(e == null) return; write(" "); - writeExpr(e); + writeETypedExpr(e, functionReturnType); + } + + function isArrayAccessable(etype:String):Bool { + return isArrayType(etype) || isVectorType(etype) || isOpenFlDictionaryType(etype) || isMapType(etype) || isByteArrayType(etype); } - function writeEArray(e:Expr, index:Expr) { + function writeEArray(e:Expr, index:Expr):Void { //write("/* EArray ("+Std.string(e)+","+Std.string(index)+") " + Std.string(getExprType(e, true)) + " */ "); var old = inArrayAccess; inArrayAccess = true; @@ -980,8 +1252,20 @@ class Writer if(etype == "FastXML" || etype == "FastXMLList") { writeExpr(e); inArrayAccess = old; - write(".get("); + var oldInLVA = inLvalAssign; + inLvalAssign = false; + if (oldInLVA) { + write(".set("); + } else { + write(".get("); + } writeExpr(index); + if (oldInLVA) { + write(", "); + writeExpr(rvalue); + rvalue = null; + } + inLvalAssign = oldInLVA; write(")"); } else if(isMapType(etype)) { writeExpr(e); @@ -1002,18 +1286,34 @@ class Writer inLvalAssign = oldInLVA; write(")"); } else { - //write("/*!!!" + etype + "!!!*/"); - if(isDynamicType(etype) || ((isArrayType(etype) || e.match(EIdent(_))) && itype != null && itype != "Int" && itype != "UInt")) { - if(cfg.debugInferredType) { - write("/* etype: " + etype + " itype: " + itype + " */"); - } - var isString = (itype == "String"); - var oldInLVA = inLvalAssign; - inLvalAssign = false; - if(oldInLVA && !inEField) - write("Reflect.setField("); - else - write("Reflect.field("); + //write("/*!!!" + e + ":" + etype + "!!!*/"); + var oldInLVA = inLvalAssign; + inLvalAssign = false; + //oldInLVA = false; + var isProxy:Bool = etype == "PropertyProxy" || etype == "feathers.core.PropertyProxy"; + var isRawData:Bool = false; //etype == "Object" || etype == CommonImports.ObjectType || etype == CommonImports.ObjectImport; + if (isArrayType(etype) || isVectorType(etype) || isOpenFlDictionaryType(etype) || isMapType(etype) || isByteArrayType(etype) || isProxy || isRawData) { + writeExpr(e); + inArrayAccess = old; + write("["); + if (isProxy) { + writeExpr(index); + } else if (isOpenFlDictionaryType(etype) || isMapType(etype) || isRawData) { + writeETypedExpr(index, TPath([typer.getMapIndexType(etype)])); + } else { + writeETypedExpr(index, TPath(["Int"])); + } + write("]"); + } else { + var isAnonymouse:Bool = isDynamicType(etype); + if(cfg.debugInferredType) { + write("/* etype: " + etype + " itype: " + itype + " */"); + } + var isString = (itype == "String"); + if(oldInLVA && !inEField) + write(isAnonymouse ? "Reflect.setField(" : "Reflect.setProperty("); + else + write(isAnonymouse ? "Reflect.field(" : "Reflect.getProperty("); writeExpr(e); inArrayAccess = old; write(", "); @@ -1025,49 +1325,45 @@ class Writer writeExpr(rvalue); rvalue = null; } - inLvalAssign = oldInLVA; write(")"); - } else { - writeExpr(e); - inArrayAccess = old; - write("["); - writeExpr(index); - write("]"); } + //inLvalAssign = false; + inLvalAssign = oldInLVA; } } - function writeLoop(incrs:Array, f:Void->Void) { + function writeLoop(incrs:Array, f:Void -> Void):Void { var old = loopIncrements; loopIncrements = incrs.slice(0); f(); loopIncrements = old; } - static function ucfirst(s : String) : String - { + static function ucfirst(s : String) : String { return s.substr(0, 1).toUpperCase() + s.substr(1); } - function writeVarType(?t : Null, ?alt : String, isNativeGetSet:Bool=false) - { - if (t == null) - { + function writeVarType(?t : Null, ?alt : String, isNativeGetSet:Bool = false):Void { + if (t == null) { if (alt != null) write(getColon() + alt); return; } - write(getColon() + tstring(t, isNativeGetSet)); + var s:String = tstring(t, isNativeGetSet); + if (s.indexOf("Dictionary<" + CommonImports.ObjectType) == 0) { + s = StringTools.replace(s, "Dictionary<" + CommonImports.ObjectType, "Dictionary<{}"); + } + write(getColon() + s); } - function writeInits(c : ClassDef) { - if(c.inits == null || c.inits.length == 0) + function writeInits(c : ClassDef):Void { + if (c.inits == null || c.inits.length == 0) return; writeNL(""); writeIndent(); writeNL('private static var ${c.name}_static_initializer = {'); lvl++; - for(e in c.inits) { + for (e in c.inits) { writeIndent(); writeExpr(e); writeNL(";"); @@ -1079,57 +1375,15 @@ class Writer writeNL("}"); } - function getConst(c : Const) : String - { - return switch(c) - { + function getConst(c : Const) : String { + return switch(c) { case CInt(v), CFloat(v): v; case CString(s): quote(s); } } - function getExprType(e:Expr):Null { - /*EField(ECall(EField(EIdent(xml),descendants),[]),user)*/ - switch(e) { - case ETypedExpr(e2, t): return tstring(t); - case EField(e2, f): - var t2 = getExprType(e2); - //write("/* e2 " + e2 + "."+f+" type: "+t2+" */"); - switch(t2) { - case "FastXML": - return switch(f) { - case "descendants", "nodes": "FastXMLList"; - case "node": "FastXML"; - case "length": "Int"; - case _: "FastXMLList"; - } - case "FastXMLList": - switch(f) { - case "length": return "Int"; - } - default: - } - case EIdent(s): - s = getModifiedIdent(s); - //if(context.get(s) == null) - // write("/* AS3HX WARNING var " + s + " is not in scope */"); - return context.get(s); - case EVars(vars) if(vars.length == 1): return tstring(vars[0].t); - case EArray(n, _): return getExprType(n); - case EArrayDecl(_): return "Array"; - case EUnop(_, _, e2): return getExprType(e2); - case EBinop(_ => "/", _, _, _): return "Float"; - case EBinop(_, e1, e2, _) if(getExprType(e1) != "Float" && getExprType(e2) != "Float"): return "Int"; - case EConst(c): - return switch(c) { - case CInt(_): "Int"; - case CFloat(_): "Float"; - case CString(_): "String"; - } - case ERegexp(_, _): return getRegexpType(); - default: - } - return null; + function getExprType(e:Expr, isFieldAccess:Bool = false):Null { + return typer.getExprType(e, isFieldAccess); } inline function getRegexpType():String return cfg.useCompat ? "as3hx.Compat.Regex" : "flash.utils.RegExp"; @@ -1147,34 +1401,9 @@ class Writer } } - function typeExpr(e:Expr) : String { - return switch(e) { - case EIdent(s): context.get(s); - default: null; - } - } - - function getModifiedIdent(s : String) : String { - return switch(s) { - case "int": "Int"; - case "uint": cfg.uintToInt ? "Int" : "UInt"; - case "Number": "Float"; - case "Boolean": "Bool"; - case "Function": cfg.functionToDynamic ? "Dynamic" : s; - case "Object": "Dynamic"; - case "undefined": "null"; - //case "Error": cfg.mapFlClasses ? "flash.errors.Error" : s; - case "XML": "FastXML"; - case "XMLList": "FastXMLList"; - case "NaN":"Math.NaN"; - case "Dictionary": cfg.dictionaryToHash ? "haxe.ds.ObjectMap" : s; - //case "QName": cfg.mapFlClasses ? "flash.utils.QName" : s; - default: s; - }; - } - - function writeModifiedIdent(s : String) { - write(getModifiedIdent(s)); + function writeModifiedIdent(s : String):Void { + s = typer.getModifiedIdent(s); + write(s); } /** @@ -1182,11 +1411,11 @@ class Writer * @return if the block requires a terminating ; */ function writeExpr(?expr : Expr) : BlockEnd { - if(cfg.debugExpr) write(" /* " + Std.string(expr) + " */ "); - if(expr == null) return None; + if (cfg.debugExpr) write(" /* " + Std.string(expr) + " */ "); + if (expr == null) return None; var rv = Semi; - switch(expr) { - case ETypedExpr(e, t): rv = writeExpr(e); + switch (expr) { + case ETypedExpr(e, t): rv = writeETypedExpr(e, t); case EConst(c): write(getConst(c)); case EIdent(v): writeModifiedIdent(v); case EVars(vars): rv = writeEVars(vars); @@ -1195,6 +1424,9 @@ class Writer case EField(e, f): rv = writeEField(expr, e, f); case EBinop(op, e1, e2, newLineAfterOp): rv = writeEBinop(op, e1, e2, newLineAfterOp); case EUnop(op, prefix, e): rv = writeEUnop(op, prefix, e); + case ECall(EIdent('super'), params): + if (isExtends) rv = writeECall(expr, EIdent('super'), params); + else return None; case ECall(e, params): rv = writeECall(expr, e, params); case EIf(cond, e1, e2): rv = writeEIf(cond, e1, e2); case ETernary(cond, e1, e2): writeETernarny(cond, e1, e2); @@ -1204,7 +1436,10 @@ class Writer case EForIn(ev, e, block): rv = writeEForIn(ev, e, block); case EBreak(label): write("break"); case EContinue: rv = writeEContinue(); - case EFunction(f, name): writeFunction(f, false, false, false, name); + case EFunction(f, name): + typer.enterFunction(f, name); + writeFunction(f, false, false, false, name); + typer.leaveFunction(); case EReturn(e): writeEReturn(e); case EArray(e, index): writeEArray(e, index); case EArrayDecl(e): @@ -1228,34 +1463,84 @@ class Writer case ENew(t, params): writeENew(t, params); case EThrow( e ): write("throw "); - writeExpr(e); + function joinToString(params:Array):Expr { + var r = params[0]; + for (i in 1...params.length) { + r = EBinop("+", r, params[i], false); + if (i < params.length - 1) { + r = EBinop("+", r, EConst(CString(" ")), false); + } + } + return r; + } + // switch (e) { + // case ENew(t, params): + // //var c = params.copy(); + // //c.unshift(EConst(CString(tstring(t) + ": "))); + // var args:Array = [EConst(CString(tstring(t)))]; + // if (params.length > 0) { + // args.push(joinToString(params)); + // } + // writeExpr(ENew(TPath(["XError"]), args)); + // case ECall(e1, params): + // switch(e1) { + // case EIdent(s): + // var args:Array = [EConst(CString(s))]; + // if (params.length > 0) { + // args.push(joinToString(params)); + // } + // writeExpr(ENew(TPath(["XError"]), args)); + // default: + // writeExpr(e); + // } + // default: + writeExpr(e); + // } + //switch (e) { + //case ENew(t, params): + //var c = params.copy(); + //c.unshift(EConst(CString(tstring(t) + ": "))); + //writeExpr(joinToString(c)); + //case ECall(e1, params): + //switch(e1) { + //case EIdent(s): + //var c = params.copy(); + //c.unshift(EConst(CString(s + ": "))); + //writeExpr(joinToString(c)); + //default: + //writeExpr(e); + //} + //default: + //writeExpr(e); + //} case ETry(e, catches): rv = writeETry(e, catches); case EObject(fl): if (fl.empty()) { - write("{ }"); + write("{}"); } else { writeNL("{"); lvl++; var length = fl.length; for (i in 0...length) { var field = fl[i]; - writeIndent(field.name + (cfg.spacesOnTypeColon ? " : " : ": ")); + if (i > 0) writeNL(", "); + var field = fl[i]; + writeIndent(prepareObjectFieldName(field.name) + (cfg.spacesOnTypeColon ? " : " : ": ")); writeExpr(field.e); - if(i < length - 1) writeNL(i > 0 || fl.length > 1 ? "," : ""); } lvl--; writeNL(); writeIndent("}"); } - case ERegexp(str, opts): write('new ${getExprType(expr)}(' + eregQuote(str) + ', "' + opts + '")'); + case ERegexp(str, opts): write('new ${getExprType(expr)}(' + eregQuote(str) + ", '" + opts + "')"); + case ENamespaceAccess(e, f): writeExpr(e); case ESwitch( e, cases, def): var newCases : Array = new Array(); var writeTestVar = false; var testVar = switch(e) { case EParent(ex): switch(ex) { - case EIdent(i): ex; - case ECall(_): ex; + case EIdent(_), ECall(_), EField(_), EArray(_, _): ex; default: null; } default: @@ -1301,12 +1586,10 @@ class Writer writeExpr(testVar); write(" = "); writeFinish(writeExpr(e)); - writeIndent(""); } //start the switch on a new line if (lineIsDirty) { - writeNL(); writeNL(); writeIndent(); } @@ -1327,27 +1610,29 @@ class Writer writeMetaData(c.meta); //write commnent and newline before "case" write("case "); - for(i in 0...c.vals.length) { - write(i>0 ? ", " : ""); + for (i in 0...c.vals.length) { + write(i > 0 ? ", " : ""); writeExpr(c.vals[i]); } write(":"); - //prevent switch case indenting if begins - //with block expr - var didIndent = if (shouldIndentCase(c.el)) { - lvl++; - true; - } else { - false; + // consume and ignore block {} in switch + function writeCase(e:Expr):Void { + switch(e) { + case ENL(e): writeCase(e); + case EBlock(exprs): + for (inBlockExpr in exprs) { + writeFinish(writeExpr(inBlockExpr)); + } + default: + writeFinish(writeExpr(ENL(e))); + } } - - for (i in 0...c.el.length) - { - writeFinish(writeExpr(c.el[i])); + lvl++; + for (e in c.el) { + writeCase(e); } - if (didIndent) - lvl--; + lvl--; } if(def != null) { writeSwitchDefault(def); @@ -1360,8 +1645,14 @@ class Writer // Vector. call // _buffers = Vector.([inst1,inst2]); // t is TPath([inst1,inst2]), which should have been handled in ECall - write("Array/*Vector. call?*/"); - addWarning("Vector.", true); + if (cfg.useOpenFlTypes && !cfg.vectorToArray) { + write("Vector<"); + write(tstring(t, false, false)); + write(">"); + } else { + write("Array/*Vector. call?*/"); + addWarning("Vector.", true); + } case EE4XAttr( e1, e2 ): // e1.@e2 writeExpr(e1); @@ -1381,7 +1672,12 @@ class Writer addWarning("EE4X"); case EE4XFilter( e1, e2 ): // e1.(weight > 300) search - writeE4XFilterExpr(e1, e2); + try { + writeE4XFilterExpr(e1, e2); + } catch (e:String) { + writeComment("//" + e, true); + addWarning(e, true); + } case EE4XDescend( e1, e2 ): //write("/* " + e2 + " */"); writeExpr(e1); @@ -1402,19 +1698,19 @@ class Writer case ECommented(s, b, t, ex): rv = writeECommented(s,b,t,ex); case EMeta(m): if (!cfg.convertFlexunit || !writeMunitMetadata(m)) { - write("@:meta("+m.name+"("); + write("@:meta(" + m.name + "("); var first = true; for(arg in m.args) { if(!first) - write(","); + write(", "); first = false; if(arg.name != null) - write(arg.name + "="); + write(arg.name + " = "); else - write("name="); + write("name = "); writeExpr(arg.val); } - writeNL("))"); + write("))"); } case ETypeof(e): switch(e) { @@ -1441,7 +1737,7 @@ class Writer switch(e) { case EBlock(elist): for (ex in elist) { - writeFinish(writeExpr(ex)); + writeBlockLine(ex); } case ENL(e): writeECondComp(e); @@ -1452,17 +1748,46 @@ class Writer } } - write("#if " + kwd); - writeECondComp(e); - writeNL(); - if (e2 != null) { - writeIndent("#else"); - writeECondComp(e2); - writeNL(); + if (e == null) { + // compile time constant + if (cfg.conditionalCompilationConstantsClass != null && cfg.conditionalCompilationConstantsClass.length > 0) { + writeExpr(EField(EIdent(cfg.conditionalCompilationConstantsClass), kwd)); + } else { + write(kwd); + } + } else { + // conditional compilation block + write("#if " + kwd); + var oneLiner:Bool = isOneLiner(e, true); + if (oneLiner) { + write(" "); + } + writeECondComp(e); + if (oneLiner) { + write(" "); + } else { + writeNL(); + writeIndent(); + } + if (e2 != null) { + write("#else"); + oneLiner = isOneLiner(e2, true); + if (oneLiner) { + write(" "); + } + writeECondComp(e2); + if (oneLiner) { + write(" "); + } else { + writeNL(); + writeIndent(); + } + } + write("#end"); + if (cfg.verbouseConditionalCompilationEnd) { + write(" // " + kwd); + } } - writeIndent("#end // " + kwd); - writeNL(); - writeIndent(); rv = Ret; case ENL(e): @@ -1476,13 +1801,13 @@ class Writer return rv; } - function writeSwitchDefault(def:SwitchDefault) { + function writeSwitchDefault(def:SwitchDefault):Void { if(def.vals != null && def.vals.length > 0) { writeNL(); writeIndent(); write("/* covers case "); for (i in 0 ... def.vals.length) { - write(i>0 ? ", " : ""); + write(i > 0 ? ", " : ""); writeExpr(def.vals[i]); } write(":"); @@ -1502,7 +1827,7 @@ class Writer } } writeMetaData(newMeta); //write comment and newline before "default" - write("default:"); + write("case _:"); lvl++; for (i in 0...def.el.length) { @@ -1514,15 +1839,17 @@ class Writer function writeEBlock(e:Array):BlockEnd { var result = Semi; if(!isInterface) { - openContext(); write("{"); lvl++; for (ex in e) { - writeFinish(writeExpr(ex)); + writeBlockLine(ex); + //writeFinish(writeETypedExpr(ex, TPath([null]))); } lvl--; - write(closeb()); - closeContext(); + if (e.length > 0) + write(closeb()); + else + write("}"); result = None; } else { write(";"); @@ -1531,6 +1858,52 @@ class Writer return result; } + inline function fixBlockLine(e:Expr):Expr { + switch(e) { + case null: + return null; + case ENL(e): + var fix = fixBlockLine(e); + if (fix != null) { + return ENL(fix); + } else { + return null; + } + case ECommented(s, isBlock, isTail, e): + var fix = fixBlockLine(e); + if (fix != null) { + return ECommented(s, isBlock, isTail, fix); + } else { + return null; + } + case EBinop("&&", e1, e2, _): + return EIf(e1, e2); + case EBinop("||", e1, e2, _): + return EIf(EUnop("!", true, e1), e2); + default: + return null; + } + } + function writeBlockLine(e:Expr):Void { + if (e != null) { + switch (e) { + case EConst(_): return; + case EIdent(_): return; + case ENL(e): + if (e != null) switch (e) { + case EConst(_): return; + case EIdent(_): return; + case _: + } + case _: + } + var fix = fixBlockLine(e); + writeFinish(writeExpr(fix == null ? e : fix)); + } + } + + private var restrictedFieldArrayAccessConversion:Array = ["length", "split", "indexOf"]; + function writeEField(fullExpr:Expr, e:Expr, f:String):BlockEnd { var n = checkE4XDescendants(fullExpr); if(n != null) return writeExpr(n); @@ -1538,18 +1911,36 @@ class Writer var t2 = getExprType(e); //write("/* EField ("+Std.string(e)+","+Std.string(f)+") " +t1 + ":"+t2+ " */\n"); var old = inArrayAccess; - if(t1 == "FastXMLList" || (t1 == null && t2 == "FastXML")) { + if (isDynamicType(t2) && restrictedFieldArrayAccessConversion.indexOf(f) == -1) { + writeEArray(e, EConst(CString(f))); + } else if (t2 == "FastXMLList" && t1 == "FastXMLList") { + writeExpr(ECall(EField(e, "descendants"), [EConst(CString(f))])); + } else if(t1 == "FastXMLList" || (t1 == null && t2 == "FastXML")) { //write("/* t1 : " + t1 + " */"); writeExpr(e); if(inArrayAccess) write(".nodes." + f); else - write(".node." + f + ".innerData"); - } else if(t1 == "FastXML" || (t1 == null && t2 == "FastXMLList")) { - writeExpr(e); - write(".node"); - write("." + f + ".innerData"); + //write(".node." + f + ".innerData"); + write(".node." + f); } else { + if (t2 == "Date") { + switch(f) { + case "time" : return writeExpr(ECall(EField(e, "getTime"), [])); + case "fullYear" : return writeExpr(ECall(EField(e, "getFullYear"), [])); + case "month" : return writeExpr(ECall(EField(e, "getMonth"), [])); + case "day" : return writeExpr(ECall(EField(e, "getDay"), [])); + case "date" : return writeExpr(ECall(EField(e, "getDate"), [])); + case "hours" : return writeExpr(ECall(EField(e, "getHours"), [])); + case "hoursUTC" : return writeExpr(ECall(EField(e, "getHours/*UTC*/"), [])); + case "minutes" : return writeExpr(ECall(EField(e, "getMinutes"), [])); + case "seconds" : return writeExpr(ECall(EField(e, "getSeconds"), [])); + case "milliseconds": return writeExpr(EParent(EBinop("%", ECall(EField(e, "getTime"), []), EConst(CInt("1000")), false))); + case "timezoneOffset": return writeExpr(getCompatCallExpr("getTimezoneOffset", [])); + case "getTimezoneOffset": return writeExpr(getCompatFieldExpr("getTimezoneOffset")); + default: + } + } inEField = true; switch(e) { case EField(e2, f2): @@ -1557,7 +1948,7 @@ class Writer if(getExprType(e2) == "FastXML") inArrayAccess = true; case EIdent(v): - switch(getModifiedIdent(v)) { + switch(typer.getModifiedIdent(v)) { case "Int": if(f == "MAX_VALUE") { writeExpr(getCompatFieldExpr("INT_MAX")); @@ -1581,9 +1972,16 @@ class Writer return None; } default: - if(f == "length" && isFunctionExpr(e)) { - writeExpr(getCompatCallExpr("getFunctionLength", [e])); - return None; + if (f == "length") { + var type:String = getExprType(e); + if (type == "Function" || type == "haxe.Constraints.Function") { + // write("1 /*# of arguments of " + v + "*/"); + write('as3hx.Compat.getFunctionLength(' + v + ')'); + return None; + } else if (type != null && isFunctionType(type)) { + writeExpr(getCompatCallExpr("getFunctionLength", [e])); + return None; + } } } case ECall(e, p): @@ -1612,10 +2010,12 @@ class Writer writeIndent(); } var v = vars[i]; + var rExpr = null; var rvalue = v.val; if(rvalue != null) { switch(rvalue) { - case ETypedExpr(e,_): + case ETypedExpr(e, _): + rExpr = e; switch(e) { case EBinop("||=", e1,_,_): writeExpr(e); @@ -1627,28 +2027,21 @@ class Writer default: } } - var type = tstring(v.t); - context.set(v.name, type); - write("var " + getModifiedIdent(v.name)); - writeVarType(v.t); - if(rvalue != null) { - write(" = "); - if(isIntType(type)) { - switch(rvalue) { - case ETypedExpr(e, t) if(needCastToInt(e)): - rvalue = switch(e) { - case EBinop(op, e1, e2, newLineAfterOp) if(isBitwiceOp(op)): - if(needCastToInt(e1)) e1 = getCastToIntExpr(e1); - if(needCastToInt(e2)) e2 = getCastToIntExpr(e2); - EBinop(op, e1, e2, newLineAfterOp); - case EUnop(op, prefix, ex): - if(isBitwiceOp(op) && needCastToInt(ex)) EUnop(op, prefix, getCastToIntExpr(ex)); - else e; - default: getCastToIntExpr(e); - } - default: + var vt = v.t; + var type = tstring(vt); + write("var " + typer.getModifiedIdent(v.name)); + if (type == "Array") { + var fieldType:String = typer.getExprType(EIdent(v.name)); + if (fieldType != type) { + vt = TPath([typer.shortenStringType(fieldType)]); + if (rExpr != null) { + rvalue = ETypedExpr(rExpr, TPath([fieldType])); } } + } + writeVarType(vt); + if(rvalue != null) { + write(" = "); writeExpr(rvalue); if(i == vars.length - 1) { switch(rvalue) { @@ -1661,7 +2054,7 @@ class Writer return result; } - inline function writeEParent(e:Expr) { + inline function writeEParent(e:Expr):Void { switch(e) { case EParent(e): writeExpr(e); default: @@ -1674,7 +2067,11 @@ class Writer function writeECall(fullExpr:Expr, expr:Expr, params:Array):BlockEnd { switch(expr) { case EField(expr, f): - if (f == "push" && params.length > 1 && isArrayExpr(expr)) { + if ((f == "push" || f == "unshift") && params.length > 1 && (isArrayExpr(expr) || isVectorExpr(expr))) { + if (f == "unshift") { + params = params.copy(); + params.reverse(); + } for(it in params) { writeExpr(ECall(EField(expr, f), [it])); write(";"); @@ -1692,17 +2089,8 @@ class Writer case ECall(e2, params2): expr = e2; params = params2; - case EArray(_,_): return writeExpr(eCall); - case ECommented(s, b, t, e2): - //This is a hack for the AS3 unit test to - //Haxe unit test conversion. In some cases, - //the first param of the test if converted - //to an end-of-line comment - writeExpr(e2); - write(";"); - write(" // "+ s); - return None; default: + return writeExpr(eCall); } } var f:Expr->Expr = null; @@ -1721,26 +2109,67 @@ class Writer switch(expr) { case EIdent(n): var c = n.charCodeAt(0); - if(n.indexOf(".") == -1 && (c>=65 && c<=90)) { + if(n.indexOf(".") == -1 && (c>=65 && c<=90) || c == 103) { handled = true; switch(n) { + case "Object": writeExpr(params[0]); case "Number": writeCastToFloat(params[0]); - case "String": writeToString(params[0]); + case "String": + if (getExprType(params[0]) == "String") { + writeExpr(params[0]); + } else { + writeExpr(getToStringExpr(params[0])); + } case "Boolean": - write("cast("); - writeExpr(params[0]); - write(", "); - write("Bool)"); + if (getExprType(params[0]) == "Bool") { + writeEParent(params[0]); + } else { + write("AS3.as("); + writeExpr(params[0]); + write(", "); + write("Bool)"); + } case "XML": var type = tstring(TPath(["XML"])); - write('$type.parse('); + var ts:String = getExprType(params[0]); + if (ts != null && ts.indexOf("ByteArray") != -1) { + write('$type.parseByteArray('); + } else { + write('$type.parse('); + } writeExpr(params[0]); write(")"); + case "getQualifiedClassName": + var t:String = getExprType(params[0]); + switch (params[0]) { + case EIdent(v) if (v == t): + writeExpr(ECall(EField(EIdent("Type"), "getClassName"), params)); + case _: + if (isClassType(t)) { + writeExpr(ECall(EField(EIdent("Type"), "getClassName"), params)); + } else if (t == "Dynamic" || t == null) { + writeExpr(ECall(EField(EIdent("as3hx.Compat"), "getQualifiedClassName"), params)); + } else { // regular type + writeExpr(ECall(EField(EIdent("Type"), "getClassName"), [ECall(EField(EIdent("Type"), "getClass"), params)])); + } + //ECall(EField(EIdent("Type"), "getClassName"), [ECall(EField(EIdent("Type"), "getSuperClass"), [e])]); + } + case "getQualifiedSuperclassName": + var t:String = getExprType(params[0]); + if (isClassType(t)) { + writeExpr(ECall(EField(EIdent("Type"), "getClassName"), [ECall(EField(EIdent("Type"), "getSuperClass"), params)])); + } else if (t == "Dynamic" || t == null) { + writeExpr(ECall(EField(EIdent("as3hx.Compat"), "getQualifiedSuperclassName"), params)); + } else { // regular type + writeExpr(ECall(EField(EIdent("Type"), "getClassName"), [ECall(EField(EIdent("Type"), "getSuperClass"), [ECall(EField(EIdent("Type"), "getClass"), params)])])); + } + //case "": // Classes that needs to be converted to casts + //write("cast(("); + //writeExpr(params[0]); + //write("), "); + //write(n + ")"); default: - write("cast(("); - writeExpr(params[0]); - write("), "); - write(n + ")"); + handled = false; } } // other cases that come up as ECall @@ -1756,60 +2185,98 @@ class Writer write(")"); handled = true; case "int" | "uint": - writeCastToInt(params[0]); + var t:String = getExprType(params[0]); + if (t == "Int" || t == "UInt") { + writeExpr(params[0]); + } else { + writeExpr(getCastToIntExpr(params[0])); + } + //writeCastToInt(params[0]); handled = true; } case EVector(t): handled = true; if(cfg.vectorToArray) { writeExpr(params[0]); - } else { - write("Vector.ofArray(cast "); - writeExpr(params[0]); - write(")"); + } else if (cfg.useOpenFlTypes) { + var needCast:Bool = true; + switch(params[0]) { + case EArrayDecl(es): + if (es.length == 0) { + write("new Vector<" + tstring(t) + ">()"); + needCast = false; + } + default: + } + if (needCast) { + write(cfg.arrayTypePath + ".ofArray("); + writeExpr(params[0]); + write(")"); + } } default: } } - if(!handled) { - //return wether the param at the index is the last - //method call argument - var isLastArgument:Array->Int->Bool = function(params, index) { - //return wether the expression is a method call - //argument, which excludes comments and newlines - var isArgument : Expr->Bool = null; - isArgument = function(fullExpr) { - return switch(fullExpr) { - case ECommented(s,b,t,expr): isArgument(expr); - case ENL(expr): isArgument(expr); - default: true; + if (!handled) { + switch(expr) { + case EIdent(n): + var globalFunction:String = typer.isExistingIdent(n) ? null : typer.hasGlobalFunction(n); + if (globalFunction != null) { + //write(properCase(globalFunction, true) + "."); + write(n.charAt(0).toUpperCase() + n.substr(1) + "."); + } else if (!typer.isExistingIdent(n)) { + var type:String = typer.getExprType(expr); + var className:String = type.substring(type.lastIndexOf(".") + 1); + if (type != "Function" && type != "haxe.Constraints.Function" && type.indexOf("->") == -1 && (className.charAt(0) == className.charAt(0).toUpperCase())) { + writeEBinop("as", params[0], expr, false); + handled = true; + } } + default: + } + } + if (!handled) { + var isArgument : Expr->Bool = null; + isArgument = function(fullExpr) { + return switch(fullExpr) { + case ECommented(s,b,t,expr): isArgument(expr); + case ENL(expr): isArgument(expr); + default: true; } - - //check all remaining parameters - var i = index; - while(i < params.length - 1) { - if(isArgument(params[i])) - return false; - i++; - } - return true; } - writeExpr(expr); + var tstring:String = typer.getExprType(expr); + var types:Array = tstring == null ? [] : tstring.split("->"); + + switch(expr) { + case EField(e, f) if(isDynamicType(getExprType(e))): + writeExpr(e); + write("." + f); + default: + writeExpr(expr); + } write("("); var enl = false; + var hadArguments:Bool = false; for(i in 0...params.length) { - var isNotLastArgument = !isLastArgument(params, i); var param = params[i]; - switch(param) { - case ECommented(s, b, t, expr): - var delimiter = t && isNotLastArgument ? ", " : null; - writeECommented(s, b, t, expr, delimiter); - default: + var hasType:Bool = i < types.length - 1; + if (isArgument(params[i])) { + if (hadArguments) { + write(", "); + } + hadArguments = true; + } + switch(param) { + case EVector(t): + param = EIdent("Vector"); + default: + } + if (hasType && param != null) { + writeETypedExpr(param, TPath([types[i]])); + } else { writeExpr(param); - if(isNotLastArgument) write(", "); - } + } if(!enl && (param.match(ECommented(_,false,true,_)) || param.match(ENL(_)))) enl = true; } if(enl) { @@ -1830,8 +2297,8 @@ class Writer write("if ("); lvl++; //extra indenting if condition on multiple lines var rb = rebuildIfExpr(cond); - if(rb != null) { - var f:Expr->Expr = null; + if (rb != null) { + var f:Expr -> Expr = null; f = function(e) return switch(e) { case EParent(e): f(e); default: e; @@ -1863,47 +2330,36 @@ class Writer e1 = EBlock(formatBlockBody(e1)); writeExpr(e1); if (e2 != null) { - //corner case : comment located - //before the "else" keyword in the - //source file. - //As to be called recursively, in - //case of multiple one-line comment - //before the "else" - var f:Expr->Expr = null; - f = function(e2) { - return switch (e2) { - case ECommented(s,b,t,e): - writeNL(); - writeIndent(s); - f(e); //skip the comment - default: e2; - } - } - e2 = f(e2); e2 = EBlock(formatBlockBody(e2)); - writeNL(); var elseif:Expr = null; // if we find an EBlock([ENL(EIf(...))]) // after an `else` then we have an // `else if` statement - switch(e2) { + var haveExtra:Bool = false; + switch (e2) { case EBlock(e3): if (e3 != null && e3.length == 1) { - switch(e3[0]) { - case ENL(e4): - switch(e4) { - case EIf(_, _, _): - // found single if statement after an else - // replace parent `block` + `new line` with - // the `if` statement instead so we stay on - // the same line as the `else` -> `else if` - elseif = e4; - case EBlock(_): - // catch double-nested blocks and replace - // outer block with inner block - e2 = e4; - default: - } + var e4 = ParserUtils.removeNewLineExpr(e3[0]); + var extraExpr:Expr = extractComments(e3[0]); + if (extraExpr != null) switch (extraExpr) { + case ENL(ECommented(s, isBlock, isTail, e)): + haveExtra = true; + writeNL(s); + case _: + } + // writeExpr(extraExpr); + + switch (e4) { + case EIf(_, _, _): + // found single if statement after an else + // replace parent `block` + `new line` with + // the `if` statement instead so we stay on + // the same line as the `else` -> `else if` + elseif = e4; + case EBlock(_): + // catch double-nested blocks and replace + // outer block with inner block + e2 = e4; default: } } @@ -1911,11 +2367,18 @@ class Writer elseif = e2; default: } + //writeNL(); if (elseif != null) { - writeIndent("else "); + if (haveExtra) + writeIndent("else "); + else + write(" else "); result = writeExpr(elseif); } else { - writeIndent("else"); + if (haveExtra) + writeIndent("else"); + else + write(" else"); writeStartStatement(); result = writeExpr(e2); } @@ -1925,19 +2388,39 @@ class Writer return result; } - inline function writeETernarny(cond:Expr, e1:Expr, ?e2:Expr) { + inline function writeETernarny(cond:Expr, e1:Expr, ?e2:Expr):BlockEnd { write("("); var rb = rebuildIfExpr(cond); if(rb != null) writeExpr(rb); else writeExpr(cond); write(") ? "); writeExpr(e1); - write(getColon()); - writeExpr(e2); + write(" : "); + switch (e2) { + case null: + case EIdent("null"): + default: + if (getExprType(e1) == "String" && getExprType(e2) != "String") { + e2 = getToStringExpr(e2); + } + } + return writeExpr(e2); + } + + function getVarName(e:Expr):String { + switch(e) { + case EIdent(v): return v; + case EVars(vars) if (vars.length > 0): return vars[0].name; + default: return null; + } } inline function writeEWhile(cond:Expr, e:Expr, doWhile:Bool):BlockEnd { var result:BlockEnd; + var rcond:Expr = rebuildIfExpr(cond); + if (rcond != null) { + cond = rcond; + } if (doWhile) { write("do"); writeStartStatement(); @@ -1956,264 +2439,356 @@ class Writer } inline function writeEFor(inits:Array, conds:Array, incrs:Array, e:Expr):BlockEnd { - //Sys.println('inits: ${inits}; conds: ${conds}; incrs: ${incrs}'); - openContext(); - var useWhileLoop:Void->Bool = function() { - if (inits.empty() || conds.empty()) return true; - switch(inits[0]) { - case EVars(vars) if(vars.length > 1): return true; - case EIdent(v): return true; - default: - } - if (conds[0].match(EBinop("&&" | "||", _, _, _))) return true; - //index must be incremented by 1 - if (incrs.length == 1) { - return switch(incrs[0]) { - case EUnop(op, _, _): op != "++"; - case EBinop(openb,_,_,_): true; - default: false; - } - } - return true; + // Sys.println('inits: ${inits}; conds: ${conds}; incrs: ${incrs}'); + for (i in 0...inits.length - 1) { + var e:Expr = inits[i]; + writeExpr(ENL(e)); } - var isWhileLoop = useWhileLoop(); - if (!isWhileLoop) { - write("for ("); - switch(inits[0]) { - case EVars(v): - write(v[0].name); + write("for ("); + switch(inits[inits.length - 1]) { + case EVars(v): + write(v[0].name); + write(" in "); + writeExpr(v[0].val); + write("..."); + // var i:int = 0; + // for (i = 0; i < size; i++) + case EBinop(op, e1, e2, newLineAfterOp): + if (op == "=") { + switch (e1) { + case EIdent(v): + write(v); + default: + } write(" in "); - writeExpr(v[0].val); + writeExpr(e2); write("..."); - // var i:int = 0; - // for (i = 0; i < size; i++) - case EBinop(op, e1, e2, newLineAfterOp): - if (op == "=") { - switch (e1) { - case EIdent(v): write(v); - default: + } + default: + } + switch(conds[0]) { + case EBinop(op, e1, e2, nl): + switch(op) { + case ">", ">=": + var t:Expr = e1; + e1 = e2; + e2 = t; + default: + } + switch(op) { + //corner case, for "<=" binop, limit value should be incremented + case "<=", ">=": + switch(e2) { + case EConst(CInt(v)): + //increment int constants + var e = EConst(CInt(Std.string(Std.parseInt(v) + 1))); + writeExpr(e); + case _: + //when var used (like <= array.length), no choice but + //to append "+1" + writeExpr(e2); + write(" + 1"); } - write(" in "); - writeExpr(e2); - write("..."); - } - default: - } - switch(conds[0]) { - case EBinop(op, e1, e2, nl): - switch(op) { - //corne case, for "<=" binop, limit value should be incremented - case "<=": - switch(e2) { - case EConst(CInt(v)): - //increment int constants - var e = EConst(CInt(Std.string(Std.parseInt(v) + 1))); - writeExpr(e2); - case _: - //when var used (like <= array.length), no choice but - //to append "+1" - writeExpr(e2); - write(" + 1"); - } - case _: writeExpr(e2); - } - writeCloseStatement(); - default: - } - } else { - inits = inits.filter(function(it) return !it.match(EIdent(_))); - for(init in inits) { - writeExpr(init); - writeNL(";"); - } - if(!inits.empty()) writeIndent(); - write("while ("); - if (conds.empty()) { - write("true"); - } else { - for (i in 0...conds.length) { - if (i > 0) - write(" && "); - writeExpr(conds[i]); + case _: writeExpr(e2); } - } - writeCloseStatement(); + writeCloseStatement(); + default: } var es = formatBlockBody(e); - //don't write increments for a "for" loop - if (isWhileLoop) { - for (incr in incrs) { - es.push(ENL(incr)); + writeLoop([], function() { writeExpr(EBlock(es)); }); + return None; + } + + function getForEachType(type:String):String { + if (isArrayType(type) || isVectorType(type)) { + return Typer.getVectorParam(type); + } else if (type == "FastXML" || type == "FastXMLList") { + return type; + } else if (isMapType(type) || isOpenFlDictionaryType(type)) { + type = Typer.getMapParam(type, 1); + if (type == null) { + return "Dynamic"; + } else { + return type; } + } else { + return "String"; } - writeLoop(isWhileLoop ? incrs : [], function() { writeExpr(EBlock(es)); }); - closeContext(); - return None; } inline function writeEForEach(ev:Expr, e:Expr, block:Expr):BlockEnd { - openContext(); - var varName = null; - write("for ("); - switch(ev) { - case EVars(vars): - if(vars.length == 1 && vars[0].val == null) { - write(vars[0].name); - varName = vars[0].name; - } else { - writeExpr(ev); - } - case EIdent(i): - varName = i; - writeExpr(ev); - default: - write("/* AS3HX ERROR unhandled " + ev + " */"); - writeExpr(ev); - } var t = getExprType(e); - var regexpMap:EReg = ~/^Map<([^,]*, *)?(.*)>$/; - var regexpArray:EReg = ~/^Array<(.*)>$/; - if(varName == null) { - write("/* AS3HX ERROR varName is null in expression " + e); - } else if(t == "FastXML" || t == "FastXMLList") { - context.set(varName, t); - } else if (t != null && regexpMap.match(t)) { - if (cfg.debugInferredType) { - write("/* inferred type: " + regexpMap.matched(1) + " */" ); - } - context.set(varName, regexpMap.matched(1)); - } else if (t != null && regexpArray.match(t)) { - if (cfg.debugInferredType) { - write("/* inferred type: " + regexpArray.matched(1) + " */" ); - } - context.set(varName, regexpArray.matched(1)); + + var isMap:Bool = isMapType(t); + //var isArray:Bool = isArrayType(t) || isVectorType(t); + //var isXml:Bool = t == "FastXML" || t == "FastXMLList"; + var isDictionary:Bool = isOpenFlDictionaryType(t); + var isObject:Bool = t == "Object" || t == CommonImports.ObjectType || t == CommonImports.ObjectImport || t == null || t == "Dynamic"; + var eValueType:String = getForEachType(t); + + write("for ("); + var castLoopVariableType:String = null; + var varName:String = getVarName(ev); + if (varName != null) { + write(varName); + var varType:String = getExprType(ev); + if (varType != eValueType && (eValueType == "Dynamic" || typer.doImplements(varType, eValueType))) { + // as we are supposing that this was a valid as3 code, we are trying to cast values to as3-defined type + castLoopVariableType = varType; + write("_"); + } } else { - write("/* AS3HX WARNING could not determine type for var: " + varName + " exp: " + e + " type: " + t + " */"); + write("/* AS3HX ERROR unhandled " + e + " */"); + writeExpr(e); } write(" in "); var old = inArrayAccess; inArrayAccess = true; - writeExpr(e); + if (isDictionary) { + writeExpr(ECall(EField(e, "each"), [])); + } else if (isObject) { + write("as3hx.Compat.each("); + writeExpr(e); + write(")"); + } else { + writeExpr(e); + } inArrayAccess = old; writeCloseStatement(); - var result = writeExpr(EBlock(formatBlockBody(block))); - closeContext(); - return result; + var es:Array = formatBlockBody(block); + if (castLoopVariableType != null) { + es.unshift(ENL(EVars([{ + name:varName, + t:TPath([castLoopVariableType]), + //val:EBinop("as", EIdent(varName + "_"), EIdent(castLoopVariableType), false) + val:EIdent("cast " + varName + "_") + }]))); + } + return writeExpr(EBlock(es)); } - inline function writeEForIn(ev:Expr, e:Expr, block:Expr):BlockEnd { - openContext(); + function writeEForIn(ev:Expr, e:Expr, block:Expr):BlockEnd { var etype = getExprType(e); - var regexp:EReg = ~/^Map<([^,]*)?,?.*>$/; - var isMap:Bool = etype != null && regexp.match(etype); + var canBeDictionary:Bool = isOpenFlDictionaryType(etype); + var isMap:Bool = canBeDictionary || isMapType(etype); + var isArray:Bool = isArrayType(etype) || isVectorType(etype); + var castLoopVariableType:String = null; write("for ("); - switch(ev) { - case EVars(vars): - if(vars.length == 1 && vars[0].val == null) { - write(vars[0].name); - if (!isMap || regexp.matched(1) == null) { - context.set(vars[0].name, "String"); - } else if (isIntType(regexp.matched(1))) { - context.set(vars[0].name, "Int"); - } else { - context.set(vars[0].name, regexp.matched(1)); - } - if (cfg.debugInferredType) { - write("/* inferred type: " + context.get(vars[0].name) + " */" ); - } - } else { - writeExpr(ev); - } - default: - writeExpr(ev); + var varName:String = getVarName(ev); + if (varName != null) { + write(varName); + var varType:String = getExprType(ev); + var mapKeyType:String; + if (isMap) { + mapKeyType = Typer.getMapParam(etype, 1); + //} else if (isArray) { + //mapKeyType = "Int"; + } else { + mapKeyType = "String"; + } + if (varType != mapKeyType && typer.doImplements(varType, mapKeyType)) { + // as we are supposing that this was a valid as3 code, we are trying to cast values to as3-defined type + castLoopVariableType = varType; + write("_"); + } + } else { + write("/* AS3HX ERROR unhandled " + e + " */"); + writeExpr(e); } write(" in "); if (isMap) { writeExpr(e); - write(".keys()"); + if (!canBeDictionary) { + write(".keys()"); + } + } else if (isArray) { + write("0..."); + writeExpr(e); + write(".length"); } else { write("Reflect.fields("); writeExpr(e); write(")"); } writeCloseStatement(); - var result = writeExpr(EBlock(formatBlockBody(block))); - closeContext(); - return result; + var es:Array = formatBlockBody(block); + if (castLoopVariableType != null) { + es.unshift(EVars([{ + name:varName, + t:TPath([castLoopVariableType]), + val:EIdent(varName + "_") + }])); + } + return writeExpr(EBlock(es)); } function writeEBinop(op:String, e1:Expr, e2:Expr, newLineAfterOp:Bool):BlockEnd { - if(op == "as") { + if (op == "as") { + var defaultCast:Bool = false; switch(e2) { case EIdent(s): switch(s) { - case "String": writeToString(e1); - case "int": writeCastToInt(e1); + case "String": + if (getExprType(e1) == "String") { + writeExpr(e1); + } else { + writeExpr(getToStringExpr(e1)); + } + case "int", "Int", "uint", "UInt": writeCastToInt(e1); case "Number": writeCastToFloat(e1); case "Array": - write("try cast("); + write("AS3.asArray("); writeExpr(e1); - write(", Array) catch(e:Dynamic) null"); - addWarning("as array", true); + write(")"); case "Class": - addWarning("as Class",true); - write("Type.getClass("); + switch (e1) { + case EField(ECall(_, _), 'constructor'): + write("Type.getClass("); + writeExpr(e1); + write(")"); + case _: + //addWarning("as Class", true); + write("as3hx.Compat.castClass("); + writeExpr(e1); + write(")"); + } + case "Function": + addWarning("as Function", false); + write("cast "); writeExpr(e1); - write(")"); default: - write("try cast("); - writeExpr(e1); - write(", "); - switch(e2) { - case EIdent(s): writeModifiedIdent(s); - default: writeExpr(e2); + if (s == "Dictionary" || s == "PropertyProxy" || s == "Object" || s == CommonImports.ObjectType) { + write("AS3.as("); + writeExpr(e1); + write(", " + s + ")"); + } else if (s == "ByteArray" || s == "Bitmap" || isVectorType(s) || s == "Dictionary") { + write("(try cast("); + writeExpr(e1); + write(", "); + switch(e2) { + case EIdent(s): writeModifiedIdent(s); + default: writeExpr(e2); + } + write(") catch(e:Dynamic) null)"); + } else { + defaultCast = true; } - write(") catch(e:Dynamic) null"); } case EField(_): - write("try cast("); + defaultCast = true; + case EVector(_): + if (cfg.useOpenFlTypes) { + write("as3hx.Compat.castVector("); + writeExpr(e1); + write(")"); + } else { + write("try cast("); + writeExpr(e1); + write(", "); + write("Vector"); + write(") catch(e:Dynamic) null"); + } + default: throw "Unexpected " + Std.string(e2); + } + if (defaultCast) { + var e2t:String = typer.getExprType(e2, true); + if (e2t == "Object" || e2t == CommonImports.ObjectType || e2t == CommonImports.ObjectImport || e2t == "Dynamic") { + write("(cast "); + writeExpr(e1); + write(")"); + } else { + write("AS3.as("); writeExpr(e1); write(", "); - switch(e2) { - case EIdent(s): writeModifiedIdent(s); - default: writeExpr(e2); + writeExpr(e2); + write(")"); + } + } + } else if (op == "is") { + switch(e2) { + case EVector(t) if (cfg.useOpenFlTypes): + write("as3hx.Compat.isVector("); + writeExpr(e1); + var ts:String = tstring(t); + if (ts != "Dynamic") { + write(", "); + write(ts); } - write(") catch(e:Dynamic) null"); - case EVector(_): - write("try cast("); + write(")"); + case EIdent("Object"): + writeEBinop("!=", e1, EIdent("null"), false); + default: + write("Std.is("); writeExpr(e1); write(", "); writeExpr(e2); - write(") catch(e:Dynamic) null"); - default: throw "Unexpected " + Std.string(e2); + write(")"); } - } else if(op == "is") { - write("Std.is("); - writeExpr(e1); - write(", "); - switch(e2) { - case EIdent(s): writeModifiedIdent(s); - default: writeExpr(e2); + + } else if (op == "in") { + var type2:String = getExprType(e2); + var result:Expr; + if (isMapType(type2) || isOpenFlDictionaryType(type2)) { + var rebuiltExpr = EField(e2, "exists"); + result = ECall(rebuiltExpr, [ETypedExpr(e1, TPath([typer.getMapIndexType(type2)]))]); + } else if (isDynamicType(type2)) { + //Reflect.hasField(e, f); + result = ECall(EField(EIdent("Reflect"), "hasField"), [e2, e1]); + } else { + //(Type.getInstanceFields(Type.getClass(e)).indexOf(params[0]) != -1) + result = EParent(EBinop("!=", ECall(EField(ECall(EField(EIdent("Type"), "getInstanceFields"), [ECall(EField(EIdent("Type"), "getClass"), [e2])]), "indexOf"), [e1]), EConst(CInt("-1")), false)); } - write(")"); - } else if(op == "in") { - write("Lambda.has("); - writeExpr(e2); - write(", "); - writeExpr(e1); - write(")"); + writeExpr(result); } else { // op e1 e2 var eBinop = rebuildBinopExpr(op, e1, e2); if (eBinop != null) return writeExpr(eBinop); + //if (op == "=") { + //switch(e1) { + //case EArray(e, index): + //var etype:String = getExprType(e); + //if (!isArrayAccessable(etype)) { + //if (isDynamicType(etype)) { + //write("Reflect.setField("); + //} else { + //write("Reflect.setProperty("); + //} + //writeExpr(e); + //write(", "); + //writeETypedExpr(index, TPath(["String"])); + //write(")"); + //} + //return Semi; + //default: + //} + //} + + var lookForRValue:Bool = true; var oldInLVA = inLvalAssign; - switch(e1) { - case EArray(_, _): if(op.indexOf("=") != -1) rvalue = e2; - case ECall(_, _): rvalue = e1; - case _: + if (op.indexOf("=") != -1 /*&& op != "=="*/ && op != "!=") { + if (op == "=") inLvalAssign = true; + rvalue = e2; + var t = getExprType(e1); + //if (t != getExprType(e2)) { + if (t != null) { + switch (e2) { + case null: + case EIdent("null"): + case EArrayDecl(es) if (es.length == 0): + case ETypedExpr(e, _): + e2 = ETypedExpr(e, TPath([t])); + default: + e2 = ETypedExpr(e2, TPath([t])); + } + } + //} + } else { + switch(e1) { + case EArrayDecl(_): rvalue = e2; + case ECall(_, _): rvalue = e1; + case _:lookForRValue = false; + } } - if(op.indexOf("=") != -1 || e1.match(EArrayDecl(_))) rvalue = e2; - if(op == "=") inLvalAssign = true; switch(e1) { case EIdent(s): writeModifiedIdent(s); @@ -2226,7 +2801,7 @@ class Writer lvl += 2; inLvalAssign = oldInLVA; - if(rvalue != null) { + if(!lookForRValue || rvalue != null) { //check wether newline was found just before //op while parsing if (newLineAfterOp) { @@ -2243,7 +2818,6 @@ class Writer case ENL(_): default: write(" "); } - switch(e2) { case EIdent(s): writeModifiedIdent(s); default: writeExpr(e2); @@ -2253,16 +2827,29 @@ class Writer if (op == "=") lvl -= 2; } - return Semi; - } - - inline function writeEUnop(op:String, prefix:Bool, e:Expr):BlockEnd { - var result = Semi; - var type = getExprType(e); - if((isIntType(type) || type == "UInt") && op == "!") { + return Semi; + } + + function writeEUnop(op:String, prefix:Bool, e:Expr):BlockEnd { + var result = Semi; + var type = getExprType(e); + switch(e) { + case EField(a, "length") if (op == "++" || op == "--"): + var type = getExprType(a); + if (isVectorType(type)) { + if (op == "++") { + writeExpr(EBinop("+=", e, EConst(CInt("1")), false)); + } else { + writeExpr(EBinop("-=", e, EConst(CInt("1")), false)); + } + return result; + } + default: + } + if ((isIntType(type) || type == "UInt") && op == "!") { writeExpr(EBinop("!=", e, EConst(CInt("0")), false)); result = None; - } else if(prefix) { + } else if (prefix) { write(op); writeExpr(e); } else { @@ -2272,17 +2859,9 @@ class Writer return result; } - function writeToString(e:Expr) { - var type = getExprType(e); - if (type != "String") { - e = getToStringExpr(e); - } - writeExpr(e); - } - inline function getToStringExpr(e:Expr):Expr return ECall(EField(EIdent("Std"), "string"), [e]); - function writeCastToInt(e:Expr) { + function writeCastToInt(e:Expr):Void { var type = getExprType(e); if(type != "Int") { e = getCastToIntExpr(e); @@ -2291,9 +2870,9 @@ class Writer } inline function needCastToInt(e:Expr):Bool { - var isCompatParseInt:Expr->Bool = function(e) return e.match(ECall(EField(EIdent("as3hx.Compat"), "parseInt"), _)); + var isCompatParseInt:Expr->Bool = function(e) return e.match(ECall(EField(EIdent("AS3"), "int"), _)); return switch(e) { - case EBinop(op,e1,_,_): !isCompatParseInt(e1) && (isBitwiceOp(op) || isNumericOp(op)); + case EBinop(op,e1,_,_): !isCompatParseInt(e1) && (isBitwiseOp(op) || isNumericOp(op)); case EUnop(op,_,e): op == "~" && !isCompatParseInt(e); case EIdent(_) | EConst(_): getExprType(e) == "Float"; case EParent(e): needCastToInt(e); @@ -2303,12 +2882,12 @@ class Writer function getCastToIntExpr(e:Expr):Expr { if(cfg.useCompat) { - return getCompatCallExpr("parseInt", [e]); + return ECall(EField(EIdent("AS3"), "int"), [e]); } return ECall(EField(EIdent("Std"), "parseInt"), [getToStringExpr(e)]); } - function writeCastToFloat(e:Expr) { + function writeCastToFloat(e:Expr):Void { var type = getExprType(e); if (type != "Float" && type != "Int") { e = getCastToFloatExpr(e); @@ -2459,15 +3038,13 @@ class Writer // return false; // }); // e1.(@user_id == 3) attribute search - function writeE4XFilterExpr(e1:Expr, e2:Expr) { + function writeE4XFilterExpr(e1:Expr, e2:Expr):Void { if(inE4XFilter) throw "Unexpected E4XFilter inside E4XFilter"; write("FastXML.filterNodes("); //write("/*"+Std.string(e1)+"*/"); var n = getBaseVar(e1); // make sure it's set to FastXML in the - if(n != null) - context.set(n, "FastXML"); // current context var old = inArrayAccess; inArrayAccess = true; // ensure 'nodes' vs. 'node' writeExpr(e1); @@ -2502,7 +3079,7 @@ class Writer result = writeExpr(e); writeDelimiter(); } - writeComment(formatComment(s, isBlock)); + writeComment(formatComment(s, isBlock), isBlock); if(!isTail) { writeDelimiter(); result = writeExpr(e); @@ -2511,41 +3088,127 @@ class Writer return result; } - inline function writeENew(t : T, params : Array):Void { + private inline function writeDictionaryTypeConstructor(k:String, v:String):Void { + //write("new "); + //if (k == "Dynamic" || k == "Object" || k == CommonImports.ObjectImport) { + //if (k == "Object" || k == CommonImports.ObjectImport) k = "Dynamic"; + //write("haxe.ds.ObjectMap<" + k + ", "); + //} else if (k == "Float" || k == "Class" || k == "Class") { + //write("Dictionary<" + k + ","); + //} else { + //write("Map<" + k + ","); + //} + //write(v + ">("); + if (k == "Dynamic" || k == CommonImports.ObjectType || k == CommonImports.ObjectImport && cfg.useOpenFlTypes) { + write("new Dictionary<{}, " + v + ">("); + } else { + write("new Dictionary<" + k + ", " + v + ">("); + } + } + + function writeENew(t : T, params : Array):Void { var writeParams = function() { - for(i in 0...params.length) { - if(i > 0) + var argTypes:Array = typer.getClassConstructorTypes(tstring(t)); + for (i in 0...params.length) { + if (i > 0) write(", "); - writeExpr(params[i]); + var param:Expr = params[i]; + switch (param) { + case EVector(t): + param = EIdent("Vector"); + default: + } + if (argTypes != null && i < argTypes.length) { + writeETypedExpr(param, argTypes[i]); + } else { + writeExpr(param); + } } } - //write("/* " +context.get(tstring(t,false,false))+ " */"); - var origType = context.get(tstring(t, false, false)); - if(origType == "Class") { + switch (t) { + case TComplex(e): + var pack:Array = typer.getPackString(e); + if (pack != null) { + t = TPath(pack); + } + default: + } + var isVariable:Bool = false; + var handled = false; + switch (t) { + case TPath(p) if (p.length == 1 && typer.isVariable(p[0])): isVariable = true; + default: + } + if(isVariable) { write("Type.createInstance("); write(tstring(t, false, false)); write(", ["); writeParams(); write("])"); + handled = true; } //in AS3, if Date constructed without argument, uses current time - else if (tstring(t) == "Date" && params.empty()) { - write("Date.now()"); //use Haxe constructor for current time - } else { - var isObject = switch(t) { - case TPath(p) if(p[0] == "Object"): true; - default: false; + else if (tstring(t) == "Date") { + if (params.length == 0) { + write("Date.now()"); //use Haxe constructor for current time + handled = true; + } else if (params.length == 1) { + write("Date.fromTime("); + writeParams(); + write(")"); + handled = true; + } else { + while (params.length < 6) { + params.push(EConst(CInt("0"))); + } + } + } + var isObject = false; + var isDictionary = false; + if (!handled) { + switch(t) { + case TDictionary(k, v): + if (cfg.useOpenFlTypes && !cfg.dictionaryToHash) { + var ks:String = tstring(k); + var kv:String = tstring(v); + writeDictionaryTypeConstructor(ks, kv); + isDictionary = true; + } + case TPath(p) if (p[0] == "Object" || p[0] == CommonImports.ObjectType || p[0] == CommonImports.ObjectImport): isObject = true; + case TPath(p) if (p[0] == "ByteArray" || p[0] == "openfl.utils.ByteArray"): + writeExpr(getCompatCallExpr("newByteArray", params)); + handled = true; + case TPath(p): + if (isOpenFlDictionaryType(p[0])) { + var ks:String = Typer.getMapParam(p[0], 0); + var kv:String = Typer.getMapParam(p[0], 1); + writeDictionaryTypeConstructor(ks, kv); + isDictionary = true; + } + default: + } + } + if (!handled) { + if (isObject) write("{}"); + else if (!isDictionary) { + var ts = tstring(t); + if (ts == "FastXML") { + writeParams(); + return; + } + write("new " + ts + "("); } - if(isObject) write("{}"); - else write("new " + tstring(t) + "("); // prevent params when converting vector to array var out = switch(t) { case TVector(_): !cfg.vectorToArray; - case TDictionary(_,_): !cfg.dictionaryToHash; + case TDictionary(_, _): !cfg.dictionaryToHash; + case TPath(p): !(p[0] == "Array"); default: true; } - if(out) writeParams(); - if(!isObject) write(")"); + if (isDictionary && params.length > 0) write("/*"); + if (out) writeParams(); + if (isDictionary && params.length > 0) write("*/"); + if (!isObject) write(")"); } } @@ -2566,37 +3229,162 @@ class Writer return result; } - inline function writeEDelete(e:Expr) { + function isOpenFlDictionaryTypeT(t:T):Bool { + switch (t) { + case TDictionary(_, _): return true; + case TPath(p): + if (p.length == 1) { + if (p[0].indexOf("Dictionary") == 0 || p[0].indexOf("openfl.utils.Dictionary") == 0) { + return true; + } + } else if (p.length == 3) { + if (p[0] == "openfl" && p[1] == "utils" && p[2].indexOf("Dictionary") == 0) { + return true; + } + } + default: + } + return false; + } + + function writeETypedExpr(e:Expr, t:T):BlockEnd { + var nullable:Bool = false; switch(e) { - case EArray(a, i): - var atype = getExprType(a); - if (atype != null) { - if (isMapType(atype)) { - writeExpr(a); - write(".remove("); - writeExpr(i); - write(")"); - } else if (atype == "Dynamic") { - switch(i) { - case EConst(c): - switch(c) { - case CInt(v) | CFloat(v): i = EConst(CString(v)); - default: - } - case EIdent(_): - var type = getExprType(i); - if (type == null || type != "String") { - i = getToStringExpr(i); - } + case null: + write("intended error: ETypedExpr(null as " + tstring(t) + ")"); + return None; + case ENL(e2): + writeNL(); + writeIndent(); + return writeETypedExpr(e2, t); + //return writeExpr(ENL(ETypedExpr(e2, t))); + case ECommented(s, isBlock, isTail, e2): + return writeECommented(s, isBlock, isTail, ETypedExpr(e2, t)); + case ETernary(cond, e1, e2): + return writeETernarny(cond, ETypedExpr(e1, t), ETypedExpr(e2, t)); + case ENew(t2, params): + if (isOpenFlDictionaryTypeT(t2) && isOpenFlDictionaryTypeT(t)) { + return writeExpr(ENew(typer.shortenType(t), params)); + } + case EField(e, field): + var t:String = getExprType(e); + if (isDynamicType(t)) { + nullable = true; + } + case EArray(e, index): + var t:String = getExprType(e); + if (isMapType(t) || isOpenFlDictionaryType(t) || isDynamicType(t)) { + nullable = true; + } + case EIdent("null"): + switch(tstring(t)) { + case "String": + return writeExpr(e); + case "Bool": + return writeExpr(EIdent("false")); + case "Int","UInt": + return writeExpr(EConst(CInt("0"))); + } + default: + } + // fix of such constructions var tail:Signal = s || p; + var type:String = tstring(t); + switch(type) { + case "Function", "haxe.Constraints.Function": + var et:String = getExprType(e); + if (et == "Function" || et == "haxe.Constraints.Function") { + write("cast "); + } + case "Signal": + write("cast "); + case "String": + if (getExprType(e) != "String") { + if (nullable) { + e = ECall(EField(EIdent("AS3"), "string"), [e]); + } else { + e = getToStringExpr(e); + } + } + case "Bool": + var re:Expr = rebuildIfExpr(e); + if (re != null) { + e = re; + } + case "Int", "UInt": + if (nullable) { + e = ECall(EField(EIdent("AS3"), "int"), [e]); + } else { + var et:String = getExprType(e); + if (et != "Int" && et != "UInt") { + e = getCastToIntExpr(e); + } + } + default: + switch (e) { + case EBinop("||", e1, e2, nl): + e = ETernary(e1, e1, e2); + case EBinop("&&", e1, e2, nl): + e = ETernary(EUnop("!", true, e1), e1, e2); + case ECall(EVector(t), params): + if (type == "Object" || type == CommonImports.ObjectType || type == CommonImports.ObjectImport || (isVectorType(type) && getExprType(e) != type)) { + write("cast "); + } + default: + if (isArrayType(type)) { + var eType:String = getExprType(e); + if (isArrayType(eType) && type != eType) { + write("cast "); + } + } + } + } + return writeExpr(e); + } + + private function writeDelete(object:Expr, index:Expr):Void { + var atype = getExprType(object); + if (atype != null) { + if (isMapType(atype) || isOpenFlDictionaryType(atype)) { + writeExpr(object); + write(".remove("); + writeExpr(index); + write(")"); + } else if (atype == "Dynamic" || atype == "Object" || atype == CommonImports.ObjectType || atype == CommonImports.ObjectImport) { + switch(index) { + case EConst(c): + switch(c) { + case CInt(v) | CFloat(v): index = EConst(CString(v)); default: } - writeExpr(ECall(EField(EIdent("Reflect"), "deleteField"), [a, i])); - } else if(atype == "Dictionary") { - addWarning("EDelete"); - writeNL("This is an intentional compilation error. See the README for handling the delete keyword"); - writeIndent('delete ${getIdentString(a)}[${getIdentString(i)}]'); - } + case EIdent(_): + var type = getExprType(index); + if (type == null || type != "String") { + index = getToStringExpr(index); + } + default: } + writeExpr(ECall(EField(EIdent("Reflect"), "deleteField"), [object, index])); + } else if(atype == "Dictionary") { + addWarning("EDelete"); + writeNL("This is an intentional compilation error. See the README for handling the delete keyword"); + writeIndent('delete ${getIdentString(object)}[${getIdentString(index)}]'); + } else if (isArrayType(atype)) { + writeExpr(EBinop("=", EArray(object, index), EIdent("null"), false)); + } else { + addWarning("EDelete"); + writeNL("This is an intentional compilation error. See the README for handling the delete keyword"); + writeIndent('delete ${getIdentString(object)}[${getIdentString(index)}]'); + write("/* " + atype + " */"); + } + } + } + + inline function writeEDelete(e:Expr) { + switch(e) { + case EField(e, f): + writeDelete(e, EConst(CString(f))); + case EArray(a, i): + writeDelete(a, i); default: addWarning("EDelete"); writeNL("This is an intentional compilation error. See the README for handling the delete keyword"); @@ -2626,7 +3414,8 @@ class Writer case EIdent(id): if(id.charAt(0) == "@") return EIdent("x.att."+id.substr(1)); - return EIdent("x.node."+id+".innerData"); + //return EIdent("x.node."+id+".innerData"); + return EIdent("x.node."+id); case ECall(e2, params): //ECall(EField(EIdent(name),charAt),[EConst(CInt(0))]) var f = function(e) : String { @@ -2703,21 +3492,33 @@ class Writer return (s == "Float" || isIntType(s) || s == "UInt"); } switch(e) { - case EArray(_,_): return EBinop("!=", e, EIdent("null"), false); - case EIdent(id): - if(id == "null") return null; + case EArray(_,_): + var t = getExprType(e); + if (t == "Bool") return null; + return EBinop("!=", e, EIdent("null"), false); + case EIdent("null"): return null; + case EIdent(_), EField(_, _), ECall(_, _): var t = getExprType(e); - if(t == null || t == "Bool") return null; return switch(t) { - case "Int" | "UInt": EBinop("!=", e, EConst(CInt("0")), false); + case "Bool": + null; + case null, "Dynamic": + ECall(EField(EIdent("AS3"), "as"), [e, EIdent("Bool")]); + case "Int" | "UInt": + EBinop("!=", e, EConst(CInt("0")), false); case "Float": - var lvalue = EBinop("!=", e, EConst(CInt("0")), false); - var rvalue = EUnop("!", true, ECall(EField(EIdent("Math"), "isNaN"), [e])); - EParent(EBinop("&&", lvalue, rvalue, false)); + switch (e) { + case ECall(_, _): + e; + case _: + var lvalue = EBinop("!=", e, EConst(CInt("0")), false); + var rvalue = EUnop("!", true, ECall(EField(EIdent("Math"), "isNaN"), [e])); + EParent(EBinop("&&", lvalue, rvalue, false)); + } default: EBinop("!=", e, EIdent("null"), false); } case EBinop(op, e2, e3, n): - if(isBitwiceOp(op)) return EBinop("!=", EParent(e), EConst(CInt("0")), false); + if(isBitwiseOp(op) || isNumericOp(op)) return EBinop("!=", EParent(e), EConst(CInt("0")), false); if(isNumericConst(e2) || isNumericConst(e3)) return null; if(op == "==" || op == "!=" || op == "!==" || op == "===") return null; if(op == "is" || op == "in" || op == "as") return null; @@ -2742,7 +3543,8 @@ class Writer case EParent(e): f(e); default: null; } - return f(r2); + var r3 = f(r2); + return r3 == null ? EUnop(op, prefix, r2) : r3; } var t = getExprType(e2); if(t == null) return null; @@ -2752,8 +3554,6 @@ class Writer var r2 = rebuildIfExpr(e2); if(r2 == null) return null; return EParent(r2); - case ECall(e2, params): //These would require a full typer - case EField(e2, f): null; case ENL(e): var expr = rebuildIfExpr(e); if (expr == null) return null; @@ -2784,9 +3584,26 @@ class Writer var result = null; switch (expr) { case EField(e, f): - if(f == "hasOwnProperty") { - var rebuiltExpr = EField(e, "exists"); - result = ECall(rebuiltExpr, params); + if (f == "hasOwnProperty") { + var type = getExprType(e); + if (isMapType(type) || isOpenFlDictionaryType(type)) { + var rebuiltExpr = EField(e, "exists"); + result = ECall(rebuiltExpr, params); + } else if (isDynamicType(type)) { + //Reflect.hasField(e, f); + var e1:Expr = params[0]; + if (getExprType(e1) != "String") { + e1 = getToStringExpr(e1); + } + result = ECall(EField(EIdent("Reflect"), "hasField"), [e, e1]); + } else if (true || isClassType(type)) { + result = ECall(EField(EIdent("AS3"), "hasOwnProperty"), [e, params[0]]); + //result = EParent(EBinop("!=", ECall(EField(ECall(EField(EIdent("Type"), "getClassFields"), [e]), "indexOf"), params), EConst(CInt("-1")), false)); + } else { + //(Type.getInstanceFields(Type.getClass(e)).indexOf(params[0]) != -1) + //result = EParent(EBinop("!=", ECall(EField(ECall(EField(EIdent("Type"), "getInstanceFields"), [ECall(EField(EIdent("Type"), "getClass"), [e])]), "indexOf"), params), EConst(CInt("-1")), false)); + result = ECall(EField(EIdent("AS3"), "hasOwnProperty"), [e, params[0]]); + } } else if(f == "replace") { var type = getExprType(e); @@ -2801,15 +3618,20 @@ class Writer params[0] = e; result = ECall(EField(param0, f), params); } else { - var isString = switch(param0) { - case ECall(e,_): getIdentString(e) == "String"; - default: param0Type == "String"; - } - if(isString) { - params.insert(0, e); - result = ECall(EField(EIdent("StringTools"), f), params); - } + params.insert(0, e); + result = ECall(EField(EIdent("StringTools"), f), params); + } + } + } + else if(f == "fromCharCode" && params.length > 1) { + var type = getIdentString(e); + if (type == "String") { + //replace AS3 slice by Haxe substr + var rebuiltExpr = ECall(EField(e, f), [params[0]]); + for (i in 1...params.length) { + rebuiltExpr = EBinop("+", rebuiltExpr, ECall(EField(e, f), [params[i]]), false); } + result = rebuiltExpr; } } else if(f == "slice") { @@ -2819,14 +3641,35 @@ class Writer //replace AS3 slice by Haxe substr var rebuiltExpr = EField(e, "substring"); result = ECall(rebuiltExpr, params); - } else if(isArrayType(type) && params.empty()) { + } else if((isArrayType(type) || isVectorExpr(e)) && params.empty()) { var rebuiltExpr = EField(e, "copy"); result = ECall(rebuiltExpr, params); } } } - else if(f == "splice") { + else if (f == "splice") { if(isArrayExpr(e)) { + switch(params.length) { + case 0 | 2: + case 1: + params.push(EField(e, "length")); + result = ECall(EField(e, f), params); + default: + if (cfg.useCompat) { + switch (params[1]) { + case EConst(CInt("1")) if (params.length == 3): + result = EBinop("=", EArray(e, params[0]), params[2], false); + case EConst(CInt("0")) if (params.length == 3): + result = ECall(EField(e, "insert"), [params[0], params[2]]); + default: + var p = [e].concat(params.slice(0, 2)); + p.push(EArrayDecl(params.slice(2, params.length))); + result = getCompatCallExpr("arraySplice", p); + } + } + } + } + if(isVectorExpr(e)) { switch(params.length) { case 0 | 2: case 1: @@ -2836,11 +3679,48 @@ class Writer if(cfg.useCompat) { var p = [e].concat(params.slice(0, 2)); p.push(EArrayDecl(params.slice(2, params.length))); - result = getCompatCallExpr("arraySplice", p); + result = getCompatCallExpr("vectorSplice", p); } } } } + else if(f == "match") { + var type = getExprType(e); + if(type != null) { + if (type == "String") { + result = ECall(EField(EIdent("as3hx.Compat"), "match"), [e, params[0]]); + } + } + } + else if(f == "toLocaleLowerCase") { + var type = getExprType(e); + if(type != null) { + if (type == "String") { + result = ECall(EField(e, "toLowerCase"), params); + } + } + } + else if(f == "toLocaleUpperCase") { + var type = getExprType(e); + if(type != null) { + if (type == "String") { + result = ECall(EField(e, "toUpperCase"), params); + } + } + } + else if(f == "search") { + var type = getExprType(e); + if(type != null) { + if (type == "String") { + var param0Type:String = getExprType(params[0]); + if (param0Type == "as3hx.Compat.Regex" || param0Type == "EReg") { + result = ECall(EField(EIdent("as3hx.Compat"), "search"), [e, params[0]]); + } else { + result = ECall(EField(e, "indexOf"), [params[0]]); + } + } + } + } else if(f == "indexOf") { //in AS3, indexOf is a method in Array while it is not in Haxe //Replace it by the Labda.indexOf method @@ -2860,6 +3740,26 @@ class Writer result = ECall(EField(e, "insert"), params); } } + else if (f == "filter") { + if(isArrayExpr(e) || isVectorExpr(e)) { + result = ECall(EField(EIdent("as3hx.Compat"), "filter"), [e, params[0]]); + } + } + else if (f == "sort") { + if (isArrayExpr(e)) { + switch (params[0]) { + case null: + result = ECall(EField(e, "sort"), [EField(EIdent("AS3"), "defaultSort")]); + case EField(e1, "RETURNINDEXEDARRAY"): + switch(e1) { + case EIdent("Array"): + result = getCompatCallExpr("sortIndexedArray", [e]); + default: + } + default: + } + } + } else if((f == "min" || f == "max") && params.length > 2) { if(getIdentString(e) == "Math") { result = ECall(EField(e, f), params.slice(0, 2)); @@ -2868,16 +3768,28 @@ class Writer } } } - else if(f == "toFixed") { + else if (f == "toFixed" || f == "toPrecision") { if(getExprType(e) == "Float") { result = getCompatCallExpr(f, [e].concat(params)); } } - else if(f == "toString") { - result = getToStringExpr(e); + else if (f == "toString") { + if (params.length == 1) { + var type:String = getExprType(e); + if (type == "Int" || type == "UInt" || type == "Float") { + if (type == "Float") { + e = getCastToIntExpr(e); + } + result = getCompatCallExpr(f, [e].concat(params)); + } else { + //result = getToStringExpr(e); // valid call + } + } else { + result = getToStringExpr(e); + } } else if(f == "concat" && params.empty()) { - if(isArrayExpr(e)) { + if(isArrayExpr(e) || isVectorExpr(e)) { var rebuildExpr = EField(e, "copy"); result = ECall(rebuildExpr, params); } @@ -2893,15 +3805,17 @@ class Writer result = ECall(EField(e, f), [EConst(CInt("0"))]); } } - else if(f == "apply") { - if(isFunctionExpr(e)) { - params = [EIdent("null"), e].concat(params.slice(1)); + else if (f == "apply") { + if (isFunctionExpr(e)) { + var thisArg = params.length == 0 ? EIdent("null") : params.shift(); + params = [thisArg, e].concat(params); result = ECall(EField(EIdent("Reflect"), "callMethod"), params); } } else if(f == "call") { if(isFunctionExpr(e)) { - params = [EIdent("null"), e].concat([EArrayDecl(params.slice(1))]); + var thisArg = params.length == 0 ? EIdent("null") : params.shift(); + params = [thisArg, e, EArrayDecl(params)]; result = ECall(EField(EIdent("Reflect"), "callMethod"), params); } } @@ -2916,6 +3830,20 @@ class Writer result = ECall(EField(EIdent("StringTools"), f), params); } } + else if (f == "resolveClass") { + if (getIdentString(e) == "Type") { + if (params.length == 1) { + switch(params[0]) { + case ECall(EIdent("getQualifiedSuperClassName"), params2): + result = ECall(EField(EIdent("Type"), "getSuperClass"), [ECall(EField(EIdent("Type"), "getClass"), params2)]); + + case ECall(EIdent("getQualifiedClassName"), params2): + result = ECall(EField(EIdent("Type"), "getClass"), params2); + default: + } + } + } + } else if(f == "isWhitespace") { if(getIdentString(e) == "StringUtil") { params.push(EConst(CInt("0"))); @@ -3023,7 +3951,7 @@ class Writer } return changed ? EBinop(op, lvalue, rvalue, false) : null; } - if(isBitwiseAndAssignmetnOp(op)) return EBinop("=", lvalue, EBinop(op.charAt(0), lvalue, rvalue, false), false); + if (isBitwiseAndAssignmetnOp(op)) return EBinop("=", lvalue, EBinop(op.charAt(0), lvalue, rvalue, false), false); switch(op) { case "||=": var type = getExprType(lvalue); @@ -3032,7 +3960,8 @@ class Writer case "Bool": lvalue; case "Int" | "UInt" | "Float" | _: rebuildIfExpr(lvalue); } - if(isDynamicType(type)) { + if (cond == null) cond = lvalue; + if (isDynamicType(type)) { cond = switch(cond) { case EBinop(op, e1, e2, false) if(op == "!="): EBinop("==", e1, e2, false); default: cond; @@ -3046,6 +3975,8 @@ class Writer if(type == "Bool") { return EBinop("=", lvalue, EBinop("&&", lvalue, rvalue, false), false); } + case "==" if (isFunctionExpr(lvalue) || isFunctionExpr(rvalue)): + return ECall(EField(EIdent("Reflect"), "compareMethods"), [lvalue, rvalue]); case "=" | "+=" | "-=" | "*=" | "/=": if(cfg.useCompat) { switch(lvalue) { @@ -3056,10 +3987,12 @@ class Writer default: } } - if(isIntExpr(lvalue)) { - if(needCastToInt(rvalue)) { + if (isIntExpr(lvalue)) { + if (op == "/=") { + return EBinop("=", lvalue, EBinop("/", lvalue, rvalue, false), false); + } else if(needCastToInt(rvalue)) { switch(rvalue) { - case EBinop(op, e1, e2, newLineAfterOp) if(isBitwiceOp(op)): rvalue = getResultForNumerics(op, e1, e2); + case EBinop(op, e1, e2, newLineAfterOp) if(isBitwiseOp(op)): rvalue = getResultForNumerics(op, e1, e2); case EUnop(op, prefix, e) if(op == "~"): if(needCastToInt(e)) e = getCastToIntExpr(e); rvalue = EUnop(op, prefix, e); @@ -3072,7 +4005,7 @@ class Writer } } switch(rvalue) { - case EBinop(op,e1,e2,_) if(op == "||="): + case EBinop(op,e1,e2,_) if (op == "||="): writeExpr(rebuildBinopExpr(op, e1, e2)); writeNL(); writeIndent(); @@ -3110,15 +4043,22 @@ class Writer * Return wether the expression contained in an * "if" statement is a one liner with no block bracket */ - function isOneLiner(e : Expr) : Bool { + function isOneLiner(e : Expr, threatOneLineBlockAsOneLiner:Bool = false) : Bool { return switch (e) { case ENL(e): //ignore newline - return isOneLiner(e); + return isOneLiner(e, threatOneLineBlockAsOneLiner); - case ECommented(s,b,t,e): //ignore comment - return isOneLiner(e); + case ECommented(s, b, t, e): //ignore comment + if (isHaxeCodeComment(s)) return false; + return isOneLiner(e, threatOneLineBlockAsOneLiner); case EBlock(e): //it is a regular block + if (threatOneLineBlockAsOneLiner && e.length == 1) { + switch(e[0]) { + case ENL(ex)://skip + default: return true; + } + } return false; default: //if it begins with anything but a block, one liner @@ -3147,6 +4087,14 @@ class Writer return false; } + function prepareObjectFieldName(name:String):String { + //if (validVariableNameEReg.match(name)) { + //return name; + //} else { + return "'" + name + "'"; + //} + } + /** * Checks if 'e' represents a numerical constant value * @return true if so @@ -3161,17 +4109,50 @@ class Writer return isArrayType(type); } + inline function isVectorExpr(e:Expr):Bool { + var type = getExprType(e); + return isVectorType(type); + } + static inline function isArrayType(s:String):Bool { - return s != null && StringTools.startsWith(s, "Array<"); + return s != null && ((s.indexOf("Array<") == 0) || s == "Array"); + } + + inline function isVectorType(s:String):Bool { + return s != null && (s.indexOf("Vector<") == 0 || s.indexOf("openfl.Vector<") == 0 || s.indexOf(cfg.arrayTypePath + "<") == 0); + } + + static inline function isDynamicType(s:String):Bool return s == "Dynamic" || s == "Object" || s == CommonImports.ObjectType || s == CommonImports.ObjectImport; + + inline function isMapType(s:String):Bool { + return s != null && (s.indexOf("Map") == 0 || s.indexOf("haxe.ds.ObjectMap") == 0); } - static inline function isDynamicType(s:String):Bool return s == "Dynamic"; + inline function isByteArrayType(s:String):Bool { + return s == "ByteArray"; + } + + inline function isOpenFlDictionaryType(s:String):Bool { + return s != null && (s.indexOf("Dictionary") == 0 || s.indexOf("openfl.utils.Dictionary") == 0) && cfg.useOpenFlTypes; + } - static inline function isMapType(s:String):Bool { - return s != null && (s.startsWith("Map") || s.startsWith("haxe.ds.ObjectMap")); + function isFunctionType(type:String):Bool { + if (type == "Function" || type == "haxe.Constraints.Function") return true; + if (type == null) return false; + var delimeters:Array = []; + Typer.getTypeParams(type, delimeters); + var level:Int = 0; + for (d in delimeters) { + if (d == "<" || d == "(") level++; + if (d == ">" || d == ")") level--; + if (d == "->" && level == 0) return true; + } + return false; } - inline function isFunctionExpr(e:Expr):Bool return getExprType(e) == "Function"; + inline function isFunctionExpr(e:Expr):Bool { + return isFunctionType(getExprType(e)); + } inline function isIntExpr(e:Expr):Bool { var type = getExprType(e); @@ -3180,12 +4161,16 @@ class Writer inline function isIntType(s:String):Bool return s == "Int"; + inline function isBoolType(s:String):Bool return s == "Bool"; + + inline function isClassType(s:String):Bool return s == "Class" || s == "Class"; + inline function isNumericOp(s:String):Bool return switch(s) { case "/" | "-" | "+" | "*" | "%" | "--" | "++": true; default: false; } - inline function isBitwiceOp(s:String):Bool return switch(s) { + inline function isBitwiseOp(s:String):Bool return switch(s) { case "<<" | ">>" | ">>>" | "^" | "|" | "&" | "~": true; default: false; } @@ -3200,18 +4185,18 @@ class Writer default: false; } - function addWarning(type:String, isError = false) { + function addWarning(type:String, isError = false):Void { warnings.set(type, isError); } static function quote(s : String) : String { - return '"' + StringTools.replace(s, '"', '\\"') + '"'; + return "'" + StringTools.replace(s, "'", "\\'") + "'"; } static function eregQuote(s : String) : String { - return "'" + StringTools.replace(s, "\\", "\\\\") + "'"; + return "'" + StringTools.replace(StringTools.replace(s, '\\', '\\\\'), "'", "\\'") + "'"; } function isOverride(kwds : Array) : Bool @@ -3229,38 +4214,35 @@ class Writer return Lambda.has(kwds, "public"); } - function isPrivate(kwds : Array) : Bool - { + function isPrivate(kwds : Array) : Bool { return Lambda.has(kwds, "private"); } - function isInternal(kwds : Array) : Bool - { + function isInternal(kwds : Array) : Bool { return Lambda.has(kwds, "internal"); } - function isFinal(kwds : Array) : Bool - { + function isMXInternal(kwds : Array) : Bool { + return Lambda.has(kwds, "mx_internal"); + } + + function isFinal(kwds : Array) : Bool { return Lambda.has(kwds, "final"); } - function isProtected(kwds : Array) : Bool - { + function isProtected(kwds : Array) : Bool { return Lambda.has(kwds, "protected"); } - function isGetter(kwds : Array) : Bool - { + function isGetter(kwds : Array) : Bool { return Lambda.has(kwds, "get"); } - function isSetter(kwds : Array) : Bool - { + function isSetter(kwds : Array) : Bool { return Lambda.has(kwds, "set"); } - function isConst(kwds : Array) : Bool - { + function isConst(kwds : Array) : Bool { return Lambda.has(kwds, "const"); } @@ -3274,35 +4256,16 @@ class Writer case "int" | "uint" | "void": return null; default: return fixCase ? properCase(c, true) : c; } + case TVector(t) if (cfg.useOpenFlTypes): return "Vector"; default: null; } } function tstring(t : T, isNativeGetSet:Bool = false, fixCase:Bool = true) : String { - if(t == null) return null; return switch(t) { - case TStar: "Dynamic"; - case TVector(t): cfg.vectorToArray ? "Array<" + tstring(t) + ">" : "Vector<" + tstring(t) + ">"; - case TPath(p): - var c = p.join("."); - return switch(c) { - case "Array" : "Array"; - case "Boolean" : "Bool"; - case "Class" : "Class"; - case "int" : "Int"; - case "Number" : "Float"; - case "uint" : cfg.uintToInt ? "Int" : "UInt"; - case "void" : "Void"; - case "Function" : cfg.functionToDynamic ? "Dynamic" : c; - case "Object" : isNativeGetSet ? "{}" : "Dynamic"; - case "XML" : cfg.useFastXML ? "FastXML" : "Xml"; - case "XMLList" : cfg.useFastXML ? "FastXMLList" : "Iterator"; - case "RegExp" : cfg.useCompat ? "as3hx.Compat.Regex" : "flash.utils.RegExp"; - default : fixCase ? properCase(c, true) : c; - } + case null: null; case TComplex(e): buffer(function() { writeExpr(e); }); - case TDictionary(k, v): "haxe.ds.ObjectMap<" + tstring(k) + ", " + tstring(v) + ">"; - case TFunction(p): p.map(function(it) return tstring(it)).join("->"); + default: typer.tstring(t, isNativeGetSet, fixCase); } } @@ -3310,8 +4273,7 @@ class Writer * Write an As3 package level function. As Haxe * does not have this, wrap it in a class definition */ - function writeFunctionDef(fDef : FunctionDef) - { + function writeFunctionDef(fDef : FunctionDef):Void { writeClassDef(wrapFuncDefInClassDef(fDef)); } @@ -3323,11 +4285,13 @@ class Writer { //first func need to be converted to the field //type for classes + var kwds:Array = fDef.kwds.copy(); + kwds.push("static"); var funcAsClassField : ClassField = { name : fDef.name, - meta : fDef.meta, + meta : [ENL(null)], condVars : [], - kwds : fDef.kwds, + kwds : kwds, kind : FFun(fDef.f) }; @@ -3335,24 +4299,24 @@ class Writer var name = fDef.name.charAt(0).toUpperCase() + fDef.name.substr(1); //generate class doc - var meta = []; + var meta = fDef.meta.copy(); meta.push(ENL(null)); meta.push(ECommented("/**\n * Class for " + fDef.name + "\n */",false,false,null)); meta.push(ENL(null)); - //builds the class definition - return { - name : "ClassFor" + name, - meta:meta, - kwds:["final"], //always final as generated class - imports:[], - isInterface : false, - extend : null, - implement : [], - fields:[funcAsClassField], - inits : [] - }; + var result:ClassDef = new ClassDef(); + result.meta = meta; + result.kwds = ["final"]; + result.imports = []; + result.isInterface = false; + result.name = name; + result.typeParams = null; + result.fields = [funcAsClassField]; + result.implement = []; + result.extend = null; + result.inits = []; + return result; } function writeNamespaceDef(n : NamespaceDef) @@ -3424,69 +4388,99 @@ class Writer return out; } - function openb() : String - { - if (cfg.bracesOnNewline) - return cfg.newlineChars + indent() + "{"; - else + function openb() : String { + if (cfg.bracesOnNewline) { + var s:String = cfg.newlineChars + indent() + "{"; + if (pendingTailComment != null) { + s = pendingTailComment + s; + pendingTailComment = null; + } + return s; + } else { return " {"; + } } - inline function closeb() : String { - return cfg.newlineChars + indent() + "}"; + function closeb() : String { + var s:String = cfg.newlineChars + indent() + "}"; + if (pendingTailComment != null) { + s = pendingTailComment + s; + pendingTailComment = null; + } + return s; } - function write(s : String) - { + function write(s : String):Void { //set line as dirty if string contains something other //than whitespace/indent - if (!containsOnlyWhiteSpace(s) && s != cfg.indentChars) + if (!containsOnlyWhiteSpace(s) && s != cfg.indentChars) { lineIsDirty = true; - + } o.writeString(s); } /** write Haxe "allow" metadata using current package */ - function writeAllow() { - write("@:allow("+properCaseA(this.pack,false).join(".")+")"); + function writeAllow():Void { + write("@:allow(" + properCaseA(this.pack, false).join(".") + ")"); writeNL(); writeIndent(); } + private inline function isHaxeCodeComment(s:String):Bool { + return s.indexOf("haxe:") == 2; + } + /** * Writing for block and line comment. If * comment written on dirty line (not first text on line), * add extra whitespace before and after comment */ - function writeComment(s : String) - { - if (lineIsDirty) - s = " " + s + " "; - - write(s); + function writeComment(s : String, blockComment : Bool):Void { + if (blockComment) { + if (isHaxeCodeComment(s)) { + write(s.substring(7, s.length - 2)); + } else if (lineIsDirty) { + write(" " + s + " "); + } else { + write(s); + } + } else { + if (pendingTailComment == null) { + pendingTailComment = s; + } else { + pendingTailComment += " " + s; + } + } } - function writeIndent(s = "") - { + function writeIndent(s = ""):Void { write(indent() + s); } - function writeLine(s = "") - { + function writeLine(s = ""):Void { lineIsDirty = false; - - write(indent() + s + cfg.newlineChars); + if (pendingTailComment != null) { + write(indent() + s + pendingTailComment + cfg.newlineChars); + pendingTailComment = null; + } else { + write(indent() + s + cfg.newlineChars); + } } - function writeNL(s = "") - { - lineIsDirty = false; //reset line dirtyness - + function writeNL(s = ""):Void { write(s); + if (pendingTailComment != null) { + if (s == "") + write(pendingTailComment); + else + write(" " + pendingTailComment); + pendingTailComment = null; + } write(cfg.newlineChars); + lineIsDirty = false; } - inline function writeStartStatement() { + inline function writeStartStatement():Void { if(cfg.bracesOnNewline) { writeNL(); writeIndent(); @@ -3495,7 +4489,7 @@ class Writer } } - inline function writeCloseStatement() { + inline function writeCloseStatement():Void { if(cfg.bracesOnNewline) { write(")"); writeNL(); @@ -3505,7 +4499,7 @@ class Writer } } - function writeFinish(cond:BlockEnd) { + function writeFinish(cond:BlockEnd):Void { switch(cond) { case None: case Semi: write(";"); @@ -3524,11 +4518,12 @@ class Writer for(field in genType.fields) { writeNL(); writeIndent(cfg.indentChars); - write("var "+field.name + " : " + field.t + ";"); + write("var " + field.name + ": " + field.t + ";"); } writeNL(); write("}"); writeNL(); + writeNL(); } } @@ -3537,6 +4532,22 @@ class Writer return StringTools.trim(s) == ""; } + function extractComments(expr:Expr) : Expr { + return switch(expr) { + case ENL(e2): + var re:Expr = extractComments(e2); + if (re != null) { + return ENL(re); + } + return null; + case ECommented(s,b,t,e2): + var re:Expr = extractComments(e2); + if (re != null) return ENL(re); + return ECommented(s,b,t,null); + default: null; + } + } + /** * Switches output to a string accumulator * @return contents of buffer after calling f() @@ -3558,39 +4569,76 @@ class Writer return b.join(""); } - public function process(program : Program, writer : Output):Map { + public function process(program : Program, output : Output):Map { warnings = new Map(); + var commons:Map = CommonImports.getImports(cfg); + typeImportMap = new Map(); + for (key in commons.keys()) { + typeImportMap.set(key, commons.get(key)); + } + //list of imported types must be reseted for each file, //as only one instance of Writer write all the files imported = []; var defined:Array = []; - for (type in program.typesDefd) + pack = program.pack; + var packWithDot:String = properCaseA(pack, false).join(".") + (program.pack.length > 0 ? "." : ""); + + for (type in program.typesDefd) { defined.push(type.name); + imported.push(packWithDot + type.name); + } - switch(program.defs[0]) { + if (program.defs.length > 0) switch (program.defs[0]) { case CDef(c): for (meta in c.meta) { switch (meta) { - case EImport(v): defined.push(v[v.length - 1]); + case EImport(v): + if (cfg.importExclude != null && cfg.importExclude.indexOf(v.join(".")) != -1) { + continue; + } + defined.push(v[v.length - 1]); default: } } default: } - o = writer; + for (def in program.defs) { + switch(def) { + case CDef(c): + defined.push(c.name); + imported.push(packWithDot + c.name); + default: + } + } + + o = new BytesOutput(); genTypes = program.genTypes; - pack = program.pack; writeComments(program.header); writePackage(program.pack); writeImports(program.imports); writeAdditionalImports(program.pack, program.typesSeen, defined); - writeGeneratedTypes(program.genTypes); + generatedTypesWritten = false; writeDefinitions(program.defs); writeComments(program.footer); + + var rv = untyped o.getBytes().toString(); + rv = StringTools.trim(rv); + var lines:Array = rv.split('\n'); + var counter:Int = lines.length; + var prevEmpty:Bool = false; + for (line in lines) { + var s:String = StringTools.rtrim(line); + output.writeString(s); + if (--counter > 0 && (s != '' || !prevEmpty)) + output.writeString('\n'); + prevEmpty = s == ''; + } + return warnings; } @@ -3600,7 +4648,7 @@ class Writer * warning is affecting, so that the porter can more easily determine * the fix. */ - public static function showWarnings(allWarnings : Map>) { + public static function showWarnings(allWarnings : Map>):Void { var wke : Map> = new Map(); // warning->files for(filename in allWarnings.keys()) { for(errname in allWarnings.get(filename).keys()) { @@ -3627,11 +4675,12 @@ class Writer case "as Class": println("WARNING: These files casted using 'obj as Class', which may produce incorrect code"); case "as number", "as int": println("WARNING: "+warn+" casts in these files"); case "as array": println("ERROR: type must be determined for 'as array' cast for:"); + case "as Function": println("WARNING: These files had a cast to Function which was ommited due to haxe type system"); case "EDelete": println("FATAL: Files will not compile due to 'delete' keyword. See README"); default: println("WARNING: " + warn); } for(f in a) - println("\t"+f); + println("\t" + f); } } } @@ -3642,9 +4691,13 @@ class Writer public static function properCaseA(path:Array, hasClassName:Bool):Array { var result = []; + if (path.length == 3 && path[0] == "haxe" && path[1] == "Constraints" && path[2] == "Function") { + return path; + } for(i in 0...path.length) { if(hasClassName && i == path.length - 1) - result[i] = removeUnderscores(path[i]); + //result[i] = removeUnderscores(path[i]); removed by no reason + result[i] = path[i]; else { var p = path[i]; result[i] = p.charAt(0).toLowerCase() + p.substr(1); @@ -3671,4 +4724,4 @@ class Writer ).array().join(""); } -} +} \ No newline at end of file diff --git a/src/as3hx/WriterImports.hx b/src/as3hx/WriterImports.hx new file mode 100644 index 0000000..f2c4d01 --- /dev/null +++ b/src/as3hx/WriterImports.hx @@ -0,0 +1,115 @@ +package as3hx; +import as3hx.As3.ClassDef; +import as3hx.As3.Program; + +/** + * ... + * @author xmi + */ +class WriterImports +{ + public static function getImportWithMap(ident:String, allClasses:Map, imports:Map, currentPackage:String = null):String { + if (imports.exists(ident)) return imports.get(ident); + if (currentPackage != null) { + var currentPackageIdent:String = currentPackage + "." + ident; + if (allClasses.exists(currentPackageIdent)) { + return currentPackageIdent; + } + } + return null; + } + + public static function getImport(ident:String, cfg:Config, allClasses:Map, c:ClassDef, program:Program, currentPackage:String = null):String { + for (e in c.meta) { + switch(e) { + case EImport(i): + var name:String = getImportString(cfg, i); + if (cfg.importExclude != null && cfg.importExclude.indexOf(name) != -1) { + continue; + } + if (i[i.length - 1] == ident && allClasses.exists(name)) return name; + default: + } + } + if (program != null) { + for (i in program.imports) { + var name:String = getImportString(cfg, i); + if (cfg.importExclude != null && cfg.importExclude.indexOf(name) != -1) { + continue; + } + if (i[i.length - 1] == ident && allClasses.exists(name)) return name; + } + for (e in program.header) { + switch(e) { + case EImport(i): + var name:String = getImportString(cfg, i); + if (cfg.importExclude != null && cfg.importExclude.indexOf(name) != -1) { + continue; + } + if (i[i.length - 1] == ident && allClasses.exists(name)) return name; + default: + } + } + } + var commons:Map = CommonImports.getImports(cfg); + if (commons.exists(ident)) return commons.get(ident); + + if (currentPackage == null && program != null) { + currentPackage = program.pack.join("."); + } + if (currentPackage != null) { + var currentPackageIdent:String = currentPackage + "." + ident; + if (allClasses.exists(currentPackageIdent)) { + return currentPackageIdent; + } + } + return null; + } + + public static function getImports(p:Program, cfg:Config, c:ClassDef):Map { + var map:Map = new Map(); + var common:Map = CommonImports.getImports(cfg); + for (key in common.keys()) { + map.set(key, common.get(key)); + } + if (p != null) { + for (i in p.imports) { + addImport(cfg, map, i); + } + for (e in p.header) { + switch(e) { + case EImport(i): + addImport(cfg, map, i); + default: + } + } + } + + for (e in c.meta) { + switch(e) { + case EImport(i): + addImport(cfg, map, i); + default: + } + } + return map; + } + + static function addImport(cfg:Config, map:Map, i:Array):Void { + var type:String = getImportString(cfg, i); + if (cfg.importExclude != null && cfg.importExclude.indexOf(type) != -1) { + return; + } + map.set(i[i.length - 1], type); + } + + static function getImportString(cfg:Config, i : Array) { + if (i[0] == "flash") { + i[0] = cfg.flashTopLevelPackage; + return i.join("."); + } else { + return Writer.properCaseA(i, true).join("."); + } + } + +} \ No newline at end of file diff --git a/src/as3hx/WriterUtils.hx b/src/as3hx/WriterUtils.hx new file mode 100644 index 0000000..d444ce8 --- /dev/null +++ b/src/as3hx/WriterUtils.hx @@ -0,0 +1,50 @@ +package as3hx; +import as3hx.As3.Expr; +import as3hx.As3.SwitchCase; +import as3hx.As3.SwitchDefault; +import as3hx.As3.T; +import as3hx.RebuildUtils.RebuildResult; +import neko.Lib; + +class WriterUtils +{ + /** + * In AS3 local function declarations are available all throw parent function, but in haxe we need to declare them before usage. + * So we need to relocate local functions that they preceded the usage expressions. + * To keep result code more consistent let's move all function declarations to the begining of parent function. + **/ + public static function moveFunctionDeclarationsToTheTop(expressions:Array):Array { + var localFunctionDeclarations:Array = []; + function lookUpForLocalFunctions(e:Expr):RebuildResult { + switch(e) { + case EBinop(op, e1, e2, newLineAfterOp): + if (op == "=") { + switch(e2) { + case EFunction(f, name): + return RebuildResult.RSkip; + default: + } + } + return RebuildResult.RSkip; + case EFunction(f, name): + if (name != null) { + localFunctionDeclarations.push(ENL(e)); + return RebuildResult.REmpty; + } else { + return RebuildResult.RSkip; + } + default: + } + return null; + } + var expressionsWithoutFunctions:Array = RebuildUtils.rebuildArray(expressions, lookUpForLocalFunctions); + if (expressionsWithoutFunctions == null) { + expressionsWithoutFunctions = expressions; + } + return localFunctionDeclarations.concat(expressionsWithoutFunctions); + } + + public static function replaceForLoopsWithWhile(expressions:Array):Array { + return new ForLoopRebuild().replaceForLoopsWithWhile(expressions); + } +} \ No newline at end of file diff --git a/src/as3hx/parsers/ClassParser.hx b/src/as3hx/parsers/ClassParser.hx index d686332..84bf51c 100644 --- a/src/as3hx/parsers/ClassParser.hx +++ b/src/as3hx/parsers/ClassParser.hx @@ -18,6 +18,32 @@ class ClassParser { var parseImport = ImportParser.parse.bind(tokenizer, cfg); var cname = tokenizer.id(); + var typeParams = null; + + var next = tokenizer.token(); + switch (next) { + case TCommented(s, isBlock, t): + var type:T = OverrideTypeComment.extractType(cname, s, types); + if (type == null) { + tokenizer.add(next); + } else { + switch(type) { + case TPath(p): + cname = p.join("."); + var i:Int = cname.indexOf("<"); + if (i != -1) { + typeParams = cname.substring(i + 1, cname.length - 1); + cname = cname.substr(0, i); + } + tokenizer.add(t); + default: + tokenizer.add(next); + } + } + default: + tokenizer.add(next); + } + var classMeta = meta; var imports = []; meta = []; @@ -61,7 +87,10 @@ class ClassParser { pf = function(included:Bool,inCondBlock:Bool) { while( true ) { // check for end of class - if( ParserUtils.opt2(tokenizer, TBrClose, meta) ) break; + + if ( ParserUtils.opt2(tokenizer, TBrClose, meta) ) { + break; + } var kwds = []; // parse all comments and metadata before next field while( true ) { @@ -95,7 +124,7 @@ class ClassParser { switch( t ) { case TId(id): switch( id ) { - case "public", "static", "private", "protected", "override", "internal", "final": kwds.push(id); + case "public", "static", "private", "protected", "override", "internal", "final", "mx_internal": kwds.push(id); case "const": kwds.push(id); do { @@ -182,19 +211,19 @@ class ClassParser { pf(false, true); case TCommented(s,b,t): - f(t); + f(t); case TNL(t): meta.push(ENL(null)); - f(t); + f(t); default: tokenizer.add(t); pf(false, false); - } + } } f(t); - + condVars.pop(); Debug.closeDebug("end conditional compilation: " + ns + "::" + id, tokenizer.line); break; @@ -226,16 +255,19 @@ class ClassParser { for(m in meta) { switch(m) { case ECommented(s,b,t,e): - if(ParserUtils.uncommentExpr(m) != null) + if(ParserUtils.removeNewLineExpr(m) != null) throw "Assert error: " + m; var a = ParserUtils.explodeCommentExpr(m); for(i in a) { - switch(i) { - case ECommented(s,b,t,e): - fields.push({name:null, meta:[ECommented(s,b,false,null)], kwds:[], kind:FComment, condVars:[]}); - default: - throw "Assert error: " + i; - } + fields.push({name:null, meta:[i], kwds:[], kind:FComment, condVars:[]}); + //switch(i) { + //case ENL(null): + //fields.push({name:null, meta:[i], kwds:[], kind:FComment, condVars:[]}); + //case ECommented(s,b,t,e): + //fields.push({name:null, meta:[i], kwds:[], kind:FComment, condVars:[]}); + //default: + //throw "Assert error: " + i; + //} } case ENL(_): default: @@ -243,17 +275,18 @@ class ClassParser { } } Debug.closeDebug("parseClass(" + cname+") finished", tokenizer.line); - return { - meta : classMeta, - kwds : kwds, - imports : imports, - isInterface : isInterface, - name : cname, - fields : fields, - implement : impl, - extend : extend, - inits : inits - }; + var result:ClassDef = new ClassDef(); + result.meta = classMeta; + result.kwds = kwds; + result.imports = imports; + result.isInterface = isInterface; + result.name = cname; + result.typeParams = typeParams; + result.fields = fields; + result.implement = impl; + result.extend = extend; + result.inits = inits; + return result; } public static function parseVar(tokenizer, types:Types, cfg:Config, kwds, meta, condVars:Array) : ClassField { @@ -266,6 +299,7 @@ class ClassParser { var t = null, val = null; if( ParserUtils.opt(tokenizer, TColon) ) t = parseType(); + if( ParserUtils.opt(tokenizer, TOp("=")) ) val = parseExpr(false); @@ -276,7 +310,7 @@ class ClassParser { kind : FVar(t, val), condVars : condVars }; - + var genType = ParserUtils.generateTypeIfNeeded(rv); if (genType != null) types.gen.push(genType); @@ -285,7 +319,7 @@ class ClassParser { return rv; } - public static function parseFun(tokenizer, types:Types, cfg, kwds:Array,meta,condVars:Array, isInterface:Bool) : ClassField { + public static function parseFun(tokenizer, types:Types, cfg, kwds:Array, meta:Array, condVars:Array, isInterface:Bool) : ClassField { var parseFunction = FunctionParser.parse.bind(tokenizer, types, cfg); Debug.openDebug("parseClassFun(", tokenizer.line); @@ -301,9 +335,15 @@ class ClassParser { name = tokenizer.id(); } } + var t = tokenizer.token(); + switch (t) { + case TCommented(s, isBlock, t): + meta.push(ECommented(s, isBlock, true, null)); + default: + } + tokenizer.add(t); Debug.dbgln(Std.string(kwds) + " " + name + ")", tokenizer.line, false); var f = parseFunction(isInterface); - tokenizer.end(); Debug.closeDebug("end parseClassFun()", tokenizer.line); return { meta : meta, diff --git a/src/as3hx/parsers/DefinitionParser.hx b/src/as3hx/parsers/DefinitionParser.hx index 4e31c60..a1877d9 100644 --- a/src/as3hx/parsers/DefinitionParser.hx +++ b/src/as3hx/parsers/DefinitionParser.hx @@ -16,16 +16,16 @@ class DefinitionParser { while( true ) { var id = tokenizer.id(); switch( id ) { - case "public", "internal", "final", "dynamic": kwds.push(id); + case "public", "internal", "final", "dynamic", "mx_internal": kwds.push(id); case "use": parseUse(); continue; case "class": - var c = parseClass(path, filename, kwds,meta,false); + var c = parseClass(path, filename, kwds, meta, false); types.defd.push(c); return CDef(c); case "interface": - var c = parseClass(path, filename, kwds,meta,true); + var c = parseClass(path, filename, kwds, meta, true); types.defd.push(c); return CDef(c); case "function": diff --git a/src/as3hx/parsers/ExprParser.hx b/src/as3hx/parsers/ExprParser.hx index d6a604f..7d1a3b8 100644 --- a/src/as3hx/parsers/ExprParser.hx +++ b/src/as3hx/parsers/ExprParser.hx @@ -17,10 +17,12 @@ class ExprParser { var tk = tokenizer.token(); Debug.dbgln("parseExpr(" + tk + ")", tokenizer.line); - switch( tk ) { + switch ( tk ) { + case TSemicolon: + return parseExpr(funcStart); case TId(id): var e = parseStructure(id); - if(e == null) + if (e == null) e = EIdent(ParserUtils.escapeName(id)); return switch(e) { case EIf(_,_,_) | EFor(_,_,_,_) | EForIn(_,_,_) | EForEach(_,_,_) | EWhile(_,_,_) | ESwitch(_,_,_): e; @@ -34,9 +36,9 @@ class ExprParser { return parseExprNext(EParent(e), 0); case TBrOpen: tk = tokenizer.token(); - + Debug.dbgln("parseExpr: " + tk, tokenizer.line); - + switch(ParserUtils.removeNewLine(tk, false)) { case TBrClose: if(funcStart) return EBlock([]); @@ -45,7 +47,7 @@ class ExprParser { var tk2 = tokenizer.token(); tokenizer.add(tk2); tokenizer.add(tk); - switch( tk2 ) { + switch( ParserUtils.removeNewLine(tk2) ) { case TColon: return parseExprNext(parseObject(), 0); default: @@ -55,44 +57,27 @@ class ExprParser { } var a = new Array(); - //check for corner case, block contains only comments and - //newlines. In this case, get all comments and add them to - //content of block expression - if (ParserUtils.uncomment(ParserUtils.removeNewLine(tk)) == TBrClose) { - var ta = ParserUtils.explodeComment(tk); - for (t in ta) { - switch (t) { - case TCommented(s,b,t): a.push(ECommented(s,b,false, null)); - case TNL(t): a.push(ENL(null)); - default: - } - } + while(tokenizer.peek() != TBrClose) { + a.push(parseFullExpr()); } - - while(!ParserUtils.opt(tokenizer, TBrClose)) { - var e = parseFullExpr(); - a.push(e); + + var ta = ParserUtils.explodeComment(tokenizer.token()); + for (i in 0...ta.length) { + var t = ta[i]; + switch (t) { + case TCommented(s,b,t): a.push(ECommented(s,b,false, null)); + case TNL(t) if (i != ta.length - 2): //last TNL in block is redundant, previous should be kept + a.push(ENL(null)); + default: + } } return EBlock(a); case TOp(op): - if(op.charAt(0) == "/") { - var str = op.substr(1); - var prevChar = 0; - var c = tokenizer.nextChar(); - while(c != "/".code || prevChar == "\\".code) { - prevChar = c; - str += String.fromCharCode(c); - c = tokenizer.nextChar(); - } - c = tokenizer.nextChar(); - var opts = ""; - while( c >= "a".code && c <= "z".code ) { - opts += String.fromCharCode(c); - c = tokenizer.nextChar(); - } - tokenizer.pushBackChar(c); - return parseExprNext(ERegexp(str, opts), 0); + if (op.charAt(0) == "/") { + return parseExprNext(parseERegexp(tokenizer, op), 0); } + if (op == "+") // not valid unop prefix in haxe + return parseExpr(false); for(x in tokenizer.unopsPrefix) if(x == op) return ParserUtils.makeUnop(op, parseExpr(false)); @@ -127,7 +112,6 @@ class ExprParser { var parseExprList = parseList.bind(tokenizer, types, cfg); var parseType= TypeParser.parse.bind(tokenizer, types, cfg); var parseE4X = E4XParser.parse.bind(tokenizer, types, cfg); - var tk = tokenizer.token(); Debug.dbgln("parseExprNext("+e1+") ("+tk+")", tokenizer.line); switch( tk ) { @@ -157,7 +141,7 @@ class ExprParser { switch(i) { case "public": return parseExprNext(ECommented("/* AS3HX WARNING namespace modifier " + i + ":: */", true, false, null), 0); - default: + default: } tk = tokenizer.token(); switch(tk) { @@ -166,17 +150,22 @@ class ExprParser { // this is a user supplied conditional compilation variable Debug.openDebug("conditional compilation: " + i + "::" + id, tokenizer.line); switch (tokenizer.peek()) { + case TSemicolon: + Debug.closeDebug("end conditional compilation: " + i + "::" + id, tokenizer.line); + return ECondComp(i + "_" + id, null, null); case TPClose: Debug.closeDebug("end conditional compilation: " + i + "::" + id, tokenizer.line); //corner case, the conditional compilation is within an "if" statement //example if(CONFIG::MY_CONFIG) { //code block } //normal "if" statement parsing will take care of it return ECondComp(i + "_" + id, null, null); - default: + default: var e = parseExpr(false); Debug.closeDebug("end conditional compilation: " + i + "::" + id, tokenizer.line); return ECondComp(i + "_" + id, e, null); } + } else if (i == 'mx_internal') { + return parseExprNext(ECommented('// ', true, false, EIdent(i + "::" + id)), 0); } else switch(tokenizer.peek()) { case TBrOpen: // functions inside a namespace return parseExprNext(ECommented("/* AS3HX WARNING namespace modifier " + i + "::"+id+" */", true, false, null), 0); @@ -196,19 +185,28 @@ class ExprParser { switch(ParserUtils.uncomment(ParserUtils.removeNewLine(tk))) { case TId(id): field = ParserUtils.escapeName(id); - if( ParserUtils.opt(tokenizer, TNs) ) - field = field + "::" + tokenizer.id(); + if( ParserUtils.opt(tokenizer, TNs) ) { + return parseExprNext(EField(ENamespaceAccess(e1, field), tokenizer.id()), 0); + } case TOp(op): - if( op != "<" || switch(e1) { case EIdent(v): v != "Vector" && v != "Dictionary"; default: true; } ) ParserUtils.unexpected(tk); + if ( op != "<" ) ParserUtils.unexpected(tk); + var parseDictionaryTypes:Bool = false; + var parseVectorType:Bool = false; + switch(e1) { + case EIdent(v): + parseVectorType = v == "Vector"; + parseDictionaryTypes = v == "Dictionary" && cfg.dictionaryToHash && cfg.useAngleBracketsNotationForDictionaryTyping; + if (!parseVectorType && !parseDictionaryTypes) { + ParserUtils.unexpected(tk); + } + default: + ParserUtils.unexpected(tk); + } + var t = parseType(); - var v = switch(e1) { - case EIdent(v): v; - default: null; - } - //for Dictionary, expected syntax is "Dictionary." - if (v == "Dictionary" && cfg.dictionaryToHash) { + if (parseDictionaryTypes) { tokenizer.ensure(TComma); tokenizer.id(); } @@ -268,7 +266,11 @@ class ExprParser { return switch(e2) { case ETernary(cond, te1, te2): ETernary(ParserUtils.makeBinop(tokenizer, s, e1, cond, pendingNewLines != 0), te1, te2); - default: ParserUtils.makeBinop(tokenizer, s, e1, e2, pendingNewLines != 0); + case EIdent(v) if (s != "in"): + types.seen.push(TPath([v])); + ParserUtils.makeBinop(tokenizer, s, e1, e2, pendingNewLines != 0); + default: + ParserUtils.makeBinop(tokenizer, s, e1, e2, pendingNewLines != 0); } default: if (pendingNewLines != 0) { @@ -289,18 +291,25 @@ class ExprParser { var addToken : Token->Void = function(tk) { if (pendingNewLines != 0) tokenizer.add(TNL(tk)); - else + else tokenizer.add(tk); } switch (t) { case TPClose: addToken(tk); - return e1; - case TCommented(s,b,t): - addToken(t); - return ECommented(s,b,true, parseExprNext(e1, ++pendingNewLines)); - default: + return e1; + case TCommented(s,b,t1): + addToken(t1); + var next = parseExprNext(e1, ++pendingNewLines); + if (next != e1) { + return ECommented(s,b,true,next); + } else { + tokenizer.token(); + addToken(t); + return e1; + } + default: tokenizer.add(t); return parseExprNext(e1, ++pendingNewLines); } @@ -308,7 +317,7 @@ class ExprParser { case TCommented(s,b,t): tokenizer.add(t); return ECommented(s,b,true, parseExprNext(e1, 0)); - + default: Debug.dbgln("parseExprNext stopped at " + tk, tokenizer.line); tokenizer.add(tk); @@ -320,13 +329,13 @@ class ExprParser { var parseExpr = parse.bind(tokenizer, types, cfg); Debug.dbgln("parseFullExpr()", tokenizer.line); var e = parseExpr(false); - if( ParserUtils.opt(tokenizer, TColon) ) { + if ( ParserUtils.opt(tokenizer, TColon) ) { switch( e ) { case EIdent(l): e = ELabel(l); default: tokenizer.add(TColon); } } - if( !ParserUtils.opt(tokenizer, TComma) ) + if ( !ParserUtils.opt(tokenizer, TComma) ) tokenizer.end(); return e; } @@ -334,21 +343,21 @@ class ExprParser { public static function parseList(tokenizer:Tokenizer, types:Types, cfg:Config, etk:Token) : Array { var parseExpr = parse.bind(tokenizer, types, cfg); Debug.dbgln("parseExprList()", tokenizer.line); - - var args = new Array(); + var args = []; var f = function(t) { - if(args.length == 0) return; - args[args.length-1] = ParserUtils.tailComment(args[args.length-1], t); + if (args.length == 0) return; + args[args.length - 1] = ParserUtils.tailComment(args[args.length - 1], t); } if (ParserUtils.opt(tokenizer, etk)) return args; var exprToToken = new Map(); - while(true) { + while (true) { var tk = tokenizer.token(); tokenizer.add(tk); var expr = parseExpr(false); exprToToken.set(expr, tk); args.push(expr); tk = tokenizer.token(); + switch(tk) { case TComma: var ntk = tokenizer.token(); @@ -393,4 +402,34 @@ class ExprParser { } return args.filter(function(e) return !e.match(ENL(null))); } + + public static function parseERegexp(tokenizer:Tokenizer, op:String):Expr { + var str = op.substr(1); + var prevChar = 0; + var c = tokenizer.nextChar(); + var escapedChar:Bool = false; + var depth:Int = 0; + var inSquareBrackets:Bool = false; + while (depth != 0 || inSquareBrackets || c != "/".code || escapedChar) { + str += String.fromCharCode(c); + if (!escapedChar) { + if (c == "(".code) depth++; + if (c == ")".code) depth--; + if (c == "[".code && !inSquareBrackets) inSquareBrackets = true; + if (c == "]".code && inSquareBrackets) inSquareBrackets = false; + escapedChar = c == "\\".code; + } else { + escapedChar = false; + } + c = tokenizer.nextChar(); + } + c = tokenizer.nextChar(); + var opts = ""; + while( c >= "a".code && c <= "z".code ) { + opts += String.fromCharCode(c); + c = tokenizer.nextChar(); + } + tokenizer.pushBackChar(c); + return ERegexp(str, opts); + } } diff --git a/src/as3hx/parsers/FunctionParser.hx b/src/as3hx/parsers/FunctionParser.hx index 0edf307..4ba9413 100644 --- a/src/as3hx/parsers/FunctionParser.hx +++ b/src/as3hx/parsers/FunctionParser.hx @@ -50,7 +50,7 @@ class FunctionParser { var t = null, val = null; expressions.push(EIdent(name)); - if( ParserUtils.opt(tokenizer, TColon) ) { // ":" + if( ParserUtils.opt(tokenizer, TColon) ) { // ":" t = parseType(); //arguments type expressions.push(ETypedExpr(null, t)); } @@ -61,10 +61,12 @@ class FunctionParser { } f.args.push( { name : name, t : t, val : val, exprs:expressions } ); - expressions = []; // reset for next argument - if( ParserUtils.opt(tokenizer, TPClose) ) // ")" end of arguments + if( ParserUtils.opt2(tokenizer, TPClose, expressions) ) // ")" end of arguments break; + + expressions = []; // reset for next argument + tokenizer.ensure(TComma); case TCommented(s,b,t): //comment in between arguments @@ -75,7 +77,7 @@ class FunctionParser { tokenizer.add(t); expressions.push(ENL(null)); - default: + default: } } @@ -86,20 +88,20 @@ class FunctionParser { //newlines var retExpressions:Array = []; - //parse return type + //parse return type if( ParserUtils.opt(tokenizer, TColon) ) { var t = parseType(); retExpressions.push(ETypedExpr(null, t)); f.ret.t = t; } - - //parse until '{' or ';' (for interface method) + + //parse until '{' or ';' (for interface method) while (true) { var tk = tokenizer.token(); switch (tk) { case TNL(t): //parse new line before '{' or ';' tokenizer.add(t); - + //corner case, in AS3 interface method don't //have to end with a ";". So If we encounter a //newline after the return definition, we assume @@ -112,22 +114,26 @@ class FunctionParser { retExpressions.push(ENL(null)); } - case TCommented(s,b,t): //comment before '{' or ';' + case TCommented(s,b,t): //comment before '{' or ';' tokenizer.add(t); - retExpressions.push(ParserUtils.makeECommented(tk, null)); + retExpressions.push(ParserUtils.makeECommented(tk, null)); - case TBrOpen, TSemicolon: //end of method return + case TBrOpen: //end of method return tokenizer.add(tk); f.ret.exprs = retExpressions; break; + case TSemicolon: //end of method return + f.ret.exprs = retExpressions; + break; + default: } } - if(tokenizer.peek() == TBrOpen) { + if (tokenizer.peek() == TBrOpen) { f.expr = parseExpr(true); - switch(ParserUtils.removeNewLineExpr(f.expr)) { + switch (ParserUtils.removeNewLineExpr(f.expr)) { case EObject(fl): if(fl.length == 0) { f.expr = EBlock([]); @@ -140,8 +146,11 @@ class FunctionParser { throw "unexpected " + Std.string(f.expr); } } + tokenizer.end(); Debug.closeDebug("end parseFun()", tokenizer.line); - rebuildLocalFunctions(f); + if (cfg.rebuildLocalFunctions) { + rebuildLocalFunctions(f); + } return f; } @@ -157,7 +166,7 @@ class FunctionParser { f : f }; } - + inline static function rebuildLocalFunctions(f:Function) { switch(f.expr) { case EBlock(e): diff --git a/src/as3hx/parsers/ObjectParser.hx b/src/as3hx/parsers/ObjectParser.hx index 5a00e86..8b13eb4 100644 --- a/src/as3hx/parsers/ObjectParser.hx +++ b/src/as3hx/parsers/ObjectParser.hx @@ -34,21 +34,27 @@ class ObjectParser { } tokenizer.ensure(TColon); fl.push({ name : id, e : parseExpr(false) }); - tk = tokenizer.token(); - switch(tk) { - case TCommented(s,b,e): - var o = fl[fl.length-1]; - o.e = ParserUtils.tailComment(o.e, tk); - default: - } - switch( ParserUtils.uncomment(tk) ) { - case TBrClose: - break; - case TComma: - null; - default: - ParserUtils.unexpected(tk); + + var parseNextField:Bool = false; + var finishObject:Bool = false; + while (!parseNextField && !finishObject) { + tk = tokenizer.token(); + switch(tk) { + case TCommented(s,b,e): + var o = fl[fl.length-1]; + o.e = ParserUtils.tailComment(o.e, tk); + tokenizer.add(e); + case TNL(e): + tokenizer.add(e); + case TBrClose: + finishObject = true; + case TComma: + parseNextField = true; + default: + ParserUtils.unexpected(tk); + } } + if (finishObject) break; } var rv = parseExprNext(EObject(fl), 0); Debug.closeDebug("parseObject() -> " + rv, tokenizer.line); diff --git a/src/as3hx/parsers/OverrideTypeComment.hx b/src/as3hx/parsers/OverrideTypeComment.hx new file mode 100644 index 0000000..fc46049 --- /dev/null +++ b/src/as3hx/parsers/OverrideTypeComment.hx @@ -0,0 +1,41 @@ +package as3hx.parsers; + +import as3hx.As3.T; +import as3hx.Parser.Types; + +/** + * ... + * @author + */ +class OverrideTypeComment { + public static function extractType(baseString:String, s:String, types:Types):T { + if (s.indexOf("haxe:") == 2) { // /*haxe: + var arg = s.substring(7, s.length - 2); + var typeArray:Array = null; + for (a in Typer.getTypeParams(arg)) { + types.seen.push(TPath(a)); + if (typeArray == null) { + typeArray = a; + } + } + var l:Int = typeArray.join(".").length; + if (l < arg.length) { + typeArray = typeArray.copy(); + } + typeArray[typeArray.length - 1] += arg.substring(typeArray.join(".").length); + return TPath(typeArray); + } else if (baseString != null && s.indexOf("<") == 2 && s.lastIndexOf(">") == s.length - 3) { + var arg = s.substring(2, s.length - 2); + var typeArray:Array = null; + for (a in Typer.getTypeParams(arg)) { + types.seen.push(TPath(a)); + if (typeArray == null) { + typeArray = a; + } + } + return TPath([baseString + arg]); + } else { + return null; + } + } +} \ No newline at end of file diff --git a/src/as3hx/parsers/PackageNameParser.hx b/src/as3hx/parsers/PackageNameParser.hx index 3d6ae68..90848e7 100644 --- a/src/as3hx/parsers/PackageNameParser.hx +++ b/src/as3hx/parsers/PackageNameParser.hx @@ -13,7 +13,7 @@ class PackageNameParser { case TDot: tk = tokenizer.token(); switch(tk) { - case TId(id): a.push(id); + case TId(id): a.push(ParserUtils.escapeName(id)); default: ParserUtils.unexpected(tk); } default: diff --git a/src/as3hx/parsers/ProgramParser.hx b/src/as3hx/parsers/ProgramParser.hx index 0e6e71f..f8b0cff 100644 --- a/src/as3hx/parsers/ProgramParser.hx +++ b/src/as3hx/parsers/ProgramParser.hx @@ -22,15 +22,19 @@ class ProgramParser { // look for first 'package' var tk = tokenizer.token(); var a = ParserUtils.explodeComment(tk); + var closed = false; + var defs:Array = []; - for(t in a) { + for (t in a) { switch(t) { case TId(s): - if(s != "package") + // if (s == 'mx_internal') continue; + if (s != "package") { ParserUtils.unexpected(t); - if(ParserUtils.opt(tokenizer, TBrOpen)) - pack = [] - else { + } + if (ParserUtils.opt(tokenizer, TBrOpen)) { + pack = []; + } else { pack = parsePackageName(); tokenizer.ensure(TBrOpen); } @@ -38,16 +42,62 @@ class ProgramParser { if(t != null) throw "Assert error " + Tokenizer.tokenString(t); header.push(ECommented(s,b,false,null)); case TNL(t): header.push(ENL(null)); - default: ParserUtils.unexpected(t); + case TBkOpen: + var tdef = tokenizer.token(); + var cname = switch tdef { + case TId(s): s; + case _: throw 'error'; + } + var t = tokenizer.token(); + if (t != TPOpen) { + ParserUtils.unexpected(t); + continue; + } + + var cdef = new ClassDef(); + cdef.imports = []; + cdef.isInterface = false; + cdef.typeParams = null; + cdef.implement = []; + cdef.extend = null; + cdef.inits = []; + cdef.meta = []; + cdef.kwds = []; + cdef.name = cname; + cdef.fields = []; + while (true) { + var name = switch (tokenizer.token()) { + case TId(s): s; + case _: throw 'error'; + } + var op = tokenizer.token(); + var value = switch (tokenizer.token()) { + case TConst(s): s; + case _: throw 'error'; + } + var dm = tokenizer.token(); + cdef.fields.push({meta: [], kwds: ['public', 'static'], name: name, kind: FVar(TPath(['String']), EConst(value)), condVars: []}); + switch dm { + case TComma: + case _: break; + } + } + defs.push(CDef(cdef)); + var close = tokenizer.token(); + if (close != TBkClose) { + ParserUtils.unexpected(close); + continue; + } + closed = true; + default: + ParserUtils.unexpected(t); } } // parse package var imports = []; var inits : Array = []; - var defs = []; var meta : Array = []; - var closed = false; var inNamespace = false; var inCondBlock = false; var outsidePackage = false; @@ -55,7 +105,7 @@ class ProgramParser { var pf : Bool->Void = null; pf = function(included:Bool) { - while( true ) { + while ( true ) { var tk = tokenizer.token(); switch( tk ) { case TBrClose: // } @@ -73,10 +123,10 @@ class ProgramParser { continue; } case TBrOpen: // { - if(inNamespace) + if (inNamespace) continue; // private classes outside of first package {} - if( !closed ) { + if ( !closed ) { ParserUtils.unexpected(tk); } closed = false; @@ -101,7 +151,7 @@ class ProgramParser { if (!outsidePackage) { meta.push(EImport(impt)); } - //coner case : import for AS3 private class, for those, + //corner case : import for AS3 private class, for those, //need to add them to regular import list or to first //class metadata so that they //get written at the top of file, as in Haxe all imports @@ -112,49 +162,53 @@ class ProgramParser { //no class def available, put in general import list if (defs.length == 0) { imports.push(impt); - } - //else check if can add to first class meta - switch (defs[0]) { - case CDef(c): - //also put the newline preceding the import - //in the first class meta - if (meta.length > 0) { - switch(meta[meta.length - 1]) { - case ENL(e): - if (e == null) { - c.meta.push(meta.pop()); - } - default: + } else { + //else check if can add to first class meta + switch (defs[0]) { + case CDef(c): + //also put the newline preceding the import + //in the first class meta + if (meta.length > 0) { + switch(meta[meta.length - 1]) { + case ENL(e): + if (e == null) { + c.meta.push(meta.pop()); + } + default: + } } - } - //remove extra new line generated for before - //class generation if not first moved import - if (hasOustidePackageMetaImport) { - c.meta.pop(); - c.meta.pop(); - } - - //put the import in the first class meta - c.meta.push(EImport(impt)); + //remove extra new line generated for before + //class generation if not first moved import + if (hasOustidePackageMetaImport) { + c.meta.pop(); + c.meta.pop(); + } + + //put the import in the first class meta + c.meta.push(EImport(impt)); - //add new line before class definition - c.meta.push(ENL(null)); - c.meta.push(ENL(null)); + //add new line before class definition + c.meta.push(ENL(null)); + c.meta.push(ENL(null)); - hasOustidePackageMetaImport = true; + hasOustidePackageMetaImport = true; - //put in regular import list - default: imports.push(impt); + //put in regular import list + default: imports.push(impt); + } } } + for (i in 0...impt.length) { + impt[i] = ParserUtils.escapeName(impt[i]); + } } tokenizer.end(); continue; case "use": parseUse(); continue; - case "final", "public", "class", "internal", "interface", "dynamic", "function": + case "final", "public", "class", "internal", "interface", "dynamic", "function", "mx_internal": inNamespace = false; tokenizer.add(tk); var d = parseDefinition(path, filename, meta); @@ -190,36 +244,44 @@ class ProgramParser { } continue; default: - if(ParserUtils.opt(tokenizer, TNs)) { + if (ParserUtils.opt(tokenizer, TNs)) { var ns : String = id; var t = ParserUtils.uncomment(tokenizer.token()); - switch(t) { + switch (t) { case TId(id2): id = id2; default: ParserUtils.unexpected(t); } - if (Lambda.has(cfg.conditionalVars, ns + "::" + id)) { // this is a user supplied conditional compilation variable Debug.openDebug("conditional compilation: " + ns + "::" + id, tokenizer.line); // condVars.push(ns + "_" + id); - meta.push(ECondComp(ns + "_" + id, null, null)); inCondBlock = true; t = tokenizer.token(); - switch (t) { - case TBrOpen: - pf(false); - default: - tokenizer.add(t); + + var nt = tokenizer.token(); + switch (nt) { + case TNL(TId('import')): + tokenizer.add(nt); pf(false); + case _: + tokenizer.add(nt); + meta.push(ECondComp(ns + "_" + id, null, null)); + + switch (t) { + case TBrOpen, TNL(TBrOpen): + pf(false); + default: + tokenizer.add(t); + pf(false); + } } - // condVars.pop(); + // condVars.pop(); Debug.closeDebug("end conditional compilation: " + ns + "::" + id, tokenizer.line); continue; } else { ParserUtils.unexpected(t); } - } - else if(ParserUtils.opt(tokenizer, TSemicolon)) { + } else if (ParserUtils.opt(tokenizer, TSemicolon)) { // class names without an import statement used // for forcing compilation and linking. inits.push(EIdent(id)); diff --git a/src/as3hx/parsers/StructureParser.hx b/src/as3hx/parsers/StructureParser.hx index 5068ef6..d93a573 100644 --- a/src/as3hx/parsers/StructureParser.hx +++ b/src/as3hx/parsers/StructureParser.hx @@ -30,12 +30,12 @@ class StructureParser { var e1 = parseExpr(false); e1 = f(e1); tokenizer.end(); - var elseExpr = if(ParserUtils.opt(tokenizer, TId("else"), true)) parseExpr(false) else null; - if(elseExpr != null) elseExpr = f(elseExpr); + var elseExpr = if (ParserUtils.opt(tokenizer, TId("else"), true)) parseExpr(false) else null; + if (elseExpr != null) elseExpr = f(elseExpr); switch(cond) { case ECondComp(v, e, e2): - //corner case, the condition is an AS3 preprocessor - //directive, it must contain the block to wrap it + //corner case, the condition is an AS3 preprocessor + //directive, it must contain the block to wrap it //in Haxe #if #end preprocessor directive ECondComp(v, e1, elseExpr); default: @@ -44,15 +44,15 @@ class StructureParser { } case "var", "const": var vars = []; - while( true ) { + while ( true ) { var name = tokenizer.id(), t = null, val = null; name = ParserUtils.escapeName(name); - if( ParserUtils.opt(tokenizer, TColon) ) + if ( ParserUtils.opt(tokenizer, TColon) ) t = parseType(); - if( ParserUtils.opt(tokenizer, TOp("=")) ) + if ( ParserUtils.opt(tokenizer, TOp("=")) ) val = ETypedExpr(parseExpr(false), t); vars.push( { name : name, t : t, val : val } ); - if( !ParserUtils.opt(tokenizer, TComma) ) + if ( !ParserUtils.opt(tokenizer, TComma) ) break; } EVars(vars); @@ -63,10 +63,10 @@ class StructureParser { var e = parseExpr(false); EWhile(econd,e, false); case "for": - if( ParserUtils.opt(tokenizer, TId("each")) ) { + if ( ParserUtils.opt(tokenizer, TId("each")) ) { tokenizer.ensure(TPOpen); var ev = parseExpr(false); - switch(ev) { + switch (ev) { case EBinop(op, e1, e2, n): if(op == "in") { tokenizer.ensure(TPClose); @@ -102,9 +102,10 @@ class StructureParser { EFor(inits, conds, incrs, parseExpr(false)); } case "break": - var label = switch( tokenizer.peek() ) { - case TId(n): tokenizer.token(); n; - default: null; + var t = tokenizer.token(); + var label = switch(ParserUtils.uncomment(t)) { + case TId(n): n; + default: tokenizer.add(t); null; }; EBreak(label); case "continue": EContinue; @@ -125,23 +126,131 @@ class StructureParser { } EReturn(e); case "new": - if(ParserUtils.opt(tokenizer, TOp("<"))) { + if (ParserUtils.opt(tokenizer, TOp("<"))) { // o = new [a,b,c..] var t = parseType(); tokenizer.ensure(TOp(">")); if(tokenizer.peek() != TBkOpen) ParserUtils.unexpected(tokenizer.peek()); + types.seen.push(TVector(t)); ECall(EVector(t), [parseExpr(false)]); } else { + var peek:Token = tokenizer.peek(); + var call:Expr = parseExpr(false); + var firstCall:Expr; + function extractFirstCall(e:Expr):Expr { + switch(e) { + case ECall(e, params): + var re:Expr = extractFirstCall(e); + if (re == null) { + var type; + switch(e) { + case EIdent(v): + type = TPath([v]); + types.seen.push(type); + case EVector(t): + type = TVector(t); + types.seen.push(type); + default: + type = TComplex(e); + } + return firstCall = ENew(type, params); + } else { + return ECall(re, params); + } + case EIdent(v): return null; + case EVector(t): return null; + case ECommented(s, isBlock, isTail, e): + var re:Expr = extractFirstCall(e); + if (re == null) { + return null; + } else if (isBlock && isTail) { + switch(re) { + case ENew(t, params): + var base:T = OverrideTypeComment.extractType(Typer.tstringStatic(t), s, types); + if (base != null) { + return ENew(base, params); + } + + var k:T = null; + var v:T = null; + if (s.indexOf(",") != -1) { + s = s.substring(2, s.length - 2); + var commaIndex:Int = s.indexOf(","); + if (commaIndex != -1) { + k = TPath([Typer.getMapParam(s, 0)]); + v = TPath([Typer.getMapParam(s, 1)]); + } + } + if (k != null && v != null) { + switch(t) { + case TPath(p) if (p.length == 1 && p[0] == "Dictionary"): + types.seen.push(TDictionary(k, v)); + return ENew(TDictionary(k, v), params); + case TDictionary(k,v): + types.seen.push(TDictionary(k, v)); + return ENew(TDictionary(k, v), params); + default: + } + } + default: + } + } + return ECommented(s, isBlock, isTail, re); + case EArray(e, index): + var re:Expr = extractFirstCall(e); + if (re == null) { + return null; + } else { + return EArray(re, index); + } + case EBinop(op, e1, e2, newLineAfterOp): + var re1:Expr = extractFirstCall(e1); + if (re1 == null) { + return null; + } else { + return EBinop(op, re1, e2, newLineAfterOp); + } + case EField(e, f): + var re:Expr = extractFirstCall(e); + if (re == null) { + return null; + } else { + return EField(re, f); + } + default: return null; + } + } + switch (call) { + case EIdent(v): + var type = TPath([v]); + types.seen.push(type); + return ENew(type, []); + case EVector(t): + var type = TVector(t); + types.seen.push(type); + return ENew(type, []); + case EBinop('as', EIdent(v), e2, nl): + var type = TPath([v]); + types.seen.push(type); + return EBinop('as', ENew(type, []), e2, nl); + default: + var result:Expr = extractFirstCall(call); + if (firstCall != null) { + return result; + } else { + ParserUtils.unexpected(peek); + } + } var t = parseType(); // o = new (iconOrLabel as Class)() as DisplayObject var cc = switch(t) { case TComplex(e1) : switch (e1) { - case EBinop(op, e2, e3, n): + case EBinop(op, e2, e3, n): if (op == "as") { switch (e2) { - case ECall(e4, a): + case ECall(e4, a): EBinop(op, ECall(EField(EIdent("Type"), "createInstance"), [e4, EArrayDecl(a)]), e3, n); default: null; } @@ -161,9 +270,19 @@ class StructureParser { while( ParserUtils.opt(tokenizer, TId("catch")) ) { tokenizer.ensure(TPOpen); var name = tokenizer.id(); - tokenizer.ensure(TColon); - var t = parseType(); - tokenizer.ensure(TPClose); + var t:T = null; + + var next = tokenizer.token(); + switch (ParserUtils.uncomment(ParserUtils.removeNewLine(next))) { + case TColon: + t = parseType(); + tokenizer.ensure(TPClose); + case TPClose: + t = TComplex(EIdent("Dynamic")); + default: + ParserUtils.unexpected(next); + } + var e = parseExpr(false); catches.push( { name : name, t : t, e : e } ); } @@ -219,7 +338,7 @@ class StructureParser { } cl.push(caseObj); } - + //reset for next case or default meta = []; } @@ -237,7 +356,7 @@ class StructureParser { ParserUtils.unexpected(tk); } } - + ESwitch(e, cl, def); case "do": var e = parseExpr(false); @@ -260,20 +379,6 @@ class StructureParser { var e = parseExpr(false); tokenizer.end(); EDelete(e); - case "getQualifiedClassName": - tokenizer.ensure(TPOpen); - var e = parseExpr(false); - e = switch(e) { - case EIdent(v) if(v == "this"): ECall(EField(EIdent("Type"), "getClass"), [e]); - default: e; - } - tokenizer.ensure(TPClose); - ECall(EField(EIdent("Type"), "getClassName"), [e]); - case "getQualifiedSuperclassName": - tokenizer.ensure(TPOpen); - var e = parseExpr(false); - tokenizer.ensure(TPClose); - ECall(EField(EIdent("Type"), "getClassName"), [ECall(EField(EIdent("Type"), "getSuperClass"), [e])]); case "getDefinitionByName": tokenizer.ensure(TPOpen); var e = parseExpr(false); @@ -310,27 +415,27 @@ class StructureParser { default: null; } } - + static function getParams(tokenizer:Tokenizer, parseExpr) { - return switch(tokenizer.token()) { + return switch (tokenizer.token()) { case TPOpen: var params = []; var parCount = 1; - while(parCount > 0) { + while (parCount > 0) { var t = tokenizer.token(); - switch(t) { + switch (t) { case TPOpen: parCount++; case TPClose: parCount--; if(params.length > 0) params[params.length - 1] = EParent(params[params.length - 1]); case TComma: - case TOp(op) if(params.length > 0): + case TOp(op) if (params.length > 0): params[params.length - 1] = ParserUtils.makeBinop(tokenizer, op, params[params.length - 1], parseExpr(false)); case _: tokenizer.add(t); - if(params.length < 2) params.push(parseExpr(false)); + if (params.length < 2) params.push(parseExpr(false)); else { - if(params.length == 2) params.push(EArrayDecl([])); + if (params.length == 2) params.push(EArrayDecl([])); switch(params[2]) { case EArrayDecl(e): e.push(parseExpr(false)); case _: @@ -342,4 +447,5 @@ class StructureParser { case _: null; } } + } diff --git a/src/as3hx/parsers/TypeParser.hx b/src/as3hx/parsers/TypeParser.hx index 6f4ee34..4bcf06d 100644 --- a/src/as3hx/parsers/TypeParser.hx +++ b/src/as3hx/parsers/TypeParser.hx @@ -3,6 +3,7 @@ package as3hx.parsers; import as3hx.Tokenizer; import as3hx.As3; import as3hx.Parser; +import neko.Lib; class TypeParser { @@ -15,31 +16,143 @@ class TypeParser { var tmp = tokenizer.opPriority.get("*="); tokenizer.opPriority.remove("*="); if(ParserUtils.opt(tokenizer, TOp("*"))) { - tokenizer.opPriority.set("*=",tmp); - return TStar; + tokenizer.opPriority.set("*=", tmp); + + var typeFromComment:T = null; + var t2 = tokenizer.token(); + switch(t2) { + case TCommented(s, true, t3): + typeFromComment = OverrideTypeComment.extractType("*", s, types); + if (typeFromComment != null) { + tokenizer.add(t3); + } else { + tokenizer.add(t2); + } + default: + tokenizer.add(t2); + } + if (typeFromComment != null) { + return typeFromComment; + } else { + return TStar; + } } tokenizer.opPriority.set("*=",tmp); // for _i = new (obj as Class)() as DisplayObject; switch(tokenizer.peek()) { + //case TId("this"): return TComplex(parseExpr(false)); case TPOpen: return TComplex(parseExpr(false)); default: } var t = tokenizer.id(); - if(t == "Vector") { + var vectorType = null; + if (t == "Vector") { + function u() { + var t = tokenizer.token(); + tokenizer.add(t); + return t; + } + var typeFromComment:T = null; + var t2 = tokenizer.token(); + switch(t2) { + case TCommented(s, true, t3): + typeFromComment = OverrideTypeComment.extractType(t, s, types); + if (typeFromComment != null) { + tokenizer.add(t3); + } else { + tokenizer.add(t2); + } + default: + tokenizer.add(t2); + } tokenizer.ensure(TDot); tokenizer.ensure(TOp("<")); var t = parseType(); splitEndTemplateOps(tokenizer); tokenizer.ensure(TOp(">")); types.seen.push(TVector(t)); - return TVector(t); - } - if(cfg.dictionaryToHash && t == "Dictionary") { - var k = TPath(["Object"]); - var v = TPath(["Object"]); - types.seen.push(TDictionary(k, v)); + if (typeFromComment == null) { + vectorType = TVector(t); + } else { + vectorType = typeFromComment; + } + } + var t2 = tokenizer.token(); + switch(t2) { + case TCommented(s, true, t3): + var typeFromComment:T = OverrideTypeComment.extractType(t, s, types); + if (typeFromComment != null) { + tokenizer.add(t3); + return typeFromComment; + } else { + tokenizer.add(t2); + } + default: + tokenizer.add(t2); + } + if (vectorType != null) { + return vectorType; + } + if (t == "Array") { + var k:T = null; + var t2 = tokenizer.token(); + switch(t2) { + case TCommented(s, true, t3): + var arg = s.substring(2, s.length - 2); + if (~/\w+/g.match(arg)) { + t = t + "<" + arg + ">"; + types.seen.push(TPath([arg])); + tokenizer.add(t3); + } + default: + tokenizer.add(t2); + } + return TPath([t]); + } + if (t == "Dictionary") { + var k:T = null; + var v:T = null; + if (cfg.useAngleBracketsNotationForDictionaryTyping) { + var t2 = tokenizer.token(); + switch(t2) { + case TDot: + tokenizer.ensure(TOp("<")); + k = parseType(); + tokenizer.ensure(TComma); + v = parseType(); + tokenizer.ensure(TOp(">")); + default: + tokenizer.add(t2); + } + } else { + var t2 = tokenizer.token(); + switch(t2) { + case TCommented(s, true, t3): + s = s.substring(2, s.length - 2); + var commaIndex:Int = s.indexOf(","); + if (commaIndex != -1) { + k = TPath(s.substr(0, commaIndex).split(".")); + v = TPath(s.substr(commaIndex + 1).split(".")); + types.seen.push(TPath(["Dictionary"])); + tokenizer.add(t3); + } + default: + tokenizer.add(t2); + } + } + if (!cfg.dictionaryToHash) { + types.seen.push(TPath(["Dictionary"])); + } + if (k == null) { + k = TPath(["Object"]); + } + if (v == null) { + v = TPath(["Object"]); + } + types.seen.push(k); + types.seen.push(v); return TDictionary(k, v); } if(!cfg.functionToDynamic && t == "Function") { @@ -80,7 +193,7 @@ class TypeParser { for(it in types.seen) { switch(it) { case TPath(p): - if(Lambda.foreach(a, function(it) return p.indexOf(it) != -1)) { + if(Lambda.foreach(a, function(it) return p.indexOf(it) != -1)) {//this condition can fail on classes com.a.b.Class && com.b.a.Class return result; } default: @@ -89,7 +202,7 @@ class TypeParser { types.seen.push(result); return result; } - + private static function splitEndTemplateOps(tokenizer:Tokenizer) { switch( tokenizer.peek() ) { case TOp(s): diff --git a/src/as3hx/parsers/XMLReader.hx b/src/as3hx/parsers/XMLReader.hx index 30432f4..3950c4e 100644 --- a/src/as3hx/parsers/XMLReader.hx +++ b/src/as3hx/parsers/XMLReader.hx @@ -5,21 +5,45 @@ import as3hx.Error; class XMLReader { - public static function read(tokenizer:Tokenizer) { + public static function read(tokenizer:Tokenizer):String { Debug.dbgln("readXML()", tokenizer.line); var buf = new StringBuf(); var input = tokenizer.input; buf.addChar("<".code); buf.addChar(tokenizer.char); - //corner case : check wether this is a satndalone CDATA element //(not wrapped in XML element) var isCDATA = tokenizer.char == "!".code; - tokenizer.char = 0; + if (isCDATA) { + var end1 = input.readByte(); + var end2 = input.readByte(); + buf.addChar(end1); + buf.addChar(end2); + if (end1 == '['.code) { + if (end2 != 'C'.code) + throw EInvalidChar(end2); + end1 = ']'.code; + } else if (end1 == '-'.code) { + if (end2 != '-'.code) + throw EInvalidChar(end2); + } else { + throw EInvalidChar(end1); + } + var p1 = 0; + var p2 = 0; + while (true) { + var c = input.readByte(); + buf.addChar(c); + if (p1 == end1 && p2 == end1 && c == '>'.code) + return buf.toString(); + p1 = p2; + p2 = c; + } + } try { var prev = 0; - while(true) { + while (true) { var c = input.readByte(); if(c == "\n".code) tokenizer.line++; buf.addChar(c); @@ -28,9 +52,7 @@ class XMLReader { } if( prev == "/".code ) return buf.toString(); - if (isCDATA) - return buf.toString(); - while(true) { + while (true) { var c = input.readByte(); if(c == "\n".code) tokenizer.line++; if( c == "<".code ) { diff --git a/test.hxml b/test.hxml index b4b8fbd..3908436 100644 --- a/test.hxml +++ b/test.hxml @@ -41,6 +41,7 @@ -cp test/unit -neko build/neko_test.n +-lib test-adapter --next diff --git a/test/e2e/mxml/First.mxml b/test/e2e/mxml/First.mxml new file mode 100644 index 0000000..deb73d0 --- /dev/null +++ b/test/e2e/mxml/First.mxml @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/test/e2e/mxml/pack/Second.mxml b/test/e2e/mxml/pack/Second.mxml new file mode 100644 index 0000000..efc5f05 --- /dev/null +++ b/test/e2e/mxml/pack/Second.mxml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/test/e2e/simple/Attr.hx b/test/e2e/simple/Attr.hx index 6cb7b0a..8c821e8 100644 --- a/test/e2e/simple/Attr.hx +++ b/test/e2e/simple/Attr.hx @@ -1,12 +1,11 @@ package test.package; -class Min -{ - private var testAttr : Int = 1; +class Min { - public function new() - { - var test : String = "hello"; - } -} + private var testAttr:Int = 1; + public function new() { + var test:String = 'hello'; + } + +} \ No newline at end of file diff --git a/test/e2e/simple/Dictionary.hx b/test/e2e/simple/Dictionary.hx index 4421e0b..6d5dfd2 100644 --- a/test/e2e/simple/Dictionary.hx +++ b/test/e2e/simple/Dictionary.hx @@ -1,13 +1,9 @@ -import flash.utils.Dictionary; +class Dict { -class Dict -{ + private var _dic:haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); - private var _dic : Dictionary = new Dictionary(); - - public function new() - { - var dic : Dictionary = new Dictionary(); - } -} + public function new() { + var dic:haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); + } +} \ No newline at end of file diff --git a/test/e2e/simple/ForLoop.hx b/test/e2e/simple/ForLoop.hx index 2fa753c..9317c9c 100644 --- a/test/e2e/simple/ForLoop.hx +++ b/test/e2e/simple/ForLoop.hx @@ -1,12 +1,9 @@ package test.package; -class ForLoop -{ - public function new() - { - for (i in 0...10){ +class ForLoop { - } - } -} + public function new() { + for (i in 0...10) {} + } +} \ No newline at end of file diff --git a/test/e2e/simple/Func.hx b/test/e2e/simple/Func.hx index b9c563c..becc93e 100644 --- a/test/e2e/simple/Func.hx +++ b/test/e2e/simple/Func.hx @@ -1,16 +1,13 @@ package test.package; -class Min -{ - private var testAttr : Int = 1; +class Min { - public function new() - { - var test : String = "hello"; - } + private var testAttr:Int = 1; - private function testFun(){ + public function new() { + var test:String = 'hello'; + } - } -} + private function testFun() {} +} \ No newline at end of file diff --git a/test/e2e/simple/Interface.hx b/test/e2e/simple/Interface.hx index e30b226..db5a717 100644 --- a/test/e2e/simple/Interface.hx +++ b/test/e2e/simple/Interface.hx @@ -1,7 +1,7 @@ package test.package; -interface Test -{ - function someFunc() : Void; -} +interface Test { + function someFunc():Void; + +} \ No newline at end of file diff --git a/test/e2e/simple/Min.hx b/test/e2e/simple/Min.hx index b0375ff..b4be5b6 100644 --- a/test/e2e/simple/Min.hx +++ b/test/e2e/simple/Min.hx @@ -1,7 +1,3 @@ -class Min -{ - public function new() - { - } -} +class Min { +} \ No newline at end of file diff --git a/test/e2e/simple/Package.hx b/test/e2e/simple/Package.hx index 3c172ad..7c8353c 100644 --- a/test/e2e/simple/Package.hx +++ b/test/e2e/simple/Package.hx @@ -1,9 +1,5 @@ package test.package; -class Min -{ - public function new() - { - } -} +class Min { +} \ No newline at end of file diff --git a/test/e2e/simple/Var.hx b/test/e2e/simple/Var.hx index a464bfd..00b7e3c 100644 --- a/test/e2e/simple/Var.hx +++ b/test/e2e/simple/Var.hx @@ -1,10 +1,9 @@ package test.package; -class Min -{ - public function new() - { - var test : String = "hello"; - } -} +class Min { + public function new() { + var test:String = 'hello'; + } + +} \ No newline at end of file diff --git a/test/e2e/src3/CommentedXML.as b/test/e2e/src3/CommentedXML.as new file mode 100644 index 0000000..40374f9 --- /dev/null +++ b/test/e2e/src3/CommentedXML.as @@ -0,0 +1,16 @@ +package { + +public class CommentedXML { + + function CommentedXML():void + { + var libXML:XMLList = p.assetLibrary; + libXML.child[0] = new XML(); + libXML.child[1] = new XML(); + libXML.child[2] = new XML( ]]>); + project.appendChild(libXML); + } + +} + +} \ No newline at end of file diff --git a/test/e2e/src3/CommentedXML.hx b/test/e2e/src3/CommentedXML.hx new file mode 100644 index 0000000..6170d58 --- /dev/null +++ b/test/e2e/src3/CommentedXML.hx @@ -0,0 +1,11 @@ +class CommentedXML { + + private function new() { + var libXML:FastXMLList = p.assetLibrary; + libXML.descendants('child').set(0, FastXML.parse('')); + libXML.descendants('child').set(1, FastXML.parse('')); + libXML.descendants('child').set(2, FastXML.parse(' ]]>')); + project.appendChild(libXML); + } + +} \ No newline at end of file diff --git a/test/e2e/src3/Comments.hx b/test/e2e/src3/Comments.hx new file mode 100644 index 0000000..eb4580c --- /dev/null +++ b/test/e2e/src3/Comments.hx @@ -0,0 +1,44 @@ +/** +Package Comment +**/ + +/** + * Class Comment + **/ +class Comments { + + /** Function comment **/ + public static function blah():Void { + var a:Int = 1;// line comment + } + + /** Second **/ + public function memberFunc() {} + + private var i(get, set):Dynamic; + private function get_i():Dynamic { + return _i; + } + + private function set_i(v:Dynamic):Dynamic { + _i = v; + return v; + } + + private var j(get, set):Int; + private function get_j():Int { + return AS3.int(_j); + } + + private function set_j(v:Int):Int { + _i = v; + return v; + } + + public function new() {} + +} + +/** + * Trailing package comment + **/ \ No newline at end of file diff --git a/test/e2e/src3/EmptySort.as b/test/e2e/src3/EmptySort.as new file mode 100644 index 0000000..f078d62 --- /dev/null +++ b/test/e2e/src3/EmptySort.as @@ -0,0 +1,9 @@ +package { + public class EmptySort { + public function EmptySort() + { + var arr:Array = []; + arr.sort(); + } + } +} \ No newline at end of file diff --git a/test/e2e/src3/GetXmlNS.as b/test/e2e/src3/GetXmlNS.as new file mode 100644 index 0000000..7e0996d --- /dev/null +++ b/test/e2e/src3/GetXmlNS.as @@ -0,0 +1,10 @@ +package { + public class GetXmlNS { + public function GetXmlNS() { + var updateDescriptor:XML = new XML(); + var updateVersion = updateDescriptor.UPDATE_XMLNS_1_0::version; + var updateDescription = updateDescriptor.UPDATE_XMLNS_1_0::description; + // var updatePackageURL = updateDescriptor.UPDATE_XMLNS_1_0::urls.UPDATE_XMLNS_1_1::[installerType]; + } + } +} \ No newline at end of file diff --git a/test/e2e/src3/XmlIfAttr.as b/test/e2e/src3/XmlIfAttr.as new file mode 100644 index 0000000..6ace80e --- /dev/null +++ b/test/e2e/src3/XmlIfAttr.as @@ -0,0 +1,14 @@ +package { + public class XmlIfAttr { + // protected static var HAS_UNCOMPRESS:Boolean = describeType(123).factory.method.(@name == "uncompress").parameter.length() > 0; + protected static var HAS_UNCOMPRESS:Boolean = getUnc(); + public function XmlIfAttr() { + } + public static function getUnc():Boolean { + return describeType(123).factory.method.(@name == "uncompress").parameter.length() > 0; + } + public function describeType(v:Number):XML { + return ; + } + } +} \ No newline at end of file diff --git a/test/issues/Issue.as b/test/issues/Issue.as deleted file mode 100644 index 7a66d66..0000000 --- a/test/issues/Issue.as +++ /dev/null @@ -1,9 +0,0 @@ -package { - public class Issue103 { - public function Issue103() { - var string:String = ""; - var boolean:Boolean = true; - var number:Number = 10.1; - } - } -} \ No newline at end of file diff --git a/test/issues/Issue.hx b/test/issues/Issue.hx deleted file mode 100644 index 889d560..0000000 --- a/test/issues/Issue.hx +++ /dev/null @@ -1,10 +0,0 @@ - -class Issue103 -{ - public function new() - { - var string : String = ""; - var boolean : Bool = true; - var number : Float = 10.1; - } -} diff --git a/test/issues/Issue103.hx b/test/issues/Issue103.hx index 889d560..cdd8e9a 100644 --- a/test/issues/Issue103.hx +++ b/test/issues/Issue103.hx @@ -1,10 +1,9 @@ +class Issue103 { -class Issue103 -{ - public function new() - { - var string : String = ""; - var boolean : Bool = true; - var number : Float = 10.1; - } -} + public function new() { + var string:String = ''; + var boolean:Bool = true; + var number:Float = 10.1; + } + +} \ No newline at end of file diff --git a/test/issues/Issue112.hx b/test/issues/Issue112.hx index 3110979..03be495 100644 --- a/test/issues/Issue112.hx +++ b/test/issues/Issue112.hx @@ -1,15 +1,10 @@ +class Issue112 { -class Issue112 -{ - public function new() - { - var timeoutId : Int = as3hx.Compat.setTimeout(function(s : String) : Void - { - }, 1000, ["string"]); - as3hx.Compat.clearTimeout(timeoutId); - var intervalId : Int = as3hx.Compat.setInterval(function(s : String) : Void - { - }, 1000, ["string"]); - as3hx.Compat.clearInterval(intervalId); - } -} + public function new() { + var timeoutId:Int = as3hx.Compat.setTimeout(function(s:String):Void {}, 1000, ['string']); + as3hx.Compat.clearTimeout(timeoutId); + var intervalId:Int = AS3.int(as3hx.Compat.setInterval(function(s:String):Void {}, 1000, ['string'])); + as3hx.Compat.clearInterval(intervalId); + } + +} \ No newline at end of file diff --git a/test/issues/Issue115.hx b/test/issues/Issue115.hx index a4a66df..1971242 100644 --- a/test/issues/Issue115.hx +++ b/test/issues/Issue115.hx @@ -1,43 +1,31 @@ +class Issue115 { -class Issue115 -{ - public function new() - { - if (true) - { - var i : Int = 1; - } - - if (true) - { - var i : Int = 1; - } - - if (true) - { - var i : Int = 1; - } - else - { - var i : Int = 1; - } - - if (true) - { - var i : Int = 1; - } - else - { - var i : Int = 1; - } - - if (true) - { - var i : Int = 1; - } - else - { - var i : Int = 1; - } - } -} + public function new() { + if (true) { + var i:Int = 1; + } + + if (true) { + var i:Int = 1; + } + + if (true) { + var i:Int = 1; + } else { + var i:Int = 1; + } + + if (true) { + var i:Int = 1; + } else { + var i:Int = 1; + } + + if (true) { + var i:Int = 1; + } else { + var i:Int = 1; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue119.hx b/test/issues/Issue119.hx new file mode 100644 index 0000000..f24c232 --- /dev/null +++ b/test/issues/Issue119.hx @@ -0,0 +1,13 @@ +import flash.system.Capabilities; + +class Issue119 { + + public function new() { + var sPlatform:String = Capabilities.version.substr(0, 3); + var sDesktop:Bool = new as3hx.Compat.Regex('(WIN|MAC|LNX)', '').exec(sPlatform) != null; + + var ereg:as3hx.Compat.Regex = new as3hx.Compat.Regex('(WIN|MAC|LNX)', ''); + var sDesktop2:Bool = ereg.exec(sPlatform) != null; + } + +} \ No newline at end of file diff --git a/test/issues/Issue119_notUseCompat.hx b/test/issues/Issue119_notUseCompat.hx index e43a53d..15dece9 100644 --- a/test/issues/Issue119_notUseCompat.hx +++ b/test/issues/Issue119_notUseCompat.hx @@ -1,13 +1,13 @@ import flash.system.Capabilities; -class Issue119 -{ - public function new() - { - var sPlatform : String = Capabilities.version.substr(0, 3); - var sDesktop : Bool = new flash.utils.RegExp('(WIN|MAC|LNX)', "").exec(sPlatform) != null; - - var ereg : flash.utils.RegExp = new flash.utils.RegExp('(WIN|MAC|LNX)', ""); - var sDesktop2 : Bool = ereg.exec(sPlatform) != null; - } -} +class Issue119 { + + public function new() { + var sPlatform:String = Std.string(Capabilities.version.substr(0, 3)); + var sDesktop:Bool = new flash.utils.RegExp('(WIN|MAC|LNX)', '').exec(sPlatform) != null; + + var ereg:flash.utils.RegExp = new flash.utils.RegExp('(WIN|MAC|LNX)', ''); + var sDesktop2:Bool = ereg.exec(sPlatform) != null; + } + +} \ No newline at end of file diff --git a/test/issues/Issue119_useCompat.hx b/test/issues/Issue119_useCompat.hx index c200f8b..04d0afc 100644 --- a/test/issues/Issue119_useCompat.hx +++ b/test/issues/Issue119_useCompat.hx @@ -1,13 +1,13 @@ import flash.system.Capabilities; -class Issue119 -{ - public function new() - { - var sPlatform : String = Capabilities.version.substr(0, 3); - var sDesktop : Bool = new as3hx.Compat.Regex('(WIN|MAC|LNX)', "").exec(sPlatform) != null; - - var ereg : as3hx.Compat.Regex = new as3hx.Compat.Regex('(WIN|MAC|LNX)', ""); - var sDesktop2 : Bool = ereg.exec(sPlatform) != null; - } -} +class Issue119 { + + public function new() { + var sPlatform:String = Std.string(Capabilities.version.substr(0, 3)); + var sDesktop:Bool = new as3hx.Compat.Regex('(WIN|MAC|LNX)', '').exec(sPlatform) != null; + + var ereg:as3hx.Compat.Regex = new as3hx.Compat.Regex('(WIN|MAC|LNX)', ''); + var sDesktop2:Bool = ereg.exec(sPlatform) != null; + } + +} \ No newline at end of file diff --git a/test/issues/Issue120.hx b/test/issues/Issue120.hx index 99412d1..101845a 100644 --- a/test/issues/Issue120.hx +++ b/test/issues/Issue120.hx @@ -1,12 +1,12 @@ import haxe.Constraints.Function; -class Issue120 -{ - public function new() - { - var call : Function = trace; - var args : Array = []; - Reflect.callMethod(null, call, args); - Reflect.callMethod(null, call, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); - } -} +class Issue120 { + + public function new() { + var call:Function = trace; + var args:Array = []; + Reflect.callMethod(null, call, args); + Reflect.callMethod(null, call, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + } + +} \ No newline at end of file diff --git a/test/issues/Issue121.hx b/test/issues/Issue121.hx index 30b50ba..9100b99 100644 --- a/test/issues/Issue121.hx +++ b/test/issues/Issue121.hx @@ -1,34 +1,26 @@ +class Issue121 { -class Issue121 -{ - public function new() - { - var o : Dynamic = { }; - if (Lambda.has(o, "some")) - { - Reflect.deleteField(o, "some"); - } - else - { - o = null; - } - - if (Lambda.has(o, 1)) - { - Reflect.deleteField(o, "1"); - } - } - - private var _eventListeners : Dynamic = { }; - public function removeEventListeners(type : String = null) : Void - { - if (type != null && _eventListeners != null) - { - Reflect.deleteField(_eventListeners, type); - } - else - { - _eventListeners = null; - } - } -} + public function new() { + var o:Dynamic = {}; + if (Reflect.hasField(o, 'some')) { + Reflect.deleteField(o, 'some'); + } else { + o = null; + } + + if (Reflect.hasField(o, Std.string(1))) { + Reflect.deleteField(o, '1'); + } + } + + private var _eventListeners:Dynamic = {}; + + public function removeEventListeners(type:String = null):Void { + if (type != null && AS3.as(_eventListeners, Bool)) { + Reflect.deleteField(_eventListeners, type); + } else { + _eventListeners = null; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue124.hx b/test/issues/Issue124.hx index f443870..0d35f53 100644 --- a/test/issues/Issue124.hx +++ b/test/issues/Issue124.hx @@ -1,22 +1,15 @@ -import flash.utils.Dictionary; +class Issue124 { -class Issue124 -{ - private var _eventListeners : Dictionary = new Dictionary(); - public function removeEventListeners(type : String = null) : Void - { - if (type != null && _eventListeners != null) - { - This is an intentional compilation error. See the README for handling the delete keyword - delete _eventListeners[type]; - } - else - { - _eventListeners = null; - } - } + private var _eventListeners:haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); - public function new() - { - } -} + public function removeEventListeners(type:String = null):Void { + if (type != null && _eventListeners != null) { + _eventListeners.remove(type); + } else { + _eventListeners = null; + } + } + + public function new() {} + +} \ No newline at end of file diff --git a/test/issues/Issue128.hx b/test/issues/Issue128.hx index 9497e9e..fa3ff6e 100644 --- a/test/issues/Issue128.hx +++ b/test/issues/Issue128.hx @@ -1,13 +1,11 @@ +class Issue128 { -class Issue128 -{ - public function new() - { - var b : Bool; - var n : Float = 1; - if (b || (n != 0 && !Math.isNaN(n))) - { - trace(n); - } - } -} + public function new() { + var b:Bool; + var n:Float = 1; + if (b || (n != 0 && !Math.isNaN(n))) { + trace(n); + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue133.hx b/test/issues/Issue133.hx index 3d5af21..baadf26 100644 --- a/test/issues/Issue133.hx +++ b/test/issues/Issue133.hx @@ -1,18 +1,15 @@ +class Issue133 { -class Issue133 -{ - public function new() - { - var max : Int = as3hx.Compat.INT_MAX; - if (max > as3hx.Compat.INT_MAX) - { - max = as3hx.Compat.INT_MAX; - } - - var min : Int = as3hx.Compat.INT_MIN; - if (min < as3hx.Compat.INT_MIN) - { - min = as3hx.Compat.INT_MIN; - } - } -} + public function new() { + var max:Int = AS3.int(as3hx.Compat.INT_MAX); + if (max > as3hx.Compat.INT_MAX) { + max = AS3.int(as3hx.Compat.INT_MAX); + } + + var min:Int = AS3.int(as3hx.Compat.INT_MIN); + if (min < as3hx.Compat.INT_MIN) { + min = AS3.int(as3hx.Compat.INT_MIN); + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue134.hx b/test/issues/Issue134.hx index 4afa400..a753046 100644 --- a/test/issues/Issue134.hx +++ b/test/issues/Issue134.hx @@ -1,22 +1,19 @@ import flash.errors.Error; -class Issue134 -{ - public function new() - { - throw new Error(); - } - - private function test() : Void - { - trace("Issue134"); - } - - private function test2() : Void - { - for (i in 0...10) - { - trace("Issue134"); - } - } -} +class Issue134 { + + public function new() { + throw new Error(); + } + + private function test():Void { + trace('Issue134'); + } + + private function test2():Void { + for (i in 0...10) { + trace('Issue134'); + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue139.hx b/test/issues/Issue139.hx index 3f97fa1..bde32bc 100644 --- a/test/issues/Issue139.hx +++ b/test/issues/Issue139.hx @@ -1,18 +1,15 @@ +class Issue139 { -class Issue139 -{ - public function new() - { - var max : Float = as3hx.Compat.FLOAT_MAX; - if (max > as3hx.Compat.FLOAT_MAX) - { - max = as3hx.Compat.FLOAT_MAX; - } - - var min : Float = as3hx.Compat.FLOAT_MIN; - if (min < as3hx.Compat.FLOAT_MIN) - { - min = as3hx.Compat.FLOAT_MIN; - } - } -} + public function new() { + var max:Float = as3hx.Compat.FLOAT_MAX; + if (max > as3hx.Compat.FLOAT_MAX) { + max = as3hx.Compat.FLOAT_MAX; + } + + var min:Float = as3hx.Compat.FLOAT_MIN; + if (min < as3hx.Compat.FLOAT_MIN) { + min = as3hx.Compat.FLOAT_MIN; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue14.hx b/test/issues/Issue14.hx index 8641850..2a60396 100644 --- a/test/issues/Issue14.hx +++ b/test/issues/Issue14.hx @@ -1,11 +1,9 @@ +class Issue14 { -class Issue14 -{ - - private var _regEx : as3hx.Compat.Regex = new as3hx.Compat.Regex('[\\/]', "g"); - - public function new() - { - var regEx : as3hx.Compat.Regex = new as3hx.Compat.Regex('[\\/]', "g"); - } -} + private var _regEx:as3hx.Compat.Regex = new as3hx.Compat.Regex('[\\/]', 'g'); + + public function new() { + var regEx:as3hx.Compat.Regex = new as3hx.Compat.Regex('[\\/]', 'g'); + } + +} \ No newline at end of file diff --git a/test/issues/Issue142.hx b/test/issues/Issue142.hx index da03654..3306fb3 100644 --- a/test/issues/Issue142.hx +++ b/test/issues/Issue142.hx @@ -2,14 +2,14 @@ import flash.display.Scene; import flash.display.Sprite; import flash.events.DataEvent; -class Issue142 -{ - public function new() - { - var sceneClass : Class = Type.getClass(Type.resolveClass("SceneType")); - var currentScene : Scene = try cast(Type.createInstance(sceneClass, []), Scene) catch(e:Dynamic) null; - - var d : Date = Date.now(); - var s : Sprite = new Sprite(); - } -} +class Issue142 { + + public function new() { + var sceneClass:Class = as3hx.Compat.castClass(Type.resolveClass('SceneType')); + var currentScene:Scene = AS3.as(Type.createInstance(sceneClass, []), Scene); + + var d:Date = Date.now(); + var s:Sprite = new Sprite(); + } + +} \ No newline at end of file diff --git a/test/issues/Issue143.hx b/test/issues/Issue143.hx index e66814c..83b6588 100644 --- a/test/issues/Issue143.hx +++ b/test/issues/Issue143.hx @@ -1,8 +1,7 @@ import flash.display3D.Context3D; -class Issue143 -{ - public function new() - { - } -} +class Issue143 { + + public function new() {} + +} \ No newline at end of file diff --git a/test/issues/Issue144.hx b/test/issues/Issue144.hx index 5a4199a..254caaf 100644 --- a/test/issues/Issue144.hx +++ b/test/issues/Issue144.hx @@ -1,17 +1,17 @@ import flash.events.TouchEvent; -class Issue144 -{ - private var multitouchEnabled : Bool; - private var types : Array = []; - public function new() - { - if (multitouchEnabled) - { - types.push(TouchEvent.TOUCH_BEGIN); - types.push(TouchEvent.TOUCH_MOVE); - types.push(TouchEvent.TOUCH_END); - - } - } -} +class Issue144 { + + private var multitouchEnabled:Bool = false; + private var types:Array = []; + + public function new() { + if (multitouchEnabled) { + types.push(TouchEvent.TOUCH_BEGIN); + types.push(TouchEvent.TOUCH_MOVE); + types.push(TouchEvent.TOUCH_END); + + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue148.hx b/test/issues/Issue148.hx index 3bcfceb..60e5dae 100644 --- a/test/issues/Issue148.hx +++ b/test/issues/Issue148.hx @@ -1,11 +1,7 @@ +class Issue148 { -class Issue148 -{ - public function new() - { - } - - public function enqueue(rawAssets : Array = null) : Void - { - } -} + public function new() {} + + public function enqueue(rawAssets:Array = null):Void {} + +} \ No newline at end of file diff --git a/test/issues/Issue15.hx b/test/issues/Issue15.hx index 4c50a5a..8edfc23 100644 --- a/test/issues/Issue15.hx +++ b/test/issues/Issue15.hx @@ -1,109 +1,87 @@ +class Issue15 { -class Issue15 -{ - - public function new() - { - var i : Int = 0; - if (i != 0) - { - trace(i); - } - ++i; - - if (true) - { - trace(""); - } - else - { - trace(""); - } - ++i; - - for (j in 0...10) - { - trace(""); - } - ++i; - - for (j in 0...10) - { - trace(""); - } - ++i; - - for (name in Reflect.fields({ })) - { - trace(""); - } - ++i; - - for (name in Reflect.fields({ })) - { - trace(""); - } - ++i; - - for (item/* AS3HX WARNING could not determine type for var: item exp: EObject([]) type: null */ in { }) - { - trace(""); - } - ++i; - - for (item/* AS3HX WARNING could not determine type for var: item exp: EObject([]) type: null */ in { }) - { - trace(""); - } - ++i; - - while (true) - { - trace(""); - } - ++i; - - while (true) - { - trace(""); - } - ++i; - - do - { - trace(""); - } - while ((true)); - ++i; - - do - { - trace(""); - } - while ((true)); - ++i; - - do - { - trace(""); - } - while ((true)); - ++i; - - switch (i) - { - case 1: - trace(""); - default: - } - ++i; - - switch (i) - { - case 1: - trace(""); - default: - } - ++i; - } -} + public function new() { + var i:Int = 0; + if (i != 0) { + trace(i); + } + ++i; + + if (true) { + trace(''); + } else { + trace(''); + } + ++i; + + for (j in 0...10) { + trace(''); + } + ++i; + + for (j in 0...10) { + trace(''); + } + ++i; + + for (name in Reflect.fields({})) { + trace(''); + } + ++i; + + for (name in Reflect.fields({})) { + trace(''); + } + ++i; + + for (item in as3hx.Compat.each({})) { + trace(''); + } + ++i; + + for (item in as3hx.Compat.each({})) { + trace(''); + } + ++i; + + while (true) { + trace(''); + } + ++i; + + while (true) { + trace(''); + } + ++i; + + do { + trace(''); + } while ((true)); + ++i; + + do { + trace(''); + } while ((true)); + ++i; + + do { + trace(''); + } while ((true)); + ++i; + + switch (i) { + case 1: + trace(''); + case _: + } + ++i; + + switch (i) { + case 1: + trace(''); + case _: + } + ++i; + } + +} \ No newline at end of file diff --git a/test/issues/Issue150.hx b/test/issues/Issue150.hx index caba124..9248f0a 100644 --- a/test/issues/Issue150.hx +++ b/test/issues/Issue150.hx @@ -1,23 +1,22 @@ +class Issue150 { -class Issue150 -{ - public function new() - { - var fragmentShader : String = [ - tex("ft0", "v1", 1, _mapTexture, false), // read map texture - "sub ft1, ft0, fc0", // subtract 0.5 -> range [-0.5, 0.5] - "mul ft1.xy, ft1.xy, ft0.ww", // zero displacement when alpha == 0 - "m44 ft2, ft1, fc1", // multiply matrix with displacement values - "add ft3, v0, ft2", // add displacement values to texture coords - tex("oc", "ft3", 0, texture) // read input texture at displaced coords - ].join("\n"); - - var s1 : String = [1, 2, 3, 4, 5].join("\n"); - - var s2 : String = [1, - 2, 3, 4, 5 - ].join("\n"); - - var s2 : String = [1, 2, 3, /*comment*/ 4, 5].join("\n"); - } -} + public function new() { + var fragmentShader:String = [ + tex('ft0', 'v1', 1, _mapTexture, false), // read map texture + 'sub ft1, ft0, fc0', // subtract 0.5 -> range [-0.5, 0.5] + 'mul ft1.xy, ft1.xy, ft0.ww', // zero displacement when alpha == 0 + 'm44 ft2, ft1, fc1', // multiply matrix with displacement values + 'add ft3, v0, ft2', // add displacement values to texture coords + tex('oc', 'ft3', 0, texture)// read input texture at displaced coords + ].join('\n'); + + var s1:String = [1, 2, 3, 4, 5].join('\n'); + + var s2:String = [1, + 2, 3, 4, 5 + ].join('\n'); + + var s2:String = [1, 2, 3, /*comment*/ 4, 5].join('\n'); + } + +} \ No newline at end of file diff --git a/test/issues/Issue152.hx b/test/issues/Issue152.hx index 38d93a2..5a338af 100644 --- a/test/issues/Issue152.hx +++ b/test/issues/Issue152.hx @@ -1,21 +1,21 @@ +class Issue152 { -class Issue152 -{ - private var vertexShader : Array; - public function new() - { - vertexShader.push( - 1 // add offset 1 - ); - vertexShader.push( - 2 // add offset 2 - ); - vertexShader.push( - 3 // add offset 3 - ); - vertexShader.push( - 4 - ); - - } -} + private var vertexShader:Array; + + public function new() { + vertexShader.push( + 1// add offset 1 + ); + vertexShader.push( + 2// add offset 2 + ); + vertexShader.push( + 3// add offset 3 + ); + vertexShader.push( + 4 + ); + + } + +} \ No newline at end of file diff --git a/test/issues/Issue156.hx b/test/issues/Issue156.hx index b269f27..94a5e46 100644 --- a/test/issues/Issue156.hx +++ b/test/issues/Issue156.hx @@ -4,22 +4,15 @@ import flash.events.EventDispatcher; import flash.display.Sprite; /** Dispatched when a fatal error is encountered. The 'data' property contains an error string. */ -@:meta(Event(name="fatalError",type="starling.events.Event")) +@:meta(Event(name = 'fatalError', type = 'starling.events.Event')) +class Issue156 extends EventDispatcher { -class Issue156 extends EventDispatcher -{ - public function new() - { - super(); - } -} + public function new() { + super(); + } +} -class PrivateClass extends Sprite -{ +class PrivateClass extends Sprite { - public function new() - { - super(); - } } \ No newline at end of file diff --git a/test/issues/Issue158.hx b/test/issues/Issue158.hx index bdcdbe5..20dbf75 100644 --- a/test/issues/Issue158.hx +++ b/test/issues/Issue158.hx @@ -1,8 +1,7 @@ +class Issue158 { -class Issue158 -{ - public function new() - { - var cls : Class = Type.getClass(this); - } -} + public function new() { + var cls:Class = Type.getClass(this); + } + +} \ No newline at end of file diff --git a/test/issues/Issue162.hx b/test/issues/Issue162.hx index bf1d675..8f503bc 100644 --- a/test/issues/Issue162.hx +++ b/test/issues/Issue162.hx @@ -1,10 +1,9 @@ +class Issue162 { -class Issue162 -{ - public function new() - { - var s : String = "1"; - var i : Int = as3hx.Compat.parseInt(s); - var n : Float = as3hx.Compat.parseFloat(s); - } -} + public function new() { + var s:String = '1'; + var i:Int = as3hx.Compat.parseInt(s); + var n:Float = as3hx.Compat.parseFloat(s); + } + +} \ No newline at end of file diff --git a/test/issues/Issue164.hx b/test/issues/Issue164.hx index 0a2cdac..d1c27e2 100644 --- a/test/issues/Issue164.hx +++ b/test/issues/Issue164.hx @@ -1,9 +1,9 @@ import flash.display.DisplayObject; -class Issue164 -{ - public function new(rootClass : Class) - { - var d : DisplayObject = try cast(Type.createInstance(rootClass, []), DisplayObject) catch(e:Dynamic) null; - } -} +class Issue164 { + + public function new(rootClass:Class) { + var d:DisplayObject = AS3.as(Type.createInstance(rootClass, []), DisplayObject); + } + +} \ No newline at end of file diff --git a/test/issues/Issue165.hx b/test/issues/Issue165.hx index b9302cc..693d513 100644 --- a/test/issues/Issue165.hx +++ b/test/issues/Issue165.hx @@ -1,11 +1,10 @@ +class Issue165 { -class Issue165 -{ - public function new() - { - var a : Array = []; - var b : Array = a.splice(0, a.length); - var c : Array = as3hx.Compat.arraySplice(a, 0, 0, [1, 2, 3, 4, 5]); - var d : Array = a.splice(0, 1); - } -} + public function new() { + var a:Array = []; + var b:Array = a.splice(0, a.length); + var c:Array = as3hx.Compat.arraySplice(a, 0, 0, [1, 2, 3, 4, 5]); + var d:Array = a.splice(0, 1); + } + +} \ No newline at end of file diff --git a/test/issues/Issue166.hx b/test/issues/Issue166.hx index b49cce9..185109a 100644 --- a/test/issues/Issue166.hx +++ b/test/issues/Issue166.hx @@ -1,9 +1,8 @@ +class Issue166 { -class Issue166 -{ - public function new() - { - var n : Float = 10.12873894504; - n = as3hx.Compat.toFixed(n, 5); - } -} + public function new() { + var n:Float = 10.12873894504; + n = as3hx.Compat.toFixed(n, 5); + } + +} \ No newline at end of file diff --git a/test/issues/Issue167.hx b/test/issues/Issue167.hx index 62e45e2..b0441e9 100644 --- a/test/issues/Issue167.hx +++ b/test/issues/Issue167.hx @@ -1,21 +1,21 @@ import flash.geom.Point; -class Issue167 -{ - public function new(p : Point = null) - { - p = (p != null) ? p : new Point(); - - var n : Float; - n = ((n != 0 && !Math.isNaN(n))) ? n : 1; - - var i : Int; - i = (i != 0) ? i : 1; - - var s : String; - s = (s != null) ? s : "string"; - - var b : Bool; - b = (b) ? b : true; - } -} +class Issue167 { + + public function new(p:Point = null) { + p = (p != null) ? p : new Point(); + + var n:Float; + n = ((n != 0 && !Math.isNaN(n))) ? n : 1; + + var i:Int; + i = (i != 0) ? i : 1; + + var s:String; + s = (s != null) ? s : 'string'; + + var b:Bool; + b = (b) ? b : true; + } + +} \ No newline at end of file diff --git a/test/issues/Issue176.hx b/test/issues/Issue176.hx index 3f48215..1dbae18 100644 --- a/test/issues/Issue176.hx +++ b/test/issues/Issue176.hx @@ -1,10 +1,10 @@ import haxe.Constraints.Function; -class Issue176 -{ - public function new() - { - var call : Function = trace; - Reflect.callMethod(null, call, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); - } -} +class Issue176 { + + public function new() { + var call:Function = trace; + Reflect.callMethod(null, call, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]); + } + +} \ No newline at end of file diff --git a/test/issues/Issue178.hx b/test/issues/Issue178.hx index 7d6b029..b2e5b4c 100644 --- a/test/issues/Issue178.hx +++ b/test/issues/Issue178.hx @@ -1,13 +1,11 @@ +class Issue178 { -class Issue178 -{ - public function new() - { - var i : Int = 0; - while (i < 10) - { - trace(i); - i += 2; - } - } -} + public function new() { + var i:Int = 0; + while (i < 10) { + trace(i); + i += 2; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue184.hx b/test/issues/Issue184.hx index 4ef46f6..5959ceb 100644 --- a/test/issues/Issue184.hx +++ b/test/issues/Issue184.hx @@ -1,9 +1,8 @@ +class Issue184 { -class Issue184 -{ - public function new() - { - var a : Array = []; - a.insert(0, 1); - } -} + public function new() { + var a:Array = []; + a.insert(0, 1); + } + +} \ No newline at end of file diff --git a/test/issues/Issue185.hx b/test/issues/Issue185.hx index 874ccd6..52e1473 100644 --- a/test/issues/Issue185.hx +++ b/test/issues/Issue185.hx @@ -1,10 +1,9 @@ +class Issue185 { -class Issue185 -{ - public function new() - { - var a : Array = [1]; - var i : Int = as3hx.Compat.parseInt(a.splice(0, 1)[0]); - a.splice(1, 1)[0]; - } -} + public function new() { + var a:Array = [1]; + var i:Int = AS3.int(a.splice(0, 1)[0]); + a.splice(1, 1)[0]; + } + +} \ No newline at end of file diff --git a/test/issues/Issue187.hx b/test/issues/Issue187.hx index 327d8ba..8815a13 100644 --- a/test/issues/Issue187.hx +++ b/test/issues/Issue187.hx @@ -1,25 +1,23 @@ import flash.geom.Matrix3D; import flash.geom.Vector3D; -class Issue187 -{ - private static var sPoint3D : Vector3D; - public function new() - { - } - - public static function createPerspectiveProjectionMatrix( - x : Float, y : Float, width : Float, height : Float, - stageWidth : Float = 0, stageHeight : Float = 0, cameraPos : Vector3D = null, - out : Matrix3D = null) : Matrix3D - { - if (cameraPos == null) - { - cameraPos = sPoint3D; - cameraPos.setTo( - stageWidth / 2, stageHeight / 2, // -> center of stage - stageWidth / Math.tan(0.5) * 0.5 - ); - } - } -} +class Issue187 { + + private static var sPoint3D:Vector3D; + + public function new() {} + + public static function createPerspectiveProjectionMatrix( + x:Float, y:Float, width:Float, height:Float, + stageWidth:Float = 0, stageHeight:Float = 0, cameraPos:Vector3D = null, + out:Matrix3D = null):Matrix3D { + if (cameraPos == null) { + cameraPos = sPoint3D; + cameraPos.setTo( + stageWidth / 2, stageHeight / 2, // -> center of stage + stageWidth / Math.tan(0.5) * 0.5 + ); + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue192.hx b/test/issues/Issue192.hx index 49b1e30..056e341 100644 --- a/test/issues/Issue192.hx +++ b/test/issues/Issue192.hx @@ -1,15 +1,14 @@ import haxe.Constraints.Function; -class Issue192 -{ - public function new() - { - var f : Function = test; - test(s); - } - - private function test(s : String) - { - trace(s); - } -} +class Issue192 { + + public function new() { + var f:Function = test; + test(Std.string(s)); + } + + private function test(s:String) { + trace(s); + } + +} \ No newline at end of file diff --git a/test/issues/Issue198.hx b/test/issues/Issue198.hx index 898e6a9..5b45dc2 100644 --- a/test/issues/Issue198.hx +++ b/test/issues/Issue198.hx @@ -1,9 +1,8 @@ +class Issue198 { -class Issue198 -{ - public function new() - { - var b : Bool; - b = b && true; - } -} + public function new() { + var b:Bool; + b = b && true; + } + +} \ No newline at end of file diff --git a/test/issues/Issue2.hx b/test/issues/Issue2.hx index 242c202..d55b541 100644 --- a/test/issues/Issue2.hx +++ b/test/issues/Issue2.hx @@ -1,10 +1,9 @@ +class Issue2 { -class Issue2 -{ - public function new() - { - trace({ - name : "hello" - }); - } -} + public function new() { + trace({ + 'name': 'hello' + }); + } + +} \ No newline at end of file diff --git a/test/issues/Issue200.hx b/test/issues/Issue200.hx index d22ff09..c1f583e 100644 --- a/test/issues/Issue200.hx +++ b/test/issues/Issue200.hx @@ -1,9 +1,8 @@ +class Issue200 { -class Issue200 -{ - public function new() - { - Type.getClassName(Type.getClass(this)); - Type.getClassName(Issue200); - } -} + public function new() { + Type.getClassName(Type.getClass(this)); + Type.getClassName(Issue200); + } + +} \ No newline at end of file diff --git a/test/issues/Issue202.hx b/test/issues/Issue202.hx index 6676f43..fbb1bd5 100644 --- a/test/issues/Issue202.hx +++ b/test/issues/Issue202.hx @@ -1,10 +1,9 @@ +class Issue202 { -class Issue202 -{ - public function new() - { - var string : String = "She sells seashells by the seashore."; - string = new as3hx.Compat.Regex('sh', "gi").replace(string, "sch"); - trace(string); - } -} + public function new() { + var string:String = 'She sells seashells by the seashore.'; + string = new as3hx.Compat.Regex('sh', 'gi').replace(string, 'sch'); + trace(string); + } + +} \ No newline at end of file diff --git a/test/issues/Issue204.hx b/test/issues/Issue204.hx index 243c66f..d671945 100644 --- a/test/issues/Issue204.hx +++ b/test/issues/Issue204.hx @@ -1,22 +1,17 @@ import haxe.Constraints.Function; -class Issue204 -{ - public function new() - { - var i : Int; - var j : Int; - var test : Int->Float->Void = function(i : Int, n : Float) : Void - { - trace(i + n); - } - var f : Function = function() : Void - { - } - var i : Int = 10; - var n : Float = 0.1; - test(i, n); - } -} +class Issue204 { + public function new() { + var i:Int; + var j:Int; + var test:Int->Float->Void = function(i:Int, n:Float):Void { + trace(i + n); + } + var f:Function = function():Void {} + var i:Int = 10; + var n:Float = 0.1; + test(i, n); + } +} \ No newline at end of file diff --git a/test/issues/Issue205.hx b/test/issues/Issue205.hx index d9e203d..c2f3e1b 100644 --- a/test/issues/Issue205.hx +++ b/test/issues/Issue205.hx @@ -1,18 +1,16 @@ import haxe.Constraints.Function; -class Issue205 -{ - public function new() - { - var f : Function = test; - if (as3hx.Compat.getFunctionLength(f) == 1) - { - f(1); - } - } - - private function test(s : String) : Void - { - trace(s); - } -} +class Issue205 { + + public function new() { + var f:Function = test; + if (as3hx.Compat.getFunctionLength(f) == 1) { + f(1); + } + } + + private function test(s:String):Void { + trace(s); + } + +} \ No newline at end of file diff --git a/test/issues/Issue208.hx b/test/issues/Issue208.hx index 301fafe..cf80d56 100644 --- a/test/issues/Issue208.hx +++ b/test/issues/Issue208.hx @@ -1,32 +1,21 @@ import flash.errors.Error; -class Issue208 -{ - public function new() - { - try - { - var s : String = ""; - } - catch (e : Error) - { - trace(e); - } - - try - { - var s : String = ""; - } - catch (e : Error) - { - } - - try - { - var s : String = ""; - } - catch (e : Error) - { - } - } -} +class Issue208 { + + public function new() { + try { + var s:String = ''; + } catch (e:Error) { + trace(e); + } + + try { + var s:String = ''; + } catch (e:Error) {} + + try { + var s:String = ''; + } catch (e:Error) {} + } + +} \ No newline at end of file diff --git a/test/issues/Issue210.hx b/test/issues/Issue210.hx index 411d4b8..b12f7db 100644 --- a/test/issues/Issue210.hx +++ b/test/issues/Issue210.hx @@ -1,8 +1,7 @@ +class Issue210 { -class Issue210 -{ - public function new() - { - var n : Float = Math.NaN; - } -} + public function new() { + var n:Float = Math.NaN; + } + +} \ No newline at end of file diff --git a/test/issues/Issue213.hx b/test/issues/Issue213.hx index 68f04c7..c1bd85f 100644 --- a/test/issues/Issue213.hx +++ b/test/issues/Issue213.hx @@ -1,20 +1,18 @@ import starling.utils.StringUtil; -class Issue213 -{ - public function new() - { - StringUtil.format("[VertexData format=\"{0}\" numVertices={1}]", "", 1); - } +class Issue213 { + + public function new() { + StringUtil.format('[VertexData format="{0}" numVertices={1}]', '', 1); + } + } +class Foo { + public function new() { + StringTools.trim(' abc '); + StringTools.isSpace('', 0); + } -class Foo -{ - public function new() - { - StringTools.trim(" abc "); - StringTools.isSpace("", 0); - } -} +} \ No newline at end of file diff --git a/test/issues/Issue214.hx b/test/issues/Issue214.hx index 2050483..0b39062 100644 --- a/test/issues/Issue214.hx +++ b/test/issues/Issue214.hx @@ -1,19 +1,14 @@ +class Issue214 { -class Issue214 -{ - public var test(never, set) : Bool; + public function new() {} - public function new() - { - } - - private function set_test(v : Bool) : Bool - { - if (!v) - { - return v; - } - trace(v); - return v; - } -} + public var test(never, set):Bool; + private function set_test(v:Bool):Bool { + if (!v) { + return v; + } + trace(v); + return v; + } + +} \ No newline at end of file diff --git a/test/issues/Issue215.hx b/test/issues/Issue215.hx index ba981d3..77e3874 100644 --- a/test/issues/Issue215.hx +++ b/test/issues/Issue215.hx @@ -1,11 +1,11 @@ +class Issue215 { -class Issue215 -{ - private static var regex : as3hx.Compat.Regex = new as3hx.Compat.Regex('sh', "gi"); - public function new(string2 : String) - { - var string : String = "She sells seashells by the seashore."; - string = regex.replace(string, "sch"); - string = regex.replace(string2, "sch"); - } -} + private static var regex(default, never):as3hx.Compat.Regex = new as3hx.Compat.Regex('sh', 'gi'); + + public function new(string2:String) { + var string:String = 'She sells seashells by the seashore.'; + string = regex.replace(string, 'sch'); + string = regex.replace(string2, 'sch'); + } + +} \ No newline at end of file diff --git a/test/issues/Issue218.hx b/test/issues/Issue218.hx index bc9152c..cce0980 100644 --- a/test/issues/Issue218.hx +++ b/test/issues/Issue218.hx @@ -1,11 +1,10 @@ +class Issue218 { -class Issue218 -{ - public function new(sub : String, sub2 : Dynamic) - { - var string : String = "She sells seashells by the seashore."; - string = StringTools.replace(string, "sh", "sch"); - string = StringTools.replace(string, sub, "sch"); - string = StringTools.replace(string, Std.string(sub2), "sch"); - } -} + public function new(sub:String, sub2:Dynamic) { + var string:String = 'She sells seashells by the seashore.'; + string = StringTools.replace(string, 'sh', 'sch'); + string = StringTools.replace(string, sub, 'sch'); + string = StringTools.replace(string, Std.string(sub2), 'sch'); + } + +} \ No newline at end of file diff --git a/test/issues/Issue223.hx b/test/issues/Issue223.hx index 0583c6b..d590a52 100644 --- a/test/issues/Issue223.hx +++ b/test/issues/Issue223.hx @@ -1,9 +1,8 @@ +class Issue223 { -class Issue223 -{ - public function new() - { - var result : Float = Math.min(Math.min(Math.min(1, 2), 3), 4); - var result : Float = Math.max(Math.max(Math.max(1, 2), 3), 4); - } -} + public function new() { + var result:Float = Math.min(Math.min(Math.min(1, 2), 3), 4); + var result:Float = Math.max(Math.max(Math.max(1, 2), 3), 4); + } + +} \ No newline at end of file diff --git a/test/issues/Issue226.hx b/test/issues/Issue226.hx index d7bce8e..65bfa27 100644 --- a/test/issues/Issue226.hx +++ b/test/issues/Issue226.hx @@ -1,10 +1,9 @@ +class Issue226 { -class Issue226 -{ - public function new() - { - var dictionary : haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); - dictionary.set("key", true); - dictionary.remove("key"); - } -} + public function new() { + var dictionary:haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); + dictionary.set('key', true); + dictionary.remove('key'); + } + +} \ No newline at end of file diff --git a/test/issues/Issue228.hx b/test/issues/Issue228.hx index 72becc8..7ca02b1 100644 --- a/test/issues/Issue228.hx +++ b/test/issues/Issue228.hx @@ -1,9 +1,8 @@ +class Issue228 { -class Issue228 -{ - public function new() - { - var n : Float = Math.POSITIVE_INFINITY; - var n : Float = Math.NEGATIVE_INFINITY; - } -} + public function new() { + var n:Float = Math.POSITIVE_INFINITY; + var n:Float = Math.NEGATIVE_INFINITY; + } + +} \ No newline at end of file diff --git a/test/issues/Issue23.hx b/test/issues/Issue23.hx index 2554783..88d7619 100644 --- a/test/issues/Issue23.hx +++ b/test/issues/Issue23.hx @@ -1,23 +1,21 @@ +class Issue23 { -class Issue23 -{ - - public function new(i : Int, n : Float, ss : String) - { - var a : Int = i; - var a2 : Int = i; - var a3 : Int = as3hx.Compat.parseInt(n); - var a4 : Int = as3hx.Compat.parseInt(s); - - var b : Float = n; - var b2 : Float = n; - var b3 : Float = i; - var b4 : Float = as3hx.Compat.parseFloat(s); - - var s : String = Std.string(n); - var s2 : String = ss; - var s3 : String = ss; - var s4 : String = Std.string(i); - var s5 : String = Std.string(i); - } -} + public function new(i:Int, n:Float, ss:String) { + var a:Int = i; + var a2:Int = i; + var a3:Int = AS3.int(n); + var a4:Int = AS3.int(s); + + var b:Float = n; + var b2:Float = n; + var b3:Float = i; + var b4:Float = as3hx.Compat.parseFloat(s); + + var s:String = Std.string(n); + var s2:String = ss; + var s3:String = ss; + var s4:String = Std.string(i); + var s5:String = Std.string(i); + } + +} \ No newline at end of file diff --git a/test/issues/Issue230.hx b/test/issues/Issue230.hx index df02fbc..af024f1 100644 --- a/test/issues/Issue230.hx +++ b/test/issues/Issue230.hx @@ -1,8 +1,7 @@ +class Issue230 { -class Issue230 -{ - public function new() - { - var d : haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); - } -} + public function new() { + var d:haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); + } + +} \ No newline at end of file diff --git a/test/issues/Issue234.hx b/test/issues/Issue234.hx index 7638b0f..91dca21 100644 --- a/test/issues/Issue234.hx +++ b/test/issues/Issue234.hx @@ -1,9 +1,9 @@ import flash.display3D.Context3D; -class Issue234 -{ - public function new() - { - var supportsVideoTexture : Bool = Reflect.field(Context3D, "supportsVideoTexture"); - } -} +class Issue234 { + + public function new() { + var supportsVideoTexture:Bool = Reflect.getProperty(Context3D, 'supportsVideoTexture') != null; + } + +} \ No newline at end of file diff --git a/test/issues/Issue235.hx b/test/issues/Issue235.hx index d6c6ec4..d09bdec 100644 --- a/test/issues/Issue235.hx +++ b/test/issues/Issue235.hx @@ -1,15 +1,10 @@ +class Issue235 { -class Issue235 -{ - public function new() - { - var mask : Int; - var flag : Int; - if ((i & flag) == 0) - { - } - if ((i & flag) != 0) - { - } - } -} + public function new() { + var mask:Int; + var flag:Int; + if ((i & flag) == 0) {} + if ((i & flag) != 0) {} + } + +} \ No newline at end of file diff --git a/test/issues/Issue238.hx b/test/issues/Issue238.hx index 61ba94d..66e47ff 100644 --- a/test/issues/Issue238.hx +++ b/test/issues/Issue238.hx @@ -1,10 +1,9 @@ +class Issue238 { -class Issue238 -{ - public function new(x : Int, y : Int) - { - x = x & y; - x = x | y; - x = x ^ y; - } -} + public function new(x:Int, y:Int) { + x = x & y; + x = x | y; + x = x ^ y; + } + +} \ No newline at end of file diff --git a/test/issues/Issue24.hx b/test/issues/Issue24.hx index 371a565..8c95832 100644 --- a/test/issues/Issue24.hx +++ b/test/issues/Issue24.hx @@ -1,47 +1,43 @@ +class Issue24 { -class Issue24 -{ - public function new(a : Int, b : Int, c : Int, n : Float) - { - c = a; - c = as3hx.Compat.parseInt(a / b); - c = as3hx.Compat.parseInt(n); - c = as3hx.Compat.parseInt(n - a); - c = as3hx.Compat.parseInt(n + a); - c = as3hx.Compat.parseInt(n * a); - c = as3hx.Compat.parseInt(n) << a; - c = as3hx.Compat.parseInt(n) >> a; - c = as3hx.Compat.parseInt(n) >>> a; - c = as3hx.Compat.parseInt(n) & a; - c = as3hx.Compat.parseInt(n) ^ a; - c = as3hx.Compat.parseInt(n) | a; - c = ~as3hx.Compat.parseInt(n); - c = as3hx.Compat.parseInt(n) | as3hx.Compat.parseInt(n); - - var i : Int = as3hx.Compat.parseInt(a / b); - var j : Int = as3hx.Compat.parseInt(n); - var k : Int = as3hx.Compat.parseInt(n - j); - var k : Int = as3hx.Compat.parseInt(n + j); - var k : Int = as3hx.Compat.parseInt(n * j); - var k : Int = as3hx.Compat.parseInt(n) << j; - var k : Int = as3hx.Compat.parseInt(n) >> j; - var k : Int = as3hx.Compat.parseInt(n) >>> j; - var k : Int = as3hx.Compat.parseInt(n) & j; - var k : Int = j & as3hx.Compat.parseInt(n); - var k : Int = as3hx.Compat.parseInt(n) ^ j; - var k : Int = j ^ as3hx.Compat.parseInt(n); - var k : Int = as3hx.Compat.parseInt(n) | j; - var k : Int = j | as3hx.Compat.parseInt(n); - var n2 : Float; - var k : Int = ~as3hx.Compat.parseInt(n2); - - if ((as3hx.Compat.parseInt(n) & as3hx.Compat.parseInt(n - 1)) == 0) - { - } - } - - public function getInt(n : Float) : Int - { - return as3hx.Compat.parseInt(n + 10); - } -} + public function new(a:Int, b:Int, c:Int, n:Float) { + c = a; + c = AS3.int(a / b); + c = AS3.int(n); + c = AS3.int(n - a); + c = AS3.int(n + a); + c = AS3.int(n * a); + c = AS3.int(n) << a; + c = AS3.int(n) >> a; + c = AS3.int(n) >>> a; + c = AS3.int(n) & a; + c = AS3.int(n) ^ a; + c = AS3.int(n) | a; + c = ~AS3.int(n); + c = AS3.int(n) | AS3.int(n); + + var i:Int = AS3.int(a / b); + var j:Int = AS3.int(n); + var k:Int = AS3.int(n - j); + var k:Int = AS3.int(n + j); + var k:Int = AS3.int(n * j); + var k:Int = AS3.int(n << j); + var k:Int = AS3.int(n >> j); + var k:Int = AS3.int(n >>> j); + var k:Int = AS3.int(AS3.int(n) & j); + var k:Int = AS3.int(j & AS3.int(n)); + var k:Int = AS3.int(n ^ j); + var k:Int = AS3.int(j ^ n); + var k:Int = AS3.int(n | j); + var k:Int = AS3.int(j | n); + var n2:Float; + var k:Int = AS3.int(~n2); + + if ((AS3.int(n) & AS3.int(n - 1)) == 0) {} + } + + public function getInt(n:Float):Int { + return AS3.int(n + 10); + } + +} \ No newline at end of file diff --git a/test/issues/Issue241.hx b/test/issues/Issue241.hx index a5cbf0b..ed23731 100644 --- a/test/issues/Issue241.hx +++ b/test/issues/Issue241.hx @@ -1,9 +1,8 @@ +class Issue241 { -class Issue241 -{ - public function new() - { - var d : haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); - trace(Std.is(d, haxe.ds.ObjectMap)); - } -} + public function new() { + var d:haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); + trace(Std.is(d, haxe.ds.ObjectMap)); + } + +} \ No newline at end of file diff --git a/test/issues/Issue244.hx b/test/issues/Issue244.hx index 9ae3cb5..6a01ce8 100644 --- a/test/issues/Issue244.hx +++ b/test/issues/Issue244.hx @@ -1,16 +1,15 @@ +class Issue244 { -class Issue244 -{ - public function new(c : Dynamic) - { - var a : Dynamic = { }; - if (Reflect.field(a, Std.string(10)) == null) { - Reflect.setField(a, Std.string(10), {}); - } - var b : Dynamic = Reflect.field(a, Std.string(10)); - if (Reflect.field(a, Std.string(10)) == null) { - Reflect.setField(a, Std.string(10), {}); - } - c = Reflect.field(a, Std.string(10)); - } -} + public function new(c:Dynamic) { + var a:Dynamic = {}; + if (Reflect.field(a, Std.string(10)) == null) { + Reflect.setField(a, Std.string(10), {}); + } + var b:Dynamic = Reflect.field(a, Std.string(10)); + if (Reflect.field(a, Std.string(10)) == null) { + Reflect.setField(a, Std.string(10), {}); + } + c = Reflect.field(a, Std.string(10)); + } + +} \ No newline at end of file diff --git a/test/issues/Issue246.hx b/test/issues/Issue246.hx index 13b60d2..586b75c 100644 --- a/test/issues/Issue246.hx +++ b/test/issues/Issue246.hx @@ -1,19 +1,15 @@ +class Issue246 { -class Issue246 -{ - public var steps(never, set) : Int; + public function new() {} - public function new() - { - } - - private var _steps : Int = 8; - private function set_steps(val : Int) : Int - { - if (_steps == val) - { - return val; - } - return val; - } -} + private var _steps:Int = 8; + + public var steps(never, set):Int; + private function set_steps(val:Int):Int { + if (_steps == val) { + return val; + } + return val; + } + +} \ No newline at end of file diff --git a/test/issues/Issue247.hx b/test/issues/Issue247.hx index 287ff1e..91aae17 100644 --- a/test/issues/Issue247.hx +++ b/test/issues/Issue247.hx @@ -1,11 +1,7 @@ +class Issue247 { -class Issue247 -{ - public function new(args : Array = null) - { - } - - public function test(args : Array = null) : Void - { - } -} + public function new(args:Array = null) {} + + public function test(args:Array = null):Void {} + +} \ No newline at end of file diff --git a/test/issues/Issue250.hx b/test/issues/Issue250.hx index 9c7213e..7f6a237 100644 --- a/test/issues/Issue250.hx +++ b/test/issues/Issue250.hx @@ -1,23 +1,19 @@ +class Issue250 { -class Issue250 -{ - - private static var globalStorage : Dynamic = {}; - - public function new() - { - var o : Dynamic = {}; - } - - private var localStorage : Dynamic = {}; - - private function onTest() : Void - { - var params : Dynamic = {}; - } - - private function getObject() : Dynamic - { - return {}; - } -} + private static var globalStorage:Dynamic = {}; + + public function new() { + var o:Dynamic = {}; + } + + private var localStorage:Dynamic = {}; + + private function onTest():Void { + var params:Dynamic = {}; + } + + private function getObject():Dynamic { + return {}; + } + +} \ No newline at end of file diff --git a/test/issues/Issue254.hx b/test/issues/Issue254.hx index 33f4440..bf7cfd2 100644 --- a/test/issues/Issue254.hx +++ b/test/issues/Issue254.hx @@ -1,16 +1,13 @@ +class Issue254 { -class Issue254 -{ - public function new() - { - if (true) - { - return; - } - - if (true) - { - return; - } - } -} + public function new() { + if (true) { + return; + } + + if (true) { + return; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue255.hx b/test/issues/Issue255.hx index d584930..3622f39 100644 --- a/test/issues/Issue255.hx +++ b/test/issues/Issue255.hx @@ -1,8 +1,7 @@ +class Issue255 { -class Issue255 -{ - private var NORMAL(default, never) : Int = 0; - public function new() - { - } -} + private var NORMAL(default, never):Int = 0; + + public function new() {} + +} \ No newline at end of file diff --git a/test/issues/Issue257.hx b/test/issues/Issue257.hx index a0a9c6c..9d39690 100644 --- a/test/issues/Issue257.hx +++ b/test/issues/Issue257.hx @@ -1,10 +1,10 @@ import flash.net.URLRequest; -class Issue257 -{ - public function new() - { - var req : URLRequest = new URLRequest("http://www.adobe.com/"); - flash.Lib.getURL(req, "_blank"); - } -} +class Issue257 { + + public function new() { + var req:URLRequest = new URLRequest('http://www.adobe.com/'); + flash.Lib.getURL(req, '_blank'); + } + +} \ No newline at end of file diff --git a/test/issues/Issue26.hx b/test/issues/Issue26.hx index ab7c378..e4fcb2d 100644 --- a/test/issues/Issue26.hx +++ b/test/issues/Issue26.hx @@ -1,44 +1,31 @@ +class Issue26 { -class Issue26 -{ - - public function new() - { - } } +class Foo { + + @:allow() + private function foo():Void {} + + private function some():Void {} + + public function new() {} -class Foo -{ - @:allow() - private function foo() : Void - { - } - - private function some() : Void - { - } - - public function new() - { - } } -class Bar extends Foo -{ - @:allow() - override private function foo() : Void - { - super.foo(); - } - - override private function some() : Void - { - super.some(); - } - - public function new() - { - super(); - } +class Bar extends Foo { + + @:allow() + override private function foo():Void { + super.foo(); + } + + override private function some():Void { + super.some(); + } + + public function new() { + super(); + } + } \ No newline at end of file diff --git a/test/issues/Issue261.hx b/test/issues/Issue261.hx index 48d0fcb..9f80497 100644 --- a/test/issues/Issue261.hx +++ b/test/issues/Issue261.hx @@ -1,9 +1,8 @@ +class Issue261 { -class Issue261 -{ - public function new() - { - var a : Array = [{ }]; - Reflect.setField(a[a.length - 1], "some", 10); - } -} + public function new() { + var a:Array = cast [{}]; + Reflect.setField(a[a.length - 1], 'some', 10); + } + +} \ No newline at end of file diff --git a/test/issues/Issue261_1.hx b/test/issues/Issue261_1.hx index 353451b..5c09717 100644 --- a/test/issues/Issue261_1.hx +++ b/test/issues/Issue261_1.hx @@ -1,13 +1,10 @@ +class Issue2611 { -class Issue2611 -{ - public function randomGen(param1 : Int) : Int - { - var _loc2_ : Int = Math.floor(Math.random() * param1); - return _loc2_; - } + public function randomGen(param1:Int):Int { + var _loc2_:Int = Math.floor(Math.random() * param1); + return _loc2_; + } - public function new() - { - } -} + public function new() {} + +} \ No newline at end of file diff --git a/test/issues/Issue261_2.hx b/test/issues/Issue261_2.hx index a45b074..7193082 100644 --- a/test/issues/Issue261_2.hx +++ b/test/issues/Issue261_2.hx @@ -1,12 +1,9 @@ +class Issue2612 { -class Issue2612 -{ - private function frame1() : Dynamic - { - gotoAndPlay((this.randomGen(48) + 1) * 4 + 1); - } + private function frame1():Dynamic { + gotoAndPlay((this.randomGen(48) + 1) * 4 + 1); + } - public function new() - { - } -} + public function new() {} + +} \ No newline at end of file diff --git a/test/issues/Issue264.hx b/test/issues/Issue264.hx index 568b2d5..4e5440d 100644 --- a/test/issues/Issue264.hx +++ b/test/issues/Issue264.hx @@ -1,25 +1,22 @@ +import flash.errors.Error; -class Issue264 -{ - public function new() - { - var obj : Dynamic = {}; - var message : String; - - // error inside - if (Std.is(obj.error, Error)) - { - message = obj.error.message; - } - // error event inside - else if (Std.is(obj.error, ErrorEvent)) - { - message = obj.error.text; - } - // unknown - else - { - message = Std.string(obj.error); - } - } -} +class Issue264 { + + public function new() { + var obj:Dynamic = {}; + var message:String; + + // error inside + if (Std.is(Reflect.field(obj, 'error'), Error)) { + message = AS3.string(Reflect.field(Reflect.field(obj, 'error'), 'message')); + }// error event inside + else if (Std.is(Reflect.field(obj, 'error'), ErrorEvent)) { + message = AS3.string(Reflect.field(Reflect.field(obj, 'error'), 'text')); + }// unknown + else { + message = Std.string(Std.string(Reflect.field(obj, 'error'))); + } + + } + +} \ No newline at end of file diff --git a/test/issues/Issue265.hx b/test/issues/Issue265.hx index de4828a..b4684ba 100644 --- a/test/issues/Issue265.hx +++ b/test/issues/Issue265.hx @@ -1,8 +1,7 @@ +class Issue265 { -class Issue265 -{ - public function new() - { - var color : Int = as3hx.Compat.parseInt("0xffffff", 16); - } -} + public function new() { + var color:Int = as3hx.Compat.parseInt('0xffffff', 16); + } + +} \ No newline at end of file diff --git a/test/issues/Issue27.hx b/test/issues/Issue27.hx index b6e3e10..d3c8c23 100644 --- a/test/issues/Issue27.hx +++ b/test/issues/Issue27.hx @@ -1,14 +1,12 @@ +class Issue27 { -class Issue27 -{ - public function new(condition : Bool) - { - if (condition) - { - trace(1); - } - - //some comment - trace(2); - } -} + public function new(condition:Bool) { + if (condition) { + trace(1); + } + + //some comment + trace(2); + } + +} \ No newline at end of file diff --git a/test/issues/Issue273.hx b/test/issues/Issue273.hx index ed36a5d..04f7bb2 100644 --- a/test/issues/Issue273.hx +++ b/test/issues/Issue273.hx @@ -1,30 +1,28 @@ +class Issue273 { -class Issue273 -{ - public function new() - { - var param1 = "j"; - switch (param1) - { - case "a": - return 1; - case "b": - return 2; - case "c": - return 3; - case "d": - return 4; - case "e": - return 5; - case "f": - return 6; - case "g": - return 7; - case "h": - return 8; - /* covers case "i": */ - default: - return 9; - } - } -} + public function new() { + var param1 = 'j'; + switch (param1) { + case 'a': + return 1; + case 'b': + return 2; + case 'c': + return 3; + case 'd': + return 4; + case 'e': + return 5; + case 'f': + return 6; + case 'g': + return 7; + case 'h': + return 8; + /* covers case 'i': */ + case _: + return 9; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue274.hx b/test/issues/Issue274.hx index 8b03344..1faf53e 100644 --- a/test/issues/Issue274.hx +++ b/test/issues/Issue274.hx @@ -1,12 +1,11 @@ +class Issue274 { -class Issue274 -{ - public function new() - { - var a : Int = 1; - var b : Int = 1; - var c : Int = 1; - var e : Int = 1; - e += ((a > b || c > b)) ? 1 : 0; - } -} + public function new() { + var a:Int = 1; + var b:Int = 1; + var c:Int = 1; + var e:Int = 1; + e += ((a > b || c > b)) ? 1 : 0; + } + +} \ No newline at end of file diff --git a/test/issues/Issue275.hx b/test/issues/Issue275.hx index 7137a57..1c565f7 100644 --- a/test/issues/Issue275.hx +++ b/test/issues/Issue275.hx @@ -1,12 +1,11 @@ +class Issue275 { -class Issue275 -{ - public function new() - { - var a : Int = 1; - var b : Int = 1; - var c : Int = 1; - var d : Int = 1; - d += (a > b || c != 0) ? 1 : 0; - } -} + public function new() { + var a:Int = 1; + var b:Int = 1; + var c:Int = 1; + var d:Int = 1; + d += (a > b || c != 0) ? 1 : 0; + } + +} \ No newline at end of file diff --git a/test/issues/Issue277.hx b/test/issues/Issue277.hx index 6ba1e37..a3d5fa3 100644 --- a/test/issues/Issue277.hx +++ b/test/issues/Issue277.hx @@ -1,51 +1,29 @@ +class Issue277 { -class Issue277 -{ - public function new() - { - if (1) - { - a(); - } - else if (2) - { - b(); - } - else if (3) - { - c(); - } - else if (4) - { - d(); - } - else if (5) - { - e(); - } - else if (6) - { - f(); - } - else if (7) - { - g(); - } - else if (8) - { - h(); - } - else if (9) - { - i(); - } - else if (10) - { - j(); - } - else - { - k(); - } - } -} + public function new() { + if (1) { + a(); + } else if (2) { + b(); + } else if (3) { + c(); + } else if (4) { + d(); + } else if (5) { + e(); + } else if (6) { + f(); + } else if (7) { + g(); + } else if (8) { + h(); + } else if (9) { + i(); + } else if (10) { + j(); + } else { + k(); + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue28.hx b/test/issues/Issue28.hx index dc417e6..3408727 100644 --- a/test/issues/Issue28.hx +++ b/test/issues/Issue28.hx @@ -1,34 +1,28 @@ +class Issue28 { -class Issue28 -{ - - private static var b : Bool = true; - private static var o : Dynamic = (b) ? { } : null; - - public function new() - { - var a : Int = 1; - var b : Int = 1; - var c : Int = 1; - var c : Int = (((a != 0 || b != 0) ? 1 : 0) && c != 0) ? 1 : 0; - } - - private function test(a : Array, b : Bool) : Int - { - return (a != null || b) ? 1 : 0; - } - - private function test2(a : Array, b : Bool) : Int - { - if ((a != null || b) ? true : false) - { - return 1; - } - return 0; - } - - private function test3(b : Bool, o : Dynamic) : Void - { - o = (b) ? { } : null; - } -} + private static var b:Bool = true; + private static var o:Dynamic = (b) ? {} : null; + + public function new() { + var a:Int = 1; + var b:Int = 1; + var c:Int = 1; + var c:Int = (((a != 0 || b != 0) ? 1 : 0) && c != 0) ? 1 : 0; + } + + private function test(a:Array, b:Bool):Int { + return (a != null || b) ? 1 : 0; + } + + private function test2(a:Array, b:Bool):Int { + if ((a != null || b) ? true : false) { + return 1; + } + return 0; + } + + private function test3(b:Bool, o:Dynamic):Void { + o = (b) ? {} : null; + } + +} \ No newline at end of file diff --git a/test/issues/Issue285.hx b/test/issues/Issue285.hx index 8f7d967..1f3704b 100644 --- a/test/issues/Issue285.hx +++ b/test/issues/Issue285.hx @@ -1,13 +1,11 @@ +class Issue285 { -class Issue285 -{ - public function new() - { - var x : Int = 1; - var max : Int = 10; - while (x < max) - { - ++x; - } - } -} + public function new() { + var x:Int = 1; + var max:Int = 10; + while (x < max) { + ++x; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue29.hx b/test/issues/Issue29.hx index 330d3bc..81fc1fb 100644 --- a/test/issues/Issue29.hx +++ b/test/issues/Issue29.hx @@ -1,14 +1,12 @@ +class Issue29 { -class Issue29 -{ - public function new() - { - var stuff : Array = []; - var i : Int = 0; - while (i < 10 || stuff[i] != null) - { - trace(i); - i++; - } - } -} + public function new() { + var stuff:Array = []; + var i:Int = 0; + while (i < 10 || stuff[i] != null) { + trace(i); + i++; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue293.hx b/test/issues/Issue293.hx index efb8f64..42a8721 100644 --- a/test/issues/Issue293.hx +++ b/test/issues/Issue293.hx @@ -1,11 +1,9 @@ +class Issue293 { -class Issue293 -{ - public function new() - { - var i : Int = as3hx.Compat.setTimeout(function(args : Array = null) - { - trace(args); - }, (1 + 1) * 1000, [1, 2, 3]); - } -} + public function new() { + var i:Int = as3hx.Compat.setTimeout(function(args:Array = null) { + trace(args); + }, (1 + 1) * 1000, [1, 2, 3]); + } + +} \ No newline at end of file diff --git a/test/issues/Issue296.as b/test/issues/Issue296.as new file mode 100644 index 0000000..6c9a207 --- /dev/null +++ b/test/issues/Issue296.as @@ -0,0 +1,24 @@ +package { + public class Issue296 { + public function Issue296() { + var a:Array = [1, 2, 3]; + for(var i:int = 0; i < a.length; i++) { + trace(a.pop()); + } + for(var i:int = 0; i < a.pop(); i++) { + trace(i); + } + for(var i:int = 0; i < a.pop() + 10; i++) { + trace(i); + } + for(var i:int = 0; i < a.length; i++) { + if(i < 3) trace(a.pop()); + else continue; + } + for(var i:int = 0; i < a.length; i++) { + trace(a.pop()); + continue; + } + } + } +} \ No newline at end of file diff --git a/test/issues/Issue296.hx b/test/issues/Issue296.hx new file mode 100644 index 0000000..8e7964f --- /dev/null +++ b/test/issues/Issue296.hx @@ -0,0 +1,27 @@ +class Issue296 { + + public function new() { + var a:Array = [1, 2, 3]; + for (i in 0...a.length) { + trace(a.pop()); + } + for (i in 0...a.pop()) { + trace(i); + } + for (i in 0...a.pop() + 10) { + trace(i); + } + for (i in 0...a.length) { + if (i < 3) { + trace(a.pop()); + } else { + continue; + } + } + for (i in 0...a.length) { + trace(a.pop()); + continue; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue296_1.as b/test/issues/Issue296_1.as new file mode 100644 index 0000000..59027f8 --- /dev/null +++ b/test/issues/Issue296_1.as @@ -0,0 +1,10 @@ +package { + public class Issue296 { + public function Issue296() { + function some(i:int):Boolean { return i < 10; } + for(var i:int = 0; some(i); i++) { + trace(a.pop()); + } + } + } +} \ No newline at end of file diff --git a/test/issues/Issue296_1.hx b/test/issues/Issue296_1.hx new file mode 100644 index 0000000..bf86d53 --- /dev/null +++ b/test/issues/Issue296_1.hx @@ -0,0 +1,14 @@ +class Issue296 { + + public function new() { + var some:Int->Bool = function(i:Int):Bool { + return i < 10; + } + var i:Int = 0; + while (some(i)) { + trace(a.pop()); + i++; + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue298.hx b/test/issues/Issue298.hx index 2e42f91..29608fe 100644 --- a/test/issues/Issue298.hx +++ b/test/issues/Issue298.hx @@ -1,8 +1,7 @@ +class Issue298 { -class Issue298 -{ - public function new() - { - var v : Float = 1.79E+308; - } -} + public function new() { + var v:Float = 1.79E+308; + } + +} \ No newline at end of file diff --git a/test/issues/Issue300.hx b/test/issues/Issue300.hx index b3341e7..0121b78 100644 --- a/test/issues/Issue300.hx +++ b/test/issues/Issue300.hx @@ -1,8 +1,7 @@ +class Issue300 { -class Issue300 -{ - public function new() - { - trace(as3hx.Compat.typeof(3)); - } -} + public function new() { + trace(as3hx.Compat.typeof(3)); + } + +} \ No newline at end of file diff --git a/test/issues/Issue302.hx b/test/issues/Issue302.hx index 87c62a5..d5fd5d5 100644 --- a/test/issues/Issue302.hx +++ b/test/issues/Issue302.hx @@ -1,7 +1,5 @@ +class Issue302 { -class Issue302 -{ - public function new(i : Int = 1) - { - } -} + public function new(i:Int = 1) {} + +} \ No newline at end of file diff --git a/test/issues/Issue303.hx b/test/issues/Issue303.hx index 0fc2be9..6eaffd5 100644 --- a/test/issues/Issue303.hx +++ b/test/issues/Issue303.hx @@ -1,7 +1,5 @@ +class Issue303 { -class Issue303 -{ - public function new(i : Int = 1000) - { - } -} + public function new(i:Int = 1000) {} + +} \ No newline at end of file diff --git a/test/issues/Issue314.hx b/test/issues/Issue314.hx index 35c2882..d1d40b4 100644 --- a/test/issues/Issue314.hx +++ b/test/issues/Issue314.hx @@ -1,17 +1,15 @@ +class Issue314 { -class Issue314 -{ - public function new() - { - } - public function hide1(param1 : Dynamic) : Dynamic - { - Reflect.setField(this, Std.string(param1), false); - Reflect.field(this, Std.string(param1)).visible = false; - } - public function hide2(param1 : Dynamic) : Dynamic - { - Reflect.setField(this, Std.string(param1), "nothing"); - Reflect.field(this, Std.string(param1)).collision.currentObject = "nothing"; - } -} + public function new() {} + + public function hide1(param1:Dynamic):Dynamic { + Reflect.setProperty(this, Std.string(param1), false); + Reflect.getProperty(this, Std.string(param1)).visible = false; + } + + public function hide2(param1:Dynamic):Dynamic { + Reflect.setProperty(this, Std.string(param1), 'nothing'); + Reflect.getProperty(this, Std.string(param1)).collision.currentObject = 'nothing'; + } + +} \ No newline at end of file diff --git a/test/issues/Issue32.hx b/test/issues/Issue32.hx index 3429ab9..846fec5 100644 --- a/test/issues/Issue32.hx +++ b/test/issues/Issue32.hx @@ -1,13 +1,12 @@ +class Issue32 { -class Issue32 -{ - public function new() - { - var a : Array = []; - var b : Array = a.copy(); - var c : Array = a.concat([1, 2, 3, 4]); - var d : Array = []; - var e : Array = d.copy(); - var f : Array = d.concat([1, 2, 3, 4]); - } -} + public function new() { + var a:Array = []; + var b:Array = a.copy(); + var c:Array = a.concat([1, 2, 3, 4]); + var d:Array = []; + var e:Array = d.copy(); + var f:Array = d.concat([1, 2, 3, 4]); + } + +} \ No newline at end of file diff --git a/test/issues/Issue323.as b/test/issues/Issue323.as new file mode 100644 index 0000000..e98cbed --- /dev/null +++ b/test/issues/Issue323.as @@ -0,0 +1,12 @@ +package { + public class Issue323 { + private var friendsList:Vector.; + public function Issue323() { + for ( var i:int = 0; i < friendsList.length; i++ ) + { + if ( friendsList[i].bSelected ) + true; + } + } + } +} \ No newline at end of file diff --git a/test/issues/Issue323.hx b/test/issues/Issue323.hx new file mode 100644 index 0000000..de82c1e --- /dev/null +++ b/test/issues/Issue323.hx @@ -0,0 +1,12 @@ +class Issue323 { + + private var friendsList:Array; + + public function new() { + for (i in 0...friendsList.length) { + if (AS3.as(Reflect.field(friendsList[i], 'bSelected'), Bool)) { + } + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue36.hx b/test/issues/Issue36.hx index e4c366e..678b94f 100644 --- a/test/issues/Issue36.hx +++ b/test/issues/Issue36.hx @@ -1,9 +1,8 @@ +class Issue36 { -class Issue36 -{ - public function new() - { - var s : String = ""; - var c : String = s.charCodeAt(0); - } -} + public function new() { + var s:String = ''; + var c:String = Std.string(s.charCodeAt(0)); + } + +} \ No newline at end of file diff --git a/test/issues/Issue37.hx b/test/issues/Issue37.hx index 64fd94f..8e0074c 100644 --- a/test/issues/Issue37.hx +++ b/test/issues/Issue37.hx @@ -1,9 +1,8 @@ +class Issue37 { -class Issue37 -{ - public function new() - { - var s : String = "test"; - var xml : FastXML = FastXML.parse(s); - } -} + public function new() { + var s:String = 'test'; + var xml:FastXML = FastXML.parse(s); + } + +} \ No newline at end of file diff --git a/test/issues/Issue38.hx b/test/issues/Issue38.hx index b0ac77f..81ae54e 100644 --- a/test/issues/Issue38.hx +++ b/test/issues/Issue38.hx @@ -1,15 +1,13 @@ +class Issue38 { -class Issue38 -{ - public function new() - { - var a : String = ""; - switch (a) - { - case "a", "c": - trace("a"); - default: - trace("b"); - } - } -} + public function new() { + var a:String = ''; + switch (a) { + case 'a', 'c': + trace('a'); + case _: + trace('b'); + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue52.hx b/test/issues/Issue52.hx index 0517acc..ace0476 100644 --- a/test/issues/Issue52.hx +++ b/test/issues/Issue52.hx @@ -1,8 +1,7 @@ +class Issue52 { -class Issue52 -{ - private var i = 10; - public function new() - { - } -} + private var i = 10; + + public function new() {} + +} \ No newline at end of file diff --git a/test/issues/Issue53.hx b/test/issues/Issue53.hx index 35b365f..abf0f7d 100644 --- a/test/issues/Issue53.hx +++ b/test/issues/Issue53.hx @@ -1,13 +1,11 @@ +class Issue53 { -class Issue53 -{ - public function new() - { - } - private static var Issue53_static_initializer = { - Issue52; - Issue103; - true; - } + public function new() {} -} + private static var Issue53_static_initializer = { + Issue52; + Issue103; + true; + } + +} \ No newline at end of file diff --git a/test/issues/Issue54.hx b/test/issues/Issue54.hx index 119542e..e29d61f 100644 --- a/test/issues/Issue54.hx +++ b/test/issues/Issue54.hx @@ -1,8 +1,7 @@ +class Issue54 { -class Issue54 -{ - private var d : haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); - public function new(d : haxe.ds.ObjectMap = null) - { - } -} + private var d:haxe.ds.ObjectMap = new haxe.ds.ObjectMap(); + + public function new(d:haxe.ds.ObjectMap = null) {} + +} \ No newline at end of file diff --git a/test/issues/Issue63.hx b/test/issues/Issue63.hx index bb713c3..29330d4 100644 --- a/test/issues/Issue63.hx +++ b/test/issues/Issue63.hx @@ -1,12 +1,12 @@ import flash.utils.ByteArray; -class Issue63 -{ - public function new() - { - var a : Array = new Array(); - as3hx.Compat.setArrayLength(a, 10); - var bytes : ByteArray = new ByteArray(); - bytes.length = 0; - } -} +class Issue63 { + + public function new() { + var a:Array = new Array(); + as3hx.Compat.setArrayLength(a, 10); + var bytes:ByteArray = as3hx.Compat.newByteArray(); + bytes.length = 0; + } + +} \ No newline at end of file diff --git a/test/issues/Issue64.as b/test/issues/Issue64.as index 0ad0b8d..d3fb1ab 100644 --- a/test/issues/Issue64.as +++ b/test/issues/Issue64.as @@ -8,7 +8,7 @@ package { var time:Number; var events:Vector.; for (var i:int = 0, n:int = timelines.length; i < n; i++) { - timelienes[i].apply(skeleton, lastTime, time, events, 1); + timelines[i].apply(skeleton, lastTime, time, events, 1); } } } diff --git a/test/issues/Issue64.hx b/test/issues/Issue64.hx index 68e0fed..520eca5 100644 --- a/test/issues/Issue64.hx +++ b/test/issues/Issue64.hx @@ -1,33 +1,27 @@ import flash.events.Event; +class Issue64 { + + public function new() { + var skeleton:Dynamic; + var timelines:Array; + var lastTime:Float; + var time:Float; + var events:Array; + var i:Int = 0; + var n:Int = timelines.length; + while (i < n) { + timelines[i].apply(skeleton, lastTime, time, events, 1); + i++; + } + } -class Issue64 -{ - public function new() - { - var skeleton : Dynamic; - var timelines : Array; - var lastTime : Float; - var time : Float; - var events : Array; - var i : Int = 0; - var n : Int = timelines.length; - while (i < n) - { - timelienes[i].apply(skeleton, lastTime, time, events, 1); - i++; - } - } } +class Timeline { + + public function apply(object:Dynamic, lastTime:Float, time:Float, events:Array, number:Float):Void {} -class Timeline -{ - public function apply(object : Dynamic, lastTime : Float, time : Float, events : Array, number : Float) : Void - { - } + public function new() {} - public function new() - { - } } \ No newline at end of file diff --git a/test/issues/Issue65.hx b/test/issues/Issue65.hx index 6e78a27..33408ca 100644 --- a/test/issues/Issue65.hx +++ b/test/issues/Issue65.hx @@ -1,16 +1,13 @@ +class Issue65 { -class Issue65 -{ - public function new() - { - var array : Array; - for (i in 0...array.length) - { - var current : Dynamic = array[i]; - if (current == null) - { - continue; - } - } - } -} + public function new() { + var array:Array; + for (i in 0...array.length) { + var current:Dynamic = array[i]; + if (!AS3.as(current, Bool)) { + continue; + } + } + } + +} \ No newline at end of file diff --git a/test/issues/Issue66.hx b/test/issues/Issue66.hx index af36e6a..6a4a61f 100644 --- a/test/issues/Issue66.hx +++ b/test/issues/Issue66.hx @@ -1,12 +1,11 @@ +class Issue66 { -class Issue66 -{ - public function new() - { - var a : Int = 1; - var b : Int = as3hx.Compat.parseInt(10.5); - var c : Int = as3hx.Compat.parseInt(a / b); - var b : Float; - var d : Int = as3hx.Compat.parseInt(b); - } -} + public function new() { + var a:Int = AS3.int(1); + var b:Int = AS3.int(AS3.int(10.5)); + var c:Int = AS3.int(AS3.int(a / b)); + var b:Float; + var d:Int = AS3.int(AS3.int(b)); + } + +} \ No newline at end of file diff --git a/test/issues/Issue68.hx b/test/issues/Issue68.hx index 842d75a..7be3b2d 100644 --- a/test/issues/Issue68.hx +++ b/test/issues/Issue68.hx @@ -1,13 +1,12 @@ +class Issue68 { -class Issue68 -{ - public function new() - { - var a : Array = []; - var b : Array = a.copy(); - var c : Array = a.slice(0, 1); - var d : Array = []; - var e : Array = d.copy(); - var f : Array = d.slice(0, 1); - } -} + public function new() { + var a:Array = []; + var b:Array = a.copy(); + var c:Array = a.slice(0, 1); + var d:Array = []; + var e:Array = d.copy(); + var f:Array = d.slice(0, 1); + } + +} \ No newline at end of file diff --git a/test/issues/Issue69.hx b/test/issues/Issue69.hx index 585a58a..1573850 100644 --- a/test/issues/Issue69.hx +++ b/test/issues/Issue69.hx @@ -1,9 +1,8 @@ +class Issue69 { -class Issue69 -{ - public function new() - { - var s : String = ""; - var c : String = s.charAt(0); - } -} + public function new() { + var s:String = ''; + var c:String = Std.string(s.charAt(0)); + } + +} \ No newline at end of file diff --git a/test/issues/Issue70.hx b/test/issues/Issue70.hx index d209b73..91eee47 100644 --- a/test/issues/Issue70.hx +++ b/test/issues/Issue70.hx @@ -1,13 +1,11 @@ +class Issue70 { -class Issue70 -{ - public function new(i : Int = 10, args : Array = null) - { - trace(args); - } - - private function test(args : Array = null) : Void - { - trace(args); - } -} + public function new(i:Int = 10, args:Array = null) { + trace(args); + } + + private function test(args:Array = null):Void { + trace(args); + } + +} \ No newline at end of file diff --git a/test/issues/Issue71.hx b/test/issues/Issue71.hx index 9c50bc2..9c05b0c 100644 --- a/test/issues/Issue71.hx +++ b/test/issues/Issue71.hx @@ -1,19 +1,17 @@ +class Issue71 { -class Issue71 -{ - public function new() - { - __DOLLAR__test({ }); - var __DOLLAR__localVar : Dynamic = { - __DOLLAR__key : "value" - }; - } - - private var __DOLLAR__name : String; - - private function __DOLLAR__test(__DOLLAR__param : Dynamic) : Void - { - trace(__DOLLAR__param); - trace(__DOLLAR__name); - } -} + public function new() { + __DOLLAR__test({}); + var __DOLLAR__localVar:Dynamic = { + '__DOLLAR__key': 'value' + }; + } + + private var __DOLLAR__name:String; + + private function __DOLLAR__test(__DOLLAR__param:Dynamic):Void { + trace(__DOLLAR__param); + trace(__DOLLAR__name); + } + +} \ No newline at end of file diff --git a/test/issues/Issue81.hx b/test/issues/Issue81.hx index cdd66b4..9363837 100644 --- a/test/issues/Issue81.hx +++ b/test/issues/Issue81.hx @@ -1,14 +1,10 @@ import flash.system.Security; -class Issue81 -{ +class Issue81 { - public function new() - { - } - private static var Issue81_static_initializer = { - Security.allowDomain("*"); - true; - } + private static var Issue81_static_initializer = { + Security.allowDomain('*'); + true; + } -} +} \ No newline at end of file diff --git a/test/issues/Issue83.hx b/test/issues/Issue83.hx index 7d103cd..0632225 100644 --- a/test/issues/Issue83.hx +++ b/test/issues/Issue83.hx @@ -1,9 +1,8 @@ +class Issue83 { -class Issue83 -{ - public function new() - { - var o : Dynamic = haxe.Json.parse(""); - var s : String = haxe.Json.stringify({ }); - } -} + public function new() { + var o:Dynamic = haxe.Json.parse(''); + var s:String = Std.string(haxe.Json.stringify({})); + } + +} \ No newline at end of file diff --git a/test/issues/Issue85.hx b/test/issues/Issue85.hx index 258797a..5cd38d6 100644 --- a/test/issues/Issue85.hx +++ b/test/issues/Issue85.hx @@ -1,8 +1,7 @@ +class Issue85 { -class Issue85 -{ - public function new() - { - var i : Int = 1; - } -} + public function new() { + var i:Int = 1; + } + +} \ No newline at end of file diff --git a/test/issues/Issue87.hx b/test/issues/Issue87.hx index 01ffe9b..3e65952 100644 --- a/test/issues/Issue87.hx +++ b/test/issues/Issue87.hx @@ -1,31 +1,27 @@ +class Issue87 { + + public function new() { + __DOLLAR__cast(''); + new PrivateClass().__DOLLAR__cast(''); + var __DOLLAR__cast:Int = AS3.int(10.0); + } + + private var __DOLLAR__cast:Int = AS3.int(10.0); + + private function __DOLLAR__cast(o:Dynamic):String { + return Std.string(o); + } -class Issue87 -{ - public function new() - { - __DOLLAR__cast(""); - new PrivateClass().__DOLLAR__cast(""); - var __DOLLAR__cast : Int = as3hx.Compat.parseInt(10.0); - } - - private var __DOLLAR__cast : Int = as3hx.Compat.parseInt(10.0); - - private function __DOLLAR__cast(o : Dynamic) : String - { - return Std.string(o); - } } +class PrivateClass { + + public function new(__DOLLAR__cast:Int) { + + } + + public function __DOLLAR__cast(o:Dynamic):String { + return Std.string(o); + } -class PrivateClass -{ - public function new(__DOLLAR__cast : Int) - { - super(); - } - - public function __DOLLAR__cast(o : Dynamic) : String - { - return Std.string(o); - } } \ No newline at end of file diff --git a/test/issues/Issue89.hx b/test/issues/Issue89.hx index 9e267ec..6cf5234 100644 --- a/test/issues/Issue89.hx +++ b/test/issues/Issue89.hx @@ -1,16 +1,14 @@ +class Issue89 { -class Issue89 -{ - public function new() - { - var n : Float = getNaN(); - var v : Float = Math.NaN; - } - - private var n : Float = Math.NaN; - - private function getNaN() : Float - { - return Math.NaN; - } -} + public function new() { + var n:Float = getNaN(); + var v:Float = Math.NaN; + } + + private var n:Float = Math.NaN; + + private function getNaN():Float { + return Math.NaN; + } + +} \ No newline at end of file diff --git a/test/issues/Issue91.hx b/test/issues/Issue91.hx index f3f7d7e..566f8b4 100644 --- a/test/issues/Issue91.hx +++ b/test/issues/Issue91.hx @@ -1,9 +1,8 @@ +class Issue91 { -class Issue91 -{ - public function new() - { - var i : Int = 1; - var b : Bool = i != 0; - } -} + public function new() { + var i:Int = 1; + var b:Bool = i == 0; + } + +} \ No newline at end of file diff --git a/test/issues/Issue93.hx b/test/issues/Issue93.hx index fec7348..d15b4b4 100644 --- a/test/issues/Issue93.hx +++ b/test/issues/Issue93.hx @@ -1,10 +1,9 @@ +class Issue93 { -class Issue93 -{ - public function new() - { - trace([1, 2, 3, 4, 5, 6, 7, 8, 9, 0].join(",")); - var a : Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; - trace(a.join(",")); - } -} + public function new() { + trace([1, 2, 3, 4, 5, 6, 7, 8, 9, 0].join(',')); + var a:Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + trace(a.join(',')); + } + +} \ No newline at end of file diff --git a/test/issues/Issue94.hx b/test/issues/Issue94.hx index 0559291..66824e9 100644 --- a/test/issues/Issue94.hx +++ b/test/issues/Issue94.hx @@ -1,19 +1,18 @@ +class Issue94 { -class Issue94 -{ - public function new() - { - var a : Array = []; - a.push(1); - a.push(2); - a.push(3); - a.push(4); - a.push(5); - a.push(6); - a.push(7); - a.push(8); - a.push(9); - a.push(0); - - } -} + public function new() { + var a:Array = []; + a.push(1); + a.push(2); + a.push(3); + a.push(4); + a.push(5); + a.push(6); + a.push(7); + a.push(8); + a.push(9); + a.push(0); + + } + +} \ No newline at end of file diff --git a/test/issues/Issue95.hx b/test/issues/Issue95.hx index 3430ddb..dab0442 100644 --- a/test/issues/Issue95.hx +++ b/test/issues/Issue95.hx @@ -1,19 +1,15 @@ - typedef ConfigsTypedef = { - var name : String; - var id : Int; + var name: String; + var id: Int; } -class Issue95 -{ - private static var _configs : Array = [ - { - name : "name", - id : 10 - } - ]; +class Issue95 { - public function new() - { - } -} + private static var _configs:Array = cast [ + { + 'name': 'name', + 'id': 10 + } + ]; + +} \ No newline at end of file diff --git a/test/issues/Issue96.hx b/test/issues/Issue96.hx index a737f4d..aa4aaec 100644 --- a/test/issues/Issue96.hx +++ b/test/issues/Issue96.hx @@ -1,9 +1,8 @@ +class Issue96 { -class Issue96 -{ - public function new() - { - var o : Dynamic = ""; - var s : String = (Std.is(o, String)) ? Std.string(o) : ""; - } -} + public function new() { + var o:Dynamic = ''; + var s:String = (Std.is(o, String)) ? Std.string(o) : ''; + } + +} \ No newline at end of file diff --git a/test/unit/TestSuite.hx b/test/unit/TestSuite.hx index 424d0c8..1b32503 100644 --- a/test/unit/TestSuite.hx +++ b/test/unit/TestSuite.hx @@ -1,33 +1,31 @@ import massive.munit.TestSuite; -import as3hx.AS3HXTest; -import as3hx.CompatTest; -import as3hx.parsers.ExprParserTest; +import ExampleTest; +import as3hx.TokenizerTest; import as3hx.parsers.ImportParserTest; +import as3hx.parsers.ExprParserTest; import as3hx.parsers.UseParserTest; +import as3hx.AS3HXTest; import as3hx.ParserUtilsTest; -import as3hx.TokenizerTest; -import ExampleTest; +import as3hx.CompatTest; /** * Auto generated Test Suite for MassiveUnit. * Refer to munit command line tool for more information (haxelib run munit) */ - class TestSuite extends massive.munit.TestSuite -{ - +{ public function new() { super(); - add(as3hx.AS3HXTest); - add(as3hx.CompatTest); - add(as3hx.parsers.ExprParserTest); + add(ExampleTest); + add(as3hx.TokenizerTest); add(as3hx.parsers.ImportParserTest); + add(as3hx.parsers.ExprParserTest); add(as3hx.parsers.UseParserTest); + add(as3hx.AS3HXTest); add(as3hx.ParserUtilsTest); - add(as3hx.TokenizerTest); - add(ExampleTest); + add(as3hx.CompatTest); } } diff --git a/test/unit/as3hx/AS3HXTest.hx b/test/unit/as3hx/AS3HXTest.hx index 03fc7b0..002c7f1 100644 --- a/test/unit/as3hx/AS3HXTest.hx +++ b/test/unit/as3hx/AS3HXTest.hx @@ -24,6 +24,11 @@ class AS3HXTest { output = new BytesOutput(); } + @Before + public function setup() { + cfg.processDefaultConfig(); + } + @Test public function issue14() { generate("Issue14.as", "Issue14.hx"); @@ -571,6 +576,21 @@ class AS3HXTest { generate("Issue27.as", "Issue27.hx"); } + @Test("for(var i = 5; i < a.length; a.pop()) -> while(i < a.length)") + public function issue296() { + generate("Issue296.as", "Issue296.hx"); + } + + @Test("for(var i = 0; some(i); i++) -> while(i < some(i))") + public function issue296_1() { + generate("Issue296_1.as", "Issue296_1.hx"); + } + + @Test("https://github.com/HaxeFoundation/as3hx/issues/323") + public function issue323() { + generate("Issue323.as", "Issue323.hx"); + } + function generate(as3FileName:String, expectedHaxeFileName:String) { var issuesDirectory = FileSystem.absolutePath("test/issues"); var generatedDirectoryPath = '$issuesDirectory/generated'; @@ -578,10 +598,22 @@ class AS3HXTest { var content = File.getContent('$issuesDirectory/$as3FileName'); var program = parser.parseString(content, issuesDirectory, '$issuesDirectory/$as3FileName'); var fw = File.write('$generatedDirectoryPath/$expectedHaxeFileName', false); + if (cfg.useFullTyping) { + writer.register(program); + writer.prepareTyping(); + if (cfg.useOpenFlTypes) { + writer.refineTypes(program); + writer.applyRefinedTypes(program); + } + writer.finishTyping(); + } writer.process(program, fw); fw.close(); var expectedText = File.getContent('$issuesDirectory/$expectedHaxeFileName'); var actualText = File.getContent('$generatedDirectoryPath/$expectedHaxeFileName'); + if (expectedText != actualText) { + Sys.command("diff", ['$issuesDirectory/$expectedHaxeFileName', '$generatedDirectoryPath/$expectedHaxeFileName']); + } Assert.areEqual(expectedText, actualText); } } \ No newline at end of file diff --git a/test/unit/as3hx/CompatTest.hx b/test/unit/as3hx/CompatTest.hx index 024a3ba..dad1b51 100644 --- a/test/unit/as3hx/CompatTest.hx +++ b/test/unit/as3hx/CompatTest.hx @@ -28,7 +28,7 @@ class CompatTest @Test public function getFunctionLength() { Assert.areEqual(0, Compat.getFunctionLength(getFunctionLength)); - Assert.areEqual(3, Compat.getFunctionLength(Assert.areEqual)); + Assert.areEqual(4, Compat.getFunctionLength(Assert.areEqual)); } @Test diff --git a/test/unit/as3hx/parsers/ImportParserTest.hx b/test/unit/as3hx/parsers/ImportParserTest.hx index ce6e2e0..c9562fc 100644 --- a/test/unit/as3hx/parsers/ImportParserTest.hx +++ b/test/unit/as3hx/parsers/ImportParserTest.hx @@ -46,6 +46,8 @@ class ImportParserTest public function testDictionary():Void { var importParts = getImportParts('import flash.utils.Dictionary;'); + Assert.areEqual(importParts.length, 0); + var importParts = getImportParts('import flash.utils.Dictionary;', false); Assert.areEqual(importParts.length, 3); Assert.areEqual(importParts[0], 'flash'); Assert.areEqual(importParts[1], 'utils'); @@ -108,11 +110,12 @@ class ImportParserTest Assert.areEqual(importParts[0], '*'); } - function getImportParts(importString:String):Array + function getImportParts(importString:String, dictionaryToHash:Bool = true):Array { var tokenizer = new Tokenizer(new haxe.io.StringInput(importString)); Assert.areEqual(tokenizer.id(), 'import'); var cfg = new as3hx.Config(); + cfg.dictionaryToHash = dictionaryToHash; var importParts = ImportParser.parse(tokenizer, cfg); return importParts; }