Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View File

@ -0,0 +1,110 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Compiler;
enum abstract ModuleCheckPolicy(Int) {
/**
Disables file modification checks, avoiding some filesystem operations.
**/
var NoCheckFileTimeModification = 0;
/**
If a file is modified, also checks if its content changed. This check
is not free, but useful when .hx files are auto-generated.
**/
var CheckFileContentModification = 1;
/**
Disables dependency checks of the module.
This should only be used for modules that don't depend on any module that
might change. It is effectively a promise to the compiler that the module
is unaffected by changes made to other modules. If that promise is broken,
the compiler is sad and things probably stop working.
**/
var NoCheckDependencies = 2;
/**
Disables file shadowing checks. Shadowing can occur when a new file
is added to a class-path that has higher priority than the class-path
of the current module file.
**/
var NoCheckShadowing = 3;
}
enum abstract ContextOptions(Int) {
/**
Affects only the normal context.
**/
var NormalContext = 0;
/**
Affects only the macro context.
**/
var MacroContext = 1;
/**
Affects the normal and macro contexts.
**/
var NormalAndMacroContext = 2;
}
/**
This class provides some methods which can be invoked from command line using
`--macro server.field(args)`.
**/
class CompilationServer {
#if (macro || display)
/**
Sets the `ModuleCheckPolicy` of all files whose dot-path matches an
element of `pathFilters`.
If `recursive` is true, a dot-path is considered matched if it starts
with the path filter. This automatically applies to path filters of
packages. Otherwise an exact match is required.
If an element in `pathFilters` is the empty String `""` it matches
everything (if `recursive = true`) or only top-level types (if
`recursive = false`).
The argument `contextOptions` determines which context (normal, macro
or both) this affects.
If a call to this function is added to the compilation parameters, the
compilation server should be restarted to ensure it takes effect.
**/
static public function setModuleCheckPolicy(pathFilters:Array<String>, policy:Array<ModuleCheckPolicy>, ?recursive = true,
?contextOptions:ContextOptions = NormalContext) {
@:privateAccess Compiler.load("server_add_module_check_policy", 4)(pathFilters, policy, recursive, contextOptions);
}
/**
Invalidates all files given in `filePaths`, removing them from the cache.
**/
static public function invalidateFiles(filePaths:Array<String>) {
@:privateAccess Compiler.load("server_invalidate_files", 1)(filePaths);
}
#end
}

View File

@ -0,0 +1,579 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Expr;
/**
All these methods can be called for compiler configuration macros.
**/
#if hl
@:hlNative("macro")
#end
class Compiler {
/**
A conditional compilation flag can be set on the command line using
`-D key=value`.
Returns the value of a compiler flag.
If the compiler flag is defined but no value is set,
`Compiler.getDefine` returns `"1"` (e.g. `-D key`).
If the compiler flag is not defined, `Compiler.getDefine` returns
`null`.
Note: This is a macro and cannot be called from within other macros. Refer
to `haxe.macro.Context.definedValue` to obtain defined values in macro context.
@see https://haxe.org/manual/lf-condition-compilation.html
**/
macro /* <-- ! */ static public function getDefine(key:String) {
return macro $v{haxe.macro.Context.definedValue(key)};
}
#if (neko || (macro && hl) || (macro && eval))
static var ident = ~/^[A-Za-z_][A-Za-z0-9_]*$/;
static var path = ~/^[A-Za-z_][A-Za-z0-9_.]*$/;
public static function allowPackage(v:String) {
#if (neko || eval)
load("allow_package", 1)(v);
#end
}
/**
Set a conditional compiler flag.
Usage of this function outside of initialization macros is deprecated and may cause compilation server issues.
**/
public static function define(flag:String, ?value:String) {
#if (neko || eval)
load("define", 2)(flag, value);
#end
}
#if (!neko && !eval)
private static function typePatch(cl:String, f:String, stat:Bool, t:String) {}
private static function metaPatch(meta:String, cl:String, f:String, stat:Bool) {}
private static function addGlobalMetadataImpl(pathFilter:String, meta:String, recursive:Bool, toTypes:Bool, toFields:Bool) {}
#end
/**
Removes a (static) field from a given class by name.
An error is thrown when `className` or `field` is invalid.
**/
public static function removeField(className:String, field:String, ?isStatic:Bool) {
if (!path.match(className))
throw "Invalid " + className;
if (!ident.match(field))
throw "Invalid " + field;
#if (neko || eval)
load("type_patch", 4)(className, field, isStatic == true, null);
#else
typePatch(className, field, isStatic == true, null);
#end
}
/**
Set the type of a (static) field at a given class by name.
An error is thrown when `className` or `field` is invalid.
**/
public static function setFieldType(className:String, field:String, type:String, ?isStatic:Bool) {
if (!path.match(className))
throw "Invalid " + className;
if (!ident.match((field.charAt(0) == "$") ? field.substr(1) : field))
throw "Invalid " + field;
#if (neko || eval)
load("type_patch", 4)(className, field, isStatic == true, type);
#else
typePatch(className, field, isStatic == true, type);
#end
}
/**
Add metadata to a (static) field or class by name.
An error is thrown when `className` or `field` is invalid.
**/
public static function addMetadata(meta:String, className:String, ?field:String, ?isStatic:Bool) {
if (!path.match(className))
throw "Invalid " + className;
if (field != null && !ident.match(field))
throw "Invalid " + field;
#if (neko || eval)
load("meta_patch", 4)(meta, className, field, isStatic == true);
#else
metaPatch(meta, className, field, isStatic == true);
#end
}
/**
Add a class path where ".hx" source files or packages (sub-directories) can be found.
Usage of this function outside of initialization macros is deprecated and may cause compilation server issues.
**/
public static function addClassPath(path:String) {
#if (neko || eval)
load("add_class_path", 1)(path);
#end
}
public static function getOutput():String {
#if (neko || eval)
return load("get_output", 0)();
#else
return null;
#end
}
public static function setOutput(fileOrDir:String) {
#if (neko || eval)
load("set_output", 1)(fileOrDir);
#end
}
public static function getDisplayPos():Null<{file:String, pos:Int}> {
#if (neko || eval)
return load("get_display_pos", 0)();
#else
return null;
#end
}
/**
Adds a native library depending on the platform (e.g. `-swf-lib` for Flash).
Usage of this function outside of initialization macros is deprecated and may cause compilation server issues.
**/
public static function addNativeLib(name:String) {
#if (neko || eval)
load("add_native_lib", 1)(name);
#end
}
/**
Adds an argument to be passed to the native compiler (e.g. `-javac-arg` for Java).
**/
public static function addNativeArg(argument:String) {
#if (neko || eval)
load("add_native_arg", 1)(argument);
#end
}
/**
Includes all modules in package `pack` in the compilation.
In order to include single modules, their paths can be listed directly
on command line: `haxe ... ModuleName pack.ModuleName`.
By default `Compiler.include` will search for modules in the directories defined with `-cp`.
If you want to specify a different set of paths to search for modules, you can use the optional
argument `classPath`.
@param pack The package dot-path as String. Use `''` to include the root package.
@param rec If true, recursively adds all sub-packages.
@param ignore Array of module names to ignore for inclusion.
You can use `module*` with a * at the end for Wildcard matching
@param classPaths An alternative array of paths (directory names) to use to search for modules to include.
Note that if you pass this argument, only the specified paths will be used for inclusion.
@param strict If true and given package wasn't found in any of class paths, fail with an error.
**/
public static function include(pack:String, ?rec = true, ?ignore:Array<String>, ?classPaths:Array<String>, strict = false) {
var ignoreWildcard:Array<String> = [];
var ignoreString:Array<String> = [];
if (ignore != null) {
for (ignoreRule in ignore) {
if (StringTools.endsWith(ignoreRule, "*")) {
ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1));
} else {
ignoreString.push(ignoreRule);
}
}
}
var skip = if (ignore == null) {
function(c) return false;
} else {
function(c:String) {
if (Lambda.has(ignoreString, c))
return true;
for (ignoreRule in ignoreWildcard)
if (StringTools.startsWith(c, ignoreRule))
return true;
return false;
}
}
var displayValue = Context.definedValue("display");
if (classPaths == null) {
classPaths = Context.getClassPath();
// do not force inclusion when using completion
switch (displayValue) {
case null:
case "usage":
case _:
return;
}
// normalize class path
for (i in 0...classPaths.length) {
var cp = StringTools.replace(classPaths[i], "\\", "/");
if (StringTools.endsWith(cp, "/"))
cp = cp.substr(0, -1);
if (cp == "")
cp = ".";
classPaths[i] = cp;
}
}
var prefix = pack == '' ? '' : pack + '.';
var found = false;
for (cp in classPaths) {
var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/");
if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path))
continue;
found = true;
for (file in sys.FileSystem.readDirectory(path)) {
if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) {
if( file == "import.hx" ) continue;
var cl = prefix + file.substr(0, file.length - 3);
if (skip(cl))
continue;
Context.getModule(cl);
} else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file))
include(prefix + file, true, ignore, classPaths);
}
}
if (strict && !found)
Context.error('Package "$pack" was not found in any of class paths', Context.currentPos());
}
/**
Exclude a class or an enum without changing it to `@:nativeGen`.
**/
static function excludeBaseType(baseType:Type.BaseType):Void {
if (!baseType.isExtern) {
var meta = baseType.meta;
if (!meta.has(":nativeGen")) {
meta.add(":hxGen", [], baseType.pos);
}
baseType.exclude();
}
}
/**
Exclude a specific class, enum, or all classes and enums in a
package from being generated. Excluded types become `extern`.
@param pack The package dot-path as String. Use `''` to exclude the root package.
@param rec If true, recursively excludes all sub-packages.
**/
public static function exclude(pack:String, ?rec = true) {
Context.onGenerate(function(types) {
for (t in types) {
var b:Type.BaseType, name;
switch (t) {
case TInst(c, _):
name = c.toString();
b = c.get();
case TEnum(e, _):
name = e.toString();
b = e.get();
default:
continue;
}
var p = b.pack.join(".");
if ((p == pack || name == pack) || (rec && StringTools.startsWith(p, pack + ".")))
excludeBaseType(b);
}
}, false);
}
/**
Exclude classes and enums listed in an extern file (one per line) from being generated.
**/
public static function excludeFile(fileName:String) {
fileName = Context.resolvePath(fileName);
var f = sys.io.File.read(fileName, true);
var classes = new haxe.ds.StringMap();
try {
while (true) {
var l = StringTools.trim(f.readLine());
if (l == "" || !~/[A-Za-z0-9._]/.match(l))
continue;
classes.set(l, true);
}
} catch (e:haxe.io.Eof) {}
Context.onGenerate(function(types) {
for (t in types) {
switch (t) {
case TInst(c, _):
if (classes.exists(c.toString()))
excludeBaseType(c.get());
case TEnum(e, _):
if (classes.exists(e.toString()))
excludeBaseType(e.get());
default:
}
}
});
}
/**
Load a type patch file that can modify the field types within declared classes and enums.
**/
public static function patchTypes(file:String):Void {
var file = Context.resolvePath(file);
var f = sys.io.File.read(file, true);
try {
while (true) {
var r = StringTools.trim(f.readLine());
if (r == "" || r.substr(0, 2) == "//")
continue;
if (StringTools.endsWith(r, ";"))
r = r.substr(0, -1);
if (r.charAt(0) == "-") {
r = r.substr(1);
var isStatic = StringTools.startsWith(r, "static ");
if (isStatic)
r = r.substr(7);
var p = r.split(".");
var field = p.pop();
removeField(p.join("."), field, isStatic);
continue;
}
if (r.charAt(0) == "@") {
var rp = r.split(" ");
var type = rp.pop();
var isStatic = rp[rp.length - 1] == "static";
if (isStatic)
rp.pop();
var meta = rp.join(" ");
var p = type.split(".");
var field = if (p.length > 1 && p[p.length - 2].charAt(0) >= "a") null else p.pop();
addMetadata(meta, p.join("."), field, isStatic);
continue;
}
if (StringTools.startsWith(r, "enum ")) {
define("enumAbstract:" + r.substr(5));
continue;
}
var rp = r.split(" : ");
if (rp.length > 1) {
r = rp.shift();
var isStatic = StringTools.startsWith(r, "static ");
if (isStatic)
r = r.substr(7);
var p = r.split(".");
var field = p.pop();
setFieldType(p.join("."), field, rp.join(" : "), isStatic);
continue;
}
throw "Invalid type patch " + r;
}
} catch (e:haxe.io.Eof) {}
}
/**
Marks types or packages to be kept by DCE.
This also extends to the sub-types of resolved modules.
In order to include module sub-types directly, their full dot path
including the containing module has to be used
(e.g. `msignal.Signal.Signal0`).
This operation has no effect if the type has already been loaded, e.g.
through `Context.getType`.
@param path A package, module or sub-type dot path to keep.
@param paths An Array of package, module or sub-type dot paths to keep.
@param recursive If true, recurses into sub-packages for package paths.
**/
public static function keep(?path:String, ?paths:Array<String>, ?recursive:Bool = true) {
if (null == paths)
paths = [];
if (null != path)
paths.push(path);
for (path in paths) {
addGlobalMetadata(path, "@:keep", recursive, true, true);
}
}
/**
Enables null safety for a type or a package.
@param path A package, module or sub-type dot path to enable null safety for.
@param recursive If true, recurses into sub-packages for package paths.
**/
public static function nullSafety(path:String, mode:NullSafetyMode = Loose, recursive:Bool = true) {
addGlobalMetadata(path, '@:nullSafety($mode)', recursive);
}
/**
Adds metadata `meta` to all types (if `toTypes = true`) or fields (if
`toFields = true`) whose dot-path matches `pathFilter`.
If `recursive` is true a dot-path is considered matched if it starts
with `pathFilter`. This automatically applies to path filters of
packages. Otherwise an exact match is required.
If `pathFilter` is the empty String `""` it matches everything (if
`recursive = true`) or only top-level types (if `recursive = false`).
This operation has no effect if the type has already been loaded, e.g.
through `Context.getType`.
**/
public static function addGlobalMetadata(pathFilter:String, meta:String, ?recursive:Bool = true, ?toTypes:Bool = true, ?toFields:Bool = false) {
#if (neko || eval)
load("add_global_metadata_impl", 5)(pathFilter, meta, recursive, toTypes, toFields);
#else
addGlobalMetadataImpl(pathFilter, meta, recursive, toTypes, toFields);
#end
}
/**
Change the default JS output by using a custom generator callback
**/
public static function setCustomJSGenerator(callb:JSGenApi->Void) {
#if (neko || eval)
load("set_custom_js_generator", 1)(callb);
#end
}
#if (neko || eval)
static inline function load(f, nargs):Dynamic {
return @:privateAccess Context.load(f, nargs);
}
#end
/**
Clears cached results of file lookups
**/
public static function flushDiskCache() {
#if (neko || eval)
load("flush_disk_cache", 0)();
#end
}
#end
#if (js || lua || macro)
/**
Embed a JavaScript or Lua file at compile time (can be called by `--macro` or within an `__init__` method).
**/
public static #if !macro macro #end function includeFile(file:String, position:IncludePosition = Top) {
return switch ((position : String).toLowerCase()) {
case Inline:
if (Context.getLocalModule() == "")
Context.error("Cannot use inline mode when includeFile is called by `--macro`", Context.currentPos());
var f = try sys.io.File.getContent(Context.resolvePath(file)) catch (e:Dynamic) Context.error(Std.string(e), Context.currentPos());
var p = Context.currentPos();
if(Context.defined("js")) {
macro @:pos(p) js.Syntax.plainCode($v{f});
} else {
macro @:pos(p) untyped __lua__($v{f});
}
case Top | Closure:
@:privateAccess Context.includeFile(file, position);
macro {};
case _:
Context.error("unknown includeFile position: " + position, Context.currentPos());
}
}
#end
}
enum abstract IncludePosition(String) from String to String {
/**
Prepend the file content to the output file.
**/
var Top = "top";
/**
Prepend the file content to the body of the top-level closure.
Since the closure is in strict-mode, there may be run-time error if the input is not strict-mode-compatible.
**/
var Closure = "closure";
/**
Directly inject the file content at the call site.
**/
var Inline = "inline";
}
enum abstract NullSafetyMode(String) to String {
/**
Disable null safety.
**/
var Off;
/**
Loose safety.
If an expression is checked `!= null`, then it's considered safe even if it could be modified after the check.
E.g.
```haxe
function example(o:{field:Null<String>}) {
if(o.field != null) {
mutate(o);
var notNullable:String = o.field; //no error
}
}
function mutate(o:{field:Null<String>}) {
o.field = null;
}
```
**/
var Loose;
/**
Full scale null safety.
If a field is checked `!= null` it stays safe until a call is made or any field of any object is reassigned,
because that could potentially alter an object of the checked field.
E.g.
```haxe
function example(o:{field:Null<String>}, b:{o:{field:Null<String>}}) {
if(o.field != null) {
var notNullable:String = o.field; //no error
someCall();
var notNullable:String = o.field; // Error!
}
if(o.field != null) {
var notNullable:String = o.field; //no error
b.o = {field:null};
var notNullable:String = o.field; // Error!
}
}
```
**/
var Strict;
/**
Full scale null safety for a multi-threaded environment.
With this mode checking a field `!= null` does not make it safe, because it could be changed from another thread
at the same time or immediately after the check.
The only nullable thing could be safe are local variables.
**/
var StrictThreaded;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Expr;
/**
This class provides some utility methods to work with AST-level types. It is
best used through `using haxe.macro.ComplexTypeTools` syntax and then provides
additional methods on `haxe.macro.ComplexType` instances.
**/
class ComplexTypeTools {
/**
Converts type `c` to a human-readable `String` representation.
The result is guaranteed to be valid Haxe code, but there may be
differences from the original lexical syntax.
**/
static public function toString(c:ComplexType):String
return new Printer().printComplexType(c);
#if (macro || display)
/**
Returns a type corresponding to `c`.
If `c` is null, the result is null.
**/
static public function toType(c:ComplexType):Null<Type>
return c == null ? null : Context.resolveType(c, Context.currentPos());
#end
}

View File

@ -0,0 +1,645 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Expr;
import haxe.macro.Type.TypedExpr;
enum Message {
Info(msg:String, pos:Position);
Warning(msg:String, pos:Position);
}
/**
Context provides an API for macro programming.
It contains common functions that interact with the macro interpreter to
query or set information. Other API functions are available in the tools
classes:
- `haxe.macro.ComplexTypeTools`
- `haxe.macro.ExprTools`
- `haxe.macro.TypeTools`
**/
class Context {
#if (neko || eval || display)
/**
Displays a compilation error `msg` at the given `Position` `pos`
and aborts the current macro call.
**/
public static function error(msg:String, pos:Position):Dynamic {
return load("error", 2)(msg, pos);
}
/**
Displays a compilation error `msg` at the given `Position` `pos`
and aborts the compilation.
**/
public static function fatalError(msg:String, pos:Position):Dynamic {
return load("fatal_error", 2)(msg, pos);
}
/**
Displays a compilation warning `msg` at the given `Position` `pos`.
**/
public static function warning(msg:String, pos:Position) {
load("warning", 2)(msg, pos);
}
/**
Displays a compilation info `msg` at the given `Position` `pos`.
**/
public static function info(msg:String, pos:Position) {
load("info", 2)(msg, pos);
}
/**
Gets a list of all current compilation info/warning messages.
**/
public static function getMessages() : Array<Message> {
return load("get_messages",0)();
}
/**
Filters all current info/warning messages. Filtered out messages will
not be displayed by the compiler.
**/
public static function filterMessages( predicate : Message -> Bool ) {
load("filter_messages",1)(predicate);
}
/**
Resolves a file name `file` based on the current class paths.
The resolution follows the usual class path rules where the last
declared class path has priority.
If a class path was declared relative, this method returns the relative
file path. Otherwise it returns the absolute file path.
**/
public static function resolvePath(file:String):String {
return load("resolve_path", 1)(file);
}
/**
Returns an `Array` of current class paths in the order of their
declaration.
Modifying the returned array has no effect on the compiler. Class paths
can be added using `haxe.macro.Compiler.addClassPath`.
**/
public static function getClassPath():Array<String> {
return load("class_path", 0)();
}
/**
Check if current display position is within `pos`.
**/
public static function containsDisplayPosition(pos:Position):Bool {
return load("contains_display_position", 1)(pos);
}
/**
Returns the position at which the macro was called.
**/
public static function currentPos():Position {
return load("current_pos", 0)();
}
/**
Returns the type which is expected at the place the macro is called.
This affects usages such as `var x:Int = macroCall()`, where the
expected type will be reported as `Int`.
Might return `null` if no specific type is expected or if the calling
macro is not an expression-macro.
**/
public static function getExpectedType():Null<Type> {
return load("get_expected_type", 0)();
}
/**
Returns the call arguments that lead to the invocation of the current
`@:genericBuild` macro, if available.
Returns `null` if the current macro is not a `@:genericBuild` macro.
**/
public static function getCallArguments():Null<Array<Expr>> {
return load("get_call_arguments", 0)();
}
/**
Returns the current class in which the macro was called.
If no such class exists, `null` is returned.
**/
public static function getLocalClass():Null<Type.Ref<Type.ClassType>> {
var l:Type = load("get_local_type", 0)();
if (l == null)
return null;
return switch (l) {
case TInst(c, _): c;
default: null;
}
}
/**
Returns the current module path in/on which the macro was called.
**/
public static function getLocalModule():String {
return load("get_local_module", 0)();
}
/**
Returns the current type in/on which the macro was called.
If no such type exists, `null` is returned.
**/
public static function getLocalType():Null<Type> {
return load("get_local_type", 0)();
}
/**
Returns the name of the method from which the macro was called.
If no such method exists, `null` is returned.
**/
public static function getLocalMethod():Null<String> {
return load("get_local_method", 0)();
}
/**
Returns an `Array` of classes which are available for `using` usage in
the context the macro was called.
Modifying the returned array has no effect on the compiler.
**/
public static function getLocalUsing():Array<Type.Ref<Type.ClassType>> {
return load("get_local_using", 0)();
}
/**
Returns an `Array` of all imports in the context the macro was called.
Modifying the returned array has no effect on the compiler.
**/
public static function getLocalImports():Array<ImportExpr> {
return load("get_local_imports", 0)();
}
/**
Returns a map of local variables accessible in the context the macro was
called.
The keys of the returned map are the variable names, the values are
their types.
Modifying the returned map has no effect on the compiler.
**/
@:deprecated("Use Context.getLocalTVars() instead")
public static function getLocalVars():Map<String, Type> {
return load("local_vars", 1)(false);
}
/**
Similar to `getLocalVars`, but returns elements of type `TVar` instead
of `Type`.
**/
public static function getLocalTVars():Map<String, Type.TVar> {
return load("local_vars", 1)(true);
}
/**
Tells if the conditional compilation flag `s` has been set.
Compiler flags are set using the `-D` command line parameter, or
by calling `haxe.macro.Compiler.define`.
@see https://haxe.org/manual/lf-condition-compilation.html
**/
public static function defined(s:String):Bool {
return load("defined", 1)(s);
}
/**
Returns the value defined for the conditional compilation flag `key`.
If no value is defined for `key`, `null` is returned.
Compiler flags values are set using the `-D key=value` command line
parameter, or by calling `haxe.macro.Compiler.define`.
The default value is `"1"`.
@see https://haxe.org/manual/lf-condition-compilation.html
**/
public static function definedValue(key:String):String {
return load("defined_value", 1)(key);
}
/**
Returns a map of all conditional compilation flags that have been set.
Compiler flags are set using the `-D` command line parameter, or
by calling `haxe.macro.Compiler.define`.
Modifying the returned map has no effect on the compiler.
@see https://haxe.org/manual/lf-condition-compilation.html
**/
public static function getDefines():Map<String, String> {
return load("get_defines", 0)();
}
/**
Resolves a type identified by `name`.
The resolution follows the usual class path rules where the last
declared class path has priority.
If no type can be found, an exception of type `String` is thrown.
**/
public static function getType(name:String):Type {
return load("get_type", 1)(name);
}
/**
Resolves a module identified by `name` and returns an `Array` of all
its contained types.
The resolution follows the usual class path rules where the last
declared class path has priority.
If no module can be found, an exception of type `String` is thrown.
**/
public static function getModule(name:String):Array<Type> {
return load("get_module", 1)(name);
}
/**
Parses `expr` as Haxe code, returning the corresponding AST.
String interpolation of single quote strings within `expr` is not
supported.
The provided `Position` `pos` is used for all generated inner AST nodes.
**/
public static function parse(expr:String, pos:Position):Expr {
return load("do_parse", 3)(expr, pos, false);
}
/**
Similar to `parse`, but error positions are reported within the provided
String `expr`.
**/
public static function parseInlineString(expr:String, pos:Position):Expr {
return load("do_parse", 3)(expr, pos, true);
}
/**
Builds an expression from `v`.
This method generates AST nodes depending on the macro-runtime value of
`v`. As such, only basic types and enums are supported and the behavior
for other types is undefined.
The provided `Position` `pos` is used for all generated inner AST nodes.
**/
public static function makeExpr(v:Dynamic, pos:Position):Expr {
return load("make_expr", 2)(v, pos);
}
/**
Returns a hashed MD5 signature of value `v`.
**/
public static function signature(v:Dynamic):String {
return load("signature", 1)(v);
}
/**
Adds a callback function `callback` which is invoked after the
compiler's typing phase, just before its generation phase.
The callback receives an `Array` containing all types which are about
to be generated. Modifications are limited to metadata, it is mainly
intended to obtain information.
By default, the callback is made before types are stored in the compilation
server, if active. This means that any effect persists for the next compilation.
If `persistent` is set to `false`, changes to types made by the callback only
affect the current compilation. If no compilation server is used, this flag has
no effect.
*Note*: the callback is still invoked when generation is disabled with `--no-output`.
**/
public static function onGenerate(callback:Array<Type>->Void, persistent:Bool = true) {
load("on_generate", 2)(callback, persistent);
}
/**
Adds a callback function `callback` which is invoked after the compiler
generation phase.
Compilation has completed at this point and cannot be influenced
anymore. However, contextual information is still available.
*Note*: the callback is still invoked when generation is disabled with `--no-output`.
**/
public static function onAfterGenerate(callback:Void->Void) {
load("on_after_generate", 1)(callback);
}
/**
Adds a callback function `callback` which is invoked after the compiler
is done typing, but before optimization. The callback receives the types
which have been typed.
It is possible to define new types in the callback, in which case it
will be called again with the new types as argument.
**/
public static function onAfterTyping(callback:Array<haxe.macro.Type.ModuleType>->Void) {
load("on_after_typing", 1)(callback);
}
/**
Adds a callback function `callback` which is invoked when a type name
cannot be resolved.
The callback may return a type definition, which is then used for the
expected type. If it returns `null`, the type is considered to still not
exist.
**/
public static function onTypeNotFound(callback:String->TypeDefinition) {
load("on_type_not_found", 1)(callback);
}
/**
Types expression `e` and returns its type.
Typing the expression may result in a compiler error which can be
caught using `try ... catch`.
**/
public static function typeof(e:Expr):Type {
return load("typeof", 1)(e);
}
/**
Types expression `e` and returns the corresponding `TypedExpr`.
Typing the expression may result in a compiler error which can be
caught using `try ... catch`.
**/
public static function typeExpr(e:Expr):TypedExpr {
return load("type_expr", 1)(e);
}
/**
Resolve type `t` and returns the corresponding `Type`.
Resolving the type may result in a compiler error which can be
caught using `try ... catch`.
Resolution is performed based on the current context in which the macro is called.
**/
public static function resolveType(t:ComplexType, p:Position):Type {
return load("resolve_type", 2)(t, p);
}
/**
Returns the `ComplexType` corresponding to the given `Type` `t`.
See `haxe.macro.TypeTools.toComplexType` for details.
**/
public static function toComplexType(t:Type):Null<ComplexType> {
return load("to_complex_type", 1)(t);
}
/**
Tries to unify `t1` and `t2` and returns `true` if successful.
**/
public static function unify(t1:Type, t2:Type):Bool {
return load("unify", 2)(t1, t2);
}
/**
Follows a type.
See `haxe.macro.TypeTools.follow` for details.
**/
public static function follow(t:Type, ?once:Bool):Type {
return load("follow", 2)(t, once);
}
/**
Follows a type, including abstracts' underlying implementation
See `haxe.macro.TypeTools.followWithAbstracts` for details.
**/
public static function followWithAbstracts(t:Type, once:Bool = false):Type {
return load("follow_with_abstracts", 2)(t, once);
}
/**
Returns the information stored in `Position` `p`.
**/
public static function getPosInfos(p:Position):{min:Int, max:Int, file:String} {
return load("get_pos_infos", 1)(p);
}
/**
Builds a `Position` from `inf`.
**/
public static function makePosition(inf:{min:Int, max:Int, file:String}):Position {
return load("make_position", 3)(inf.min, inf.max, inf.file);
}
/**
Returns a map of all registered resources for this compilation unit.
Modifying the returned map has no effect on the compilation, use
`haxe.macro.Context.addResource` to add new resources to the compilation unit.
**/
public static function getResources():Map<String, haxe.io.Bytes> {
return load("get_resources", 0)();
}
/**
Makes resource `data` available as `name`.
The resource is then available using the `haxe.macro.Resource` API.
If a previous resource was bound to `name`, it is overwritten.
Compilation server : when using the compilation server, the resource is bound
to the Haxe module which calls the macro, so it will be included again if
that module is reused. If this resource concerns several modules, prefix its
name with a `$` sign, this will bind it to the macro module instead.
**/
public static function addResource(name:String, data:haxe.io.Bytes) {
load("add_resource", 2)(name, data);
}
/**
Returns an `Array` of fields of the class which is to be built.
This is only defined for `@:build/@:autoBuild` macros.
**/
public static function getBuildFields():Array<Field> {
return load("get_build_fields", 0)();
}
/**
Defines a new type from `TypeDefinition` `t`.
If `moduleDependency` is given and is not `null`, it should contain
a module path that will be used as a dependency for the newly defined module
instead of the current module.
**/
public static function defineType(t:TypeDefinition, ?moduleDependency:String):Void {
load("define_type", 2)(t, moduleDependency);
}
/**
Defines a new module as `modulePath` with several `TypeDefinition`
`types`. This is analogous to defining a .hx file.
The individual `types` can reference each other and any identifier
respects the `imports` and `usings` as usual, expect that imports are
not allowed to have `.*` wildcards or `as s` shorthands.
**/
public static function defineModule(modulePath:String, types:Array<TypeDefinition>, ?imports:Array<ImportExpr>, ?usings:Array<TypePath>):Void {
if (imports == null)
imports = [];
if (usings == null)
usings = [];
load("define_module", 4)(modulePath, types, imports, usings);
}
/**
Returns a syntax-level expression corresponding to typed expression `t`.
This process may lose some information.
**/
public static function getTypedExpr(t:Type.TypedExpr):Expr {
return load("get_typed_expr", 1)(t);
}
/**
Store typed expression `t` internally and give a syntax-level expression
that can be returned from a macro and will be replaced by the stored
typed expression.
If `t` is `null` or invalid, an exception is thrown.
NOTE: the returned value references an internally stored typed expression
that is reset between compilations, so care should be taken when storing
the expression returned by this method in a static variable and using the
compilation server.
**/
public static function storeTypedExpr(t:Type.TypedExpr):Expr {
return load("store_typed_expr", 1)(t);
}
/**
Types expression `e`, stores the resulting typed expression internally and
returns a syntax-level expression that can be returned from a macro and
will be replaced by the stored typed expression.
If `e` is `null` or invalid, an exception is thrown.
A call to `storeExpr(e)` is equivalent to `storeTypedExpr(typeExpr(e))` without
the overhead of encoding and decoding between regular and macro runtime.
NOTE: the returned value references an internally stored typed expression
that is reset between compilations, so care should be taken when storing
the expression returned by this method in a static variable and using the
compilation server.
**/
public static function storeExpr(e:Expr):Expr {
return load("store_expr", 1)(e);
}
/**
Manually adds a dependency between module `modulePath` and an external
file `externFile`.
This affects the compilation cache, causing the module to be typed if
`externFile` has changed.
Has no effect if the compilation cache is not used.
**/
public static function registerModuleDependency(modulePath:String, externFile:String) {
load("register_module_dependency", 2)(modulePath, externFile);
}
/**
Creates a timer which will be printed in the compilation report
if `--times` compilation argument is set.
Note that a timer may be omitted from the report if the amount of time
measured is too small.
This method immediately starts a timer and returns a function to stop it:
```
var stopTimer = haxe.macro.Context.timer("my heavy task");
runTask();
stopTimer();
```
**/
public static function timer(id:String):()->Void {
return load("timer", 1)(id);
}
@:deprecated
public static function registerModuleReuseCall(modulePath:String, macroCall:String) {
throw "This method is no longer supported. See https://github.com/HaxeFoundation/haxe/issues/5746";
}
@:deprecated
public static function onMacroContextReused(callb:Void->Bool) {
throw "This method is no longer supported. See https://github.com/HaxeFoundation/haxe/issues/5746";
}
@:allow(haxe.macro.TypeTools)
@:allow(haxe.macro.MacroStringTools)
@:allow(haxe.macro.TypedExprTools)
@:allow(haxe.macro.PositionTools)
static function load(f:String, nargs:Int):Dynamic {
#if neko
return neko.Lib.load("macro", f, nargs);
#elseif eval
return eval.vm.Context.callMacroApi(f);
#else
return Reflect.makeVarArgs(function(_) return throw "Can't be called outside of macro");
#end
}
private static function includeFile(file:String, position:String) {
load("include_file", 2)(file, position);
}
private static function sExpr(e:TypedExpr, pretty:Bool):String {
return haxe.macro.Context.load("s_expr", 2)(e, pretty);
}
#end
}

View File

@ -0,0 +1,258 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Type;
import haxe.macro.Expr;
using Lambda;
class ExampleJSGenerator {
var api:JSGenApi;
var buf:StringBuf;
var inits:List<TypedExpr>;
var statics:List<{c:ClassType, f:ClassField}>;
var packages:haxe.ds.StringMap<Bool>;
var forbidden:haxe.ds.StringMap<Bool>;
public function new(api) {
this.api = api;
buf = new StringBuf();
inits = new List();
statics = new List();
packages = new haxe.ds.StringMap();
forbidden = new haxe.ds.StringMap();
for (x in ["prototype", "__proto__", "constructor"])
forbidden.set(x, true);
api.setTypeAccessor(getType);
}
function getType(t:Type) {
return switch (t) {
case TInst(c, _): getPath(c.get());
case TEnum(e, _): getPath(e.get());
case TAbstract(a, _): getPath(a.get());
default: throw "assert";
};
}
inline function print(str:String) {
buf.add(str);
}
inline function newline() {
buf.add(";\n");
}
inline function genExpr(e) {
print(api.generateValue(e));
}
function field(p) {
return api.isKeyword(p) ? '["' + p + '"]' : "." + p;
}
function genPackage(p:Array<String>) {
var full = null;
for (x in p) {
var prev = full;
if (full == null)
full = x
else
full += "." + x;
if (packages.exists(full))
continue;
packages.set(full, true);
if (prev == null)
print('if(typeof $x==\'undefined\') $x = {}');
else {
var p = prev + field(x);
print('if(!$p) $p = {}');
}
newline();
}
}
function getPath(t:BaseType) {
return (t.pack.length == 0) ? t.name : t.pack.join(".") + "." + t.name;
}
function checkFieldName(c:ClassType, f:ClassField) {
if (forbidden.exists(f.name))
Context.error("The field " + f.name + " is not allowed in JS", c.pos);
}
function genClassField(c:ClassType, p:String, f:ClassField) {
checkFieldName(c, f);
var field = field(f.name);
print('$p.prototype$field = ');
var e = f.expr();
if (e == null)
print("null");
else {
genExpr(e);
}
newline();
}
function genStaticField(c:ClassType, p:String, f:ClassField) {
checkFieldName(c, f);
var field = field(f.name);
var e = f.expr();
if (e == null) {
print('$p$field = null');
newline();
} else
switch (f.kind) {
case FMethod(_):
print('$p$field = ');
genExpr(e);
newline();
default:
statics.add({c: c, f: f});
}
}
function genClass(c:ClassType) {
genPackage(c.pack);
api.setCurrentClass(c);
var p = getPath(c);
print('$p = $$hxClasses[\'$p\'] = ');
if (c.constructor != null)
genExpr(c.constructor.get().expr());
else
print("function() { }");
newline();
print('$p.__name__ = "$p"');
newline();
if (c.superClass != null) {
var psup = getPath(c.superClass.t.get());
print('$p.__super__ = $psup');
newline();
print('for(var k in $psup.prototype ) $p.prototype[k] = $psup.prototype[k]');
newline();
}
for (f in c.statics.get())
genStaticField(c, p, f);
for (f in c.fields.get()) {
switch (f.kind) {
case FVar(r, _):
if (r == AccResolve)
continue;
default:
}
genClassField(c, p, f);
}
print('$p.prototype.__class__ = $p');
newline();
if (c.interfaces.length > 0) {
var me = this;
var inter = c.interfaces.map(function(i) return me.getPath(i.t.get())).join(",");
print('$p.__interfaces__ = [$inter]');
newline();
}
}
function genEnum(e:EnumType) {
genPackage(e.pack);
var p = getPath(e);
var constructs = e.names.map(api.quoteString).join(",");
print('$p = $$hxClasses[\'$p\'] = { __ename__ : \'$p\', __constructs__ : [$constructs] }');
newline();
for (c in e.constructs.keys()) {
var c = e.constructs.get(c);
var f = field(c.name);
print('$p$f = ');
switch (c.type) {
case TFun(args, _):
var sargs = args.map(function(a) return a.name).join(",");
print('function($sargs) { var $$x = ["${c.name}",${c.index},$sargs]; $$x.__enum__ = $p; $$x.toString = $$estr; return $$x; }');
default:
print("[" + api.quoteString(c.name) + "," + c.index + "]");
newline();
print('$p$f.toString = $$estr');
newline();
print('$p$f.__enum__ = $p');
}
newline();
}
var meta = api.buildMetaData(e);
if (meta != null) {
print('$p.__meta__ = ');
genExpr(meta);
newline();
}
}
function genStaticValue(c:ClassType, cf:ClassField) {
var p = getPath(c);
var f = field(cf.name);
print('$p$f = ');
genExpr(cf.expr());
newline();
}
function genType(t:Type) {
switch (t) {
case TInst(c, _):
var c = c.get();
if (c.init != null)
inits.add(c.init);
if (!c.isExtern)
genClass(c);
case TEnum(r, _):
var e = r.get();
if (!e.isExtern)
genEnum(e);
default:
}
}
public function generate() {
print("var $_, $hxClasses = $hxClasses || {}, $estr = function() { return js.Boot.__string_rec(this,''); }");
newline();
print("function $bind(o,m) { var f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; return f; };");
newline();
for (t in api.types)
genType(t);
for (e in inits) {
print(api.generateStatement(e));
newline();
}
for (s in statics) {
genStaticValue(s.c, s.f);
newline();
}
if (api.main != null) {
genExpr(api.main);
newline();
}
sys.io.File.saveContent(api.outputFile, buf.toString());
}
#if (macro || display)
public static function use() {
Compiler.setCustomJSGenerator(function(api) new ExampleJSGenerator(api).generate());
}
#end
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,314 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Expr;
using Lambda;
/**
This class provides some utility methods to work with expressions. It is
best used through 'using haxe.macro.ExprTools' syntax and then provides
additional methods on haxe.macro.Expr instances.
While mainly intended to be used in macros, it works in non-macro code as
well.
**/
class ExprTools {
/**
Converts expression `e` to a human-readable String representation.
The result is guaranteed to be valid Haxe code, but there may be
differences from the original lexical syntax.
**/
static public function toString(e:Expr):String
return new Printer().printExpr(e);
/**
Calls function `f` on each sub-expression of `e`.
If `e` has no sub-expressions, this operation has no effect.
Otherwise `f` is called once per sub-expression of `e`, with the
sub-expression as argument. These calls are done in order of the
sub-expression declarations.
This method does not call itself recursively. It should instead be used
in a recursive function which handles the expression nodes of interest.
Usage example:
```haxe
function findStrings(e:Expr) {
switch(e.expr) {
case EConst(CString(s)):
// handle s
case _:
ExprTools.iter(e, findStrings);
}
}
```
**/
static public function iter(e:Expr, f:Expr->Void):Void {
switch (e.expr) {
case EConst(_), EContinue, EBreak, EDisplayNew(_):
case EField(e, _), EParenthesis(e), EUntyped(e), EThrow(e), EDisplay(e, _), ECheckType(e, _), EUnop(_, _, e), ECast(e, _), EIs(e, _) | EMeta(_, e):
f(e);
case EArray(e1, e2), EWhile(e1, e2, _), EBinop(_, e1, e2), EFor(e1, e2):
f(e1);
f(e2);
case EVars(vl):
for (v in vl)
opt2(v.expr, f);
case ETry(e, cl):
f(e);
for (c in cl)
f(c.expr);
case ETernary(e1, e2, e3) | EIf(e1, e2, e3):
f(e1);
f(e2);
opt2(e3, f);
case EArrayDecl(el), ENew(_, el), EBlock(el):
ExprArrayTools.iter(el, f);
case EObjectDecl(fl):
for (fd in fl)
f(fd.expr);
case ECall(e, el):
f(e);
ExprArrayTools.iter(el, f);
case EReturn(e):
opt2(e, f);
case EFunction(_, func):
for (arg in func.args)
opt2(arg.value, f);
opt2(func.expr, f);
case ESwitch(e, cl, edef):
f(e);
for (c in cl) {
ExprArrayTools.iter(c.values, f);
opt2(c.guard, f);
opt2(c.expr, f);
}
if (edef != null && edef.expr != null)
f(edef);
}
}
/**
Transforms the sub-expressions of `e` by calling `f` on each of them.
If `e` has no sub-expressions, this operation returns `e` unchanged.
Otherwise `f` is called once per sub-expression of `e`, with the
sub-expression as argument. These calls are done in order of the
sub-expression declarations.
This method does not call itself recursively. It should instead be used
in a recursive function which handles the expression nodes of interest.
Usage example:
```haxe
function capitalizeStrings(e:Expr) {
return switch(e.expr) {
case EConst(CString(s)):
{ expr: EConst(CString(s.toUpperCase())), pos: e.pos };
case _:
ExprTools.map(e, capitalizeStrings);
}
}
```
**/
static public function map(e:Expr, f:Expr->Expr):Expr {
return {
pos: e.pos,
expr: switch (e.expr) {
case EConst(_): e.expr;
case EArray(e1, e2): EArray(f(e1), f(e2));
case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
case EField(e, field): EField(f(e), field);
case EParenthesis(e): EParenthesis(f(e));
case EObjectDecl(fields):
var ret = [];
for (field in fields)
ret.push({field: field.field, expr: f(field.expr), quotes: field.quotes});
EObjectDecl(ret);
case EArrayDecl(el): EArrayDecl(ExprArrayTools.map(el, f));
case ECall(e, params): ECall(f(e), ExprArrayTools.map(params, f));
case ENew(tp, params): ENew(tp, ExprArrayTools.map(params, f));
case EUnop(op, postFix, e): EUnop(op, postFix, f(e));
case EVars(vars):
var ret = [];
for (v in vars) {
var v2:Var = {name: v.name, type: v.type, expr: opt(v.expr, f)};
if (v.isFinal != null)
v2.isFinal = v.isFinal;
ret.push(v2);
}
EVars(ret);
case EBlock(el): EBlock(ExprArrayTools.map(el, f));
case EFor(it, expr): EFor(f(it), f(expr));
case EIf(econd, eif, eelse): EIf(f(econd), f(eif), opt(eelse, f));
case EWhile(econd, e, normalWhile): EWhile(f(econd), f(e), normalWhile);
case EReturn(e): EReturn(opt(e, f));
case EUntyped(e): EUntyped(f(e));
case EThrow(e): EThrow(f(e));
case ECast(e, t): ECast(f(e), t);
case EIs(e, t): EIs(f(e), t);
case EDisplay(e, dk): EDisplay(f(e), dk);
case ETernary(econd, eif, eelse): ETernary(f(econd), f(eif), f(eelse));
case ECheckType(e, t): ECheckType(f(e), t);
case EDisplayNew(_), EContinue, EBreak:
e.expr;
case ETry(e, catches):
var ret = [];
for (c in catches)
ret.push({name: c.name, type: c.type, expr: f(c.expr)});
ETry(f(e), ret);
case ESwitch(e, cases, edef):
var ret = [];
for (c in cases)
ret.push({expr: opt(c.expr, f), guard: opt(c.guard, f), values: ExprArrayTools.map(c.values, f)});
ESwitch(f(e), ret, edef == null || edef.expr == null ? edef : f(edef));
case EFunction(kind, func):
var ret = [];
for (arg in func.args)
ret.push({
name: arg.name,
opt: arg.opt,
type: arg.type,
value: opt(arg.value, f)
});
EFunction(kind, {
args: ret,
ret: func.ret,
params: func.params,
expr: f(func.expr)
});
case EMeta(m, e): EMeta(m, f(e));
}
};
}
/**
Returns the value `e` represents.
Supported expressions are:
- `Int`, `Float` and `String` literals
- identifiers `true`, `false` and `null`
- structure declarations if all their fields are values
- array declarations if all their elements are values
- unary operators `-`, `!` and `~` if the operand is a value
- binary operators except `=>`, `...` and assignments
Parentheses, metadata and the `untyped` keyword are ignored.
If any non-value is encountered, an exception of type `String` is
thrown.
If `e` is null, the result is unspecified.
**/
static public function getValue(e:Expr):Dynamic {
return switch (e.expr) {
case EConst(CInt(v)): Std.parseInt(v);
case EConst(CFloat(v)): Std.parseFloat(v);
case EConst(CString(s)): s;
case EConst(CIdent("true")): true;
case EConst(CIdent("false")): false;
case EConst(CIdent("null")): null;
case EParenthesis(e1) | EUntyped(e1) | EMeta(_, e1): getValue(e1);
case EObjectDecl(fields):
var obj = {};
for (field in fields) {
Reflect.setField(obj, field.field, getValue(field.expr));
}
obj;
case EArrayDecl(el): el.map(getValue);
case EIf(econd, eif, eelse) | ETernary(econd, eif, eelse):
if (eelse == null) {
throw "If statements only have a value if the else clause is defined";
} else {
var econd:Dynamic = getValue(econd);
econd ? getValue(eif) : getValue(eelse);
}
case EUnop(op, false, e1):
var e1:Dynamic = getValue(e1);
switch (op) {
case OpNot: !e1;
case OpNeg: -e1;
case OpNegBits: ~e1;
case _: throw 'Unsupported expression: $e';
}
case EBinop(op, e1, e2):
var e1:Dynamic = getValue(e1);
var e2:Dynamic = getValue(e2);
switch (op) {
case OpAdd: e1 + e2;
case OpSub: e1 - e2;
case OpMult: e1 * e2;
case OpDiv: e1 / e2;
case OpMod: e1 % e2;
case OpEq: e1 == e2;
case OpNotEq: e1 != e2;
case OpLt: e1 < e2;
case OpLte: e1 <= e2;
case OpGt: e1 > e2;
case OpGte: e1 >= e2;
case OpOr: e1 | e2;
case OpAnd: e1 & e2;
case OpXor: e1 ^ e2;
case OpBoolAnd: e1 && e2;
case OpBoolOr: e1 || e2;
case OpShl: e1 << e2;
case OpShr: e1 >> e2;
case OpUShr: e1 >>> e2;
case _: throw 'Unsupported expression: $e';
}
case _: throw 'Unsupported expression: $e';
}
}
static inline function opt(e:Null<Expr>, f:Expr->Expr):Expr
return e == null ? null : f(e);
static inline function opt2(e:Null<Expr>, f:Expr->Void):Void
if (e != null)
f(e);
}
/**
This class provides functions on expression arrays for convenience. For a
detailed reference on each method, see the documentation of ExprTools.
**/
class ExprArrayTools {
static public function map(el:Array<Expr>, f:Expr->Expr):Array<Expr> {
var ret = [];
for (e in el)
ret.push(f(e));
return ret;
}
static public function iter(el:Array<Expr>, f:Expr->Void):Void {
for (e in el)
f(e);
}
}

View File

@ -0,0 +1,119 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Expr;
import haxe.macro.Context;
/**
The actual macro implemented for Std.format
**/
class Format {
#if (macro || display)
public static function format(estr:Expr) {
var str = switch (estr.expr) {
case EConst(c): switch (c) {
case CString(s): s;
default: null;
}
default: null;
};
if (str == null)
Context.error("Constant string required", estr.pos);
var pos = Context.getPosInfos(estr.pos);
var min = pos.min;
pos.min++;
var expr = null;
function make(size) {
pos.max = pos.min + size;
var p = Context.makePosition(pos);
pos.min += size;
return p;
}
function add(e) {
if (expr == null)
expr = e;
else
expr = {expr: EBinop(OpAdd, expr, e), pos: Context.makePosition({min: min, max: pos.min, file: pos.file})};
}
var i = 0, start = 0;
var max = str.length;
while (i < max) {
if (StringTools.fastCodeAt(str, i++) != '$'.code)
continue;
var len = i - start - 1;
if (len > 0 || expr == null)
add({expr: EConst(CString(str.substr(start, len))), pos: make(len)});
pos.min++;
start = i;
var c = StringTools.fastCodeAt(str, i);
if (c == '{'.code) {
var count = 1;
i++;
while (i < max) {
var c = StringTools.fastCodeAt(str, i++);
if (c == "}".code) {
if (--count == 0)
break;
} else if (c == "{".code)
count++;
}
if (count > 0)
Context.error("Closing brace not found", make(1));
pos.min++;
start++;
var len = i - start - 1;
var expr = str.substr(start, len);
add(Context.parseInlineString(expr, make(len)));
pos.min++;
start++;
} else if ((c >= 'a'.code && c <= 'z'.code) || (c >= 'A'.code && c <= 'Z'.code) || c == '_'.code) {
i++;
while (true) {
var c = StringTools.fastCodeAt(str, i);
if ((c >= 'a'.code && c <= 'z'.code) || (c >= 'A'.code && c <= 'Z'.code) || (c >= '0'.code && c <= '9'.code) || c == '_'.code)
i++;
else
break;
}
var len = i - start;
var ident = str.substr(start, len);
add({expr: EConst(CIdent(ident)), pos: make(len)});
} else if (c == '$'.code) {
start = i++;
continue;
} else {
start = i - 1;
continue;
}
start = i;
}
var len = i - start;
if (len > 0)
add({expr: EConst(CString(str.substr(start, len))), pos: make(len)});
if (expr == null)
expr = {expr: EConst(CString("")), pos: make(0)};
return {expr: ECheckType(expr, TPath({pack: [], name: "String", params: []})), pos: expr.pos};
}
#end
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Type;
/**
This is the api that is passed to the custom JS generator.
**/
typedef JSGenApi = {
/** the file in which the JS code can be generated **/
var outputFile:String;
/** all the types that were compiled by Haxe **/
var types:Array<Type>;
/** the main call expression, if a -main class is defined **/
var main:Null<TypedExpr>;
/** generate the JS code for any given typed expression **/
function generateStatement(e:TypedExpr):String;
/** generate the JS code for a given typed expression-value **/
function generateValue(e:TypedExpr):String;
/** define the JS code that gets generated when a class or enum is accessed in a typed expression **/
function setTypeAccessor(callb:Type->String):Void;
/** tells if the given identifier is a JS keyword **/
function isKeyword(ident:String):Bool;
/** add a feature **/
function addFeature(f:String):Bool;
/** check if a feature is used **/
function hasFeature(f:String):Bool;
/** quote and escape the given string constant **/
function quoteString(s:String):String;
/** create the metadata expression for the given type **/
function buildMetaData(t:BaseType):Null<TypedExpr>;
/** select the current classe **/
function setCurrentClass(c:ClassType):Void;
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Expr;
/**
This class provides some utility methods to work with strings in macro
context.
**/
#if hl
@:hlNative("macro")
#end
class MacroStringTools {
#if (macro || display)
/**
Formats `String` `s` using the usual interpolation rules.
The returned expression is a concatenation of string parts and escaped
elements.
**/
static public function formatString(s:String, pos:Position):Expr {
#if (neko || eval)
return Context.load("format_string", 2)(s, pos);
#end
}
/**
Tells if `e` is a format string, i.e. uses single quotes `'` as
delimiters.
**/
static public function isFormatExpr(e:ExprOf<String>):Bool {
return e.expr.match(EConst(CString(_, SingleQuotes)));
}
#end
/**
Converts an array of Strings `sl` to a field expression.
If `sl` has no elements, the result is null.
If `sl` has one element, the result is `EConst(CIdent(sl[0])`.
Otherwise the result is a chain of `EField` nodes.
If `sl` is null, the result is unspecified.
**/
static public function toFieldExpr(sl:Array<String>, ?pos):Expr {
if (pos == null)
return Lambda.fold(sl, function(s, e) return e == null ? (macro $i{s}) : (macro $e.$s), null);
var e = null;
for (v in sl)
if (e == null)
e = {expr: EConst(CIdent(v)), pos: pos};
else
e = {expr: EField(e, v), pos: pos};
return e;
}
/**
Converts a path given by package `pack` and name `name` to a `String`
separated by dots.
If `pack` has no elements, the result is `name`.
If `pack` is null, the result is unspecified.
Otherwise the elements of `pack` are joined with a separating dot, with
an appended dot separating the result from `name`.
**/
static public function toDotPath(pack:Array<String>, name:String):String {
return if (pack.length == 0) name else pack.join(".") + "." + name;
}
static public function toComplex(path:String):ComplexType {
var pack = path.split(".");
return TPath({pack: pack, name: pack.pop(), params: []});
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
/**
This type is meant to be used to generate custom types using a macro.
For instance by doing MacroType<[my.Class.myMacro(55)]>
**/
extern class MacroType<Const> {}

View File

@ -0,0 +1,74 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.display.Position.Location;
import haxe.macro.Expr;
class PositionTools {
/**
Returns the `Position` where the caller of `here` is.
**/
macro public static function here():ExprOf<Position> {
var positionExpr = Context.makeExpr(Context.getPosInfos(Context.currentPos()), Context.currentPos());
if (Context.defined("macro")) {
return macro haxe.macro.Context.makePosition($positionExpr);
} else {
return positionExpr;
}
}
/**
Like `Context.getPosInfos`, except this method is available on all platforms.
**/
public static function getInfos(p:Position):{min:Int, max:Int, file:String} {
#if macro
return Context.getPosInfos(p);
#else
return p;
#end
}
/**
Like `Context.makePosition`, except this method is available on all platforms.
**/
public static function make(inf:{min:Int, max:Int, file:String}):Position {
#if macro
return Context.makePosition(inf);
#else
return inf;
#end
}
#if (macro || display)
/**
Converts a `haxe.macro.Position` to a `haxe.display.Position.Location`.
This operation requires the source file the be known to the Haxe lexer in order
to determine line breaks. It is thus only available in macro context.
**/
public static function toLocation(p:Position):Location {
return Context.load("position_to_range", 1)(p);
}
#end
}

View File

@ -0,0 +1,564 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Expr;
using Lambda;
using StringTools;
/**
This class provides some utility methods to convert elements from the
macro context to a human-readable String representation.
*/
class Printer {
var tabs:String;
var tabString:String;
public function new(?tabString = "\t") {
tabs = "";
this.tabString = tabString;
}
public function printUnop(op:Unop)
return switch (op) {
case OpIncrement: "++";
case OpDecrement: "--";
case OpNot: "!";
case OpNeg: "-";
case OpNegBits: "~";
case OpSpread: "...";
}
public function printBinop(op:Binop)
return switch (op) {
case OpAdd: "+";
case OpMult: "*";
case OpDiv: "/";
case OpSub: "-";
case OpAssign: "=";
case OpEq: "==";
case OpNotEq: "!=";
case OpGt: ">";
case OpGte: ">=";
case OpLt: "<";
case OpLte: "<=";
case OpAnd: "&";
case OpOr: "|";
case OpXor: "^";
case OpBoolAnd: "&&";
case OpBoolOr: "||";
case OpShl: "<<";
case OpShr: ">>";
case OpUShr: ">>>";
case OpMod: "%";
case OpInterval: "...";
case OpArrow: "=>";
case OpIn: "in";
case OpAssignOp(op):
printBinop(op) + "=";
}
function escapeString(s:String, delim:String) {
return delim
+ s.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("\r", "\\r")
.replace("'", "\\'")
.replace('"', "\\\"") #if sys .replace("\x00", "\\x00") #end + delim;
}
public function printFormatString(s:String) {
return escapeString(s, "'");
}
public function printString(s:String) {
return escapeString(s, '"');
}
public function printConstant(c:Constant)
return switch (c) {
case CString(s, SingleQuotes): printFormatString(s);
case CString(s, _): printString(s);
case CIdent(s), CInt(s), CFloat(s):
s;
case CRegexp(s, opt): '~/$s/$opt';
}
public function printTypeParam(param:TypeParam)
return switch (param) {
case TPType(ct): printComplexType(ct);
case TPExpr(e): printExpr(e);
}
public function printTypePath(tp:TypePath)
return (tp.pack.length > 0 ? tp.pack.join(".") + "." : "")
+ tp.name
+ (tp.sub != null ? '.${tp.sub}' : "")
+ (tp.params == null ? "" : tp.params.length > 0 ? "<" + tp.params.map(printTypeParam).join(", ") + ">" : "");
// TODO: check if this can cause loops
public function printComplexType(ct:ComplexType)
return switch (ct) {
case TPath(tp): printTypePath(tp);
case TFunction(args, ret):
var wrapArgumentsInParentheses = switch args {
// type `:(a:X) -> Y` has args as [TParent(TNamed(...))], i.e `a:X` gets wrapped in `TParent()`. We don't add parentheses to avoid printing `:((a:X)) -> Y`
case [TParent(t)]: false;
// this case catches a single argument that's a type-path, so that `X -> Y` prints `X -> Y` not `(X) -> Y`
case [TPath(_) | TOptional(TPath(_))]: false;
default: true;
}
var argStr = args.map(printComplexType).join(", ");
(wrapArgumentsInParentheses ? '($argStr)' : argStr) + " -> " + (switch ret {
// wrap return type in parentheses if it's also a function
case TFunction(_): '(${printComplexType(ret)})';
default: (printComplexType(ret): String);
});
case TAnonymous(fields): "{ " + [for (f in fields) printField(f) + "; "].join("") + "}";
case TParent(ct): "(" + printComplexType(ct) + ")";
case TOptional(ct): "?" + printComplexType(ct);
case TNamed(n, ct): n + ":" + printComplexType(ct);
case TExtend(tpl, fields):
var types = [for (t in tpl) "> " + printTypePath(t) + ", "].join("");
var fields = [for (f in fields) printField(f) + "; "].join("");
'{${types}${fields}}';
case TIntersection(tl): tl.map(printComplexType).join(" & ");
}
public function printMetadata(meta:MetadataEntry)
return '@${meta.name}' + ((meta.params != null && meta.params.length > 0) ? '(${printExprs(meta.params, ", ")})' : "");
public function printAccess(access:Access)
return switch (access) {
case AStatic: "static";
case APublic: "public";
case APrivate: "private";
case AOverride: "override";
case AInline: "inline";
case ADynamic: "dynamic";
case AMacro: "macro";
case AFinal: "final";
case AExtern: "extern";
case AAbstract: "abstract";
case AOverload: "overload";
}
public function printField(field:Field) {
inline function orderAccess(access: Array<Access>) {
// final should always be printed last
// (does not modify input array)
return access.has(AFinal) ? access.filter(a -> !a.match(AFinal)).concat([AFinal]) : access;
}
return (field.doc != null
&& field.doc != "" ? "/**\n"
+ tabs
+ tabString
+ StringTools.replace(field.doc, "\n", "\n" + tabs + tabString)
+ "\n"
+ tabs
+ "**/\n"
+ tabs : "")
+ (field.meta != null && field.meta.length > 0 ? field.meta.map(printMetadata).join('\n$tabs') + '\n$tabs' : "")
+ (field.access != null && field.access.length > 0 ? orderAccess(field.access).map(printAccess).join(" ") + " " : "")
+ switch (field.kind) {
case FVar(t, eo): ((field.access != null && field.access.has(AFinal)) ? '' : 'var ') + '${field.name}' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
case FProp(get, set, t, eo): 'var ${field.name}($get, $set)' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
case FFun(func): 'function ${field.name}' + printFunction(func);
}
}
public function printTypeParamDecl(tpd:TypeParamDecl)
return (tpd.meta != null && tpd.meta.length > 0 ? tpd.meta.map(printMetadata).join(" ") + " " : "")
+ tpd.name
+ (tpd.params != null && tpd.params.length > 0 ? "<" + tpd.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (tpd.constraints != null && tpd.constraints.length > 0 ? ":(" + tpd.constraints.map(printComplexType).join(", ") + ")" : "");
public function printFunctionArg(arg:FunctionArg)
return (arg.opt ? "?" : "") + arg.name + opt(arg.type, printComplexType, ":") + opt(arg.value, printExpr, " = ");
public function printFunction(func:Function, ?kind:FunctionKind) {
var skipParentheses = switch func.args {
case [{ type:null }]: kind == FArrow;
case _: false;
}
return (func.params == null ? "" : func.params.length > 0 ? "<" + func.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (skipParentheses ? "" : "(")
+ func.args.map(printFunctionArg).join(", ")
+ (skipParentheses ? "" : ")")
+ (kind == FArrow ? " ->" : "")
+ opt(func.ret, printComplexType, ":")
+ opt(func.expr, printExpr, " ");
}
public function printVar(v:Var) {
var s = v.name + opt(v.type, printComplexType, ":") + opt(v.expr, printExpr, " = ");
return switch v.meta {
case null|[]: s;
case meta: meta.map(printMetadata).join(" ") + " " + s;
}
}
public function printObjectFieldKey(of:ObjectField) {
return switch (of.quotes) {
case null | Unquoted: of.field;
case Quoted: '"${of.field}"'; // TODO: Have to escape that?
}
}
public function printObjectField(of:ObjectField) {
return '${printObjectFieldKey(of)} : ${printExpr(of.expr)}';
}
public function printExpr(e:Expr)
return e == null ? "#NULL" : switch (e.expr) {
case EConst(c): printConstant(c);
case EArray(e1, e2): '${printExpr(e1)}[${printExpr(e2)}]';
case EBinop(op, e1, e2): '${printExpr(e1)} ${printBinop(op)} ${printExpr(e2)}';
case EField(e1, n): '${printExpr(e1)}.$n';
case EParenthesis(e1): '(${printExpr(e1)})';
case EObjectDecl(fl):
"{ " + fl.map(function(fld) return printObjectField(fld)).join(", ") + " }";
case EArrayDecl(el): '[${printExprs(el, ", ")}]';
case ECall(e1, el): '${printExpr(e1)}(${printExprs(el, ", ")})';
case ENew(tp, el): 'new ${printTypePath(tp)}(${printExprs(el, ", ")})';
case EUnop(op, true, e1): printExpr(e1) + printUnop(op);
case EUnop(op, false, e1): printUnop(op) + printExpr(e1);
case EFunction(FNamed(no,inlined), func): (inlined ? 'inline ' : '') + 'function $no' + printFunction(func);
case EFunction(kind, func): (kind != FArrow ? "function" : "") + printFunction(func, kind);
case EVars(vl): "var " + vl.map(printVar).join(", ");
case EBlock([]): '{ }';
case EBlock(el):
var old = tabs;
tabs += tabString;
var s = '{\n$tabs' + printExprs(el, ';\n$tabs');
tabs = old;
s + ';\n$tabs}';
case EFor(e1, e2): 'for (${printExpr(e1)}) ${printExpr(e2)}';
case EIf(econd, eif, null): 'if (${printExpr(econd)}) ${printExpr(eif)}';
case EIf(econd, eif, eelse): 'if (${printExpr(econd)}) ${printExpr(eif)} else ${printExpr(eelse)}';
case EWhile(econd, e1, true): 'while (${printExpr(econd)}) ${printExpr(e1)}';
case EWhile(econd, e1, false): 'do ${printExpr(e1)} while (${printExpr(econd)})';
case ESwitch(e1, cl, edef):
var old = tabs;
tabs += tabString;
var s = 'switch ${printExpr(e1)} {\n$tabs'
+ cl.map(function(c) return 'case ${printExprs(c.values, ", ")}' + (c.guard != null ? ' if (${printExpr(c.guard)}):' : ":")
+ (c.expr != null ? (opt(c.expr, printExpr)) + ";" : ""))
.join('\n$tabs');
if (edef != null)
s += '\n${tabs}default:' + (edef.expr == null ? "" : printExpr(edef) + ";");
tabs = old;
s + '\n$tabs}';
case ETry(e1, cl):
'try ${printExpr(e1)}' + cl.map(function(c) return ' catch(${c.name}${c.type == null ? '' : (':' + printComplexType(c.type))}) ${printExpr(c.expr)}').join("");
case EReturn(eo): "return" + opt(eo, printExpr, " ");
case EBreak: "break";
case EContinue: "continue";
case EUntyped(e1): "untyped " + printExpr(e1);
case EThrow(e1): "throw " + printExpr(e1);
case ECast(e1, cto) if (cto != null): 'cast(${printExpr(e1)}, ${printComplexType(cto)})';
case ECast(e1, _): "cast " + printExpr(e1);
case EIs(e1, ct): '${printExpr(e1)} is ${printComplexType(ct)}';
case EDisplay(e1, _): '#DISPLAY(${printExpr(e1)})';
case EDisplayNew(tp): '#DISPLAY(${printTypePath(tp)})';
case ETernary(econd, eif, eelse): '${printExpr(econd)} ? ${printExpr(eif)} : ${printExpr(eelse)}';
case ECheckType(e1, ct): '(${printExpr(e1)} : ${printComplexType(ct)})';
case EMeta({ name:":implicitReturn" }, { expr:EReturn(e1) }): printExpr(e1);
case EMeta(meta, e1): printMetadata(meta) + " " + printExpr(e1);
}
public function printExprs(el:Array<Expr>, sep:String) {
return el.map(printExpr).join(sep);
}
function printExtension(tpl:Array<TypePath>, fields:Array<Field>) {
return '{\n$tabs>'
+ tpl.map(printTypePath).join(',\n$tabs>')
+ ","
+ (fields.length > 0 ? ('\n$tabs' + fields.map(printField).join(';\n$tabs') + ";\n}") : ("\n}"));
}
function printStructure(fields:Array<Field>) {
return fields.length == 0 ? "{ }" : '{\n$tabs' + fields.map(printField).join(';\n$tabs') + ";\n}";
}
public function printTypeDefinition(t:TypeDefinition, printPackage = true):String {
var old = tabs;
tabs = tabString;
var str = t == null ? "#NULL" : (printPackage && t.pack.length > 0 && t.pack[0] != "" ? "package " + t.pack.join(".") + ";\n" : "")
+ (t.doc != null && t.doc != "" ? "/**\n" + tabString + StringTools.replace(t.doc, "\n", "\n" + tabString) + "\n**/\n" : "")
+ (t.meta != null && t.meta.length > 0 ? t.meta.map(printMetadata).join(" ") + " " : "")
+ (t.isExtern ? "extern " : "")
+ switch (t.kind) {
case TDEnum:
"enum "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ " {\n"
+ [
for (field in t.fields)
tabs
+ (field.doc != null
&& field.doc != "" ? "/**\n"
+ tabs
+ tabString
+ StringTools.replace(field.doc, "\n", "\n" + tabs + tabString)
+ "\n"
+ tabs
+ "**/\n"
+ tabs : "")
+ (field.meta != null && field.meta.length > 0 ? field.meta.map(printMetadata).join(" ") + " " : "")
+ (switch (field.kind) {
case FVar(t, _): field.name + opt(t, printComplexType, ":");
case FProp(_, _, _, _): throw "FProp is invalid for TDEnum.";
case FFun(func): field.name + printFunction(func);
})
+ ";"].join("\n")
+ "\n}";
case TDStructure:
"typedef "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ " = {\n"
+ [
for (f in t.fields) {
tabs + printField(f) + ";";
}
].join("\n")
+ "\n}";
case TDClass(superClass, interfaces, isInterface, isFinal, isAbstract):
(isFinal ? "final " : "")
+ (isAbstract ? "abstract " : "")
+ (isInterface ? "interface " : "class ")
+ t.name
+ (t.params != null && t.params.length > 0 ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (superClass != null ? " extends " + printTypePath(superClass) : "")
+ (interfaces != null ? (isInterface ? [for (tp in interfaces) " extends " + printTypePath(tp)] : [
for (tp in interfaces)
" implements " + printTypePath(tp)
]).join("") : "")
+ " {\n"
+ [
for (f in t.fields) {
tabs + printFieldWithDelimiter(f);
}
].join("\n")
+ "\n}";
case TDAlias(ct):
"typedef "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ " = "
+ (switch (ct) {
case TExtend(tpl, fields): printExtension(tpl, fields);
case TAnonymous(fields): printStructure(fields);
case _: printComplexType(ct);
})
+ ";";
case TDAbstract(tthis, from, to):
"abstract "
+ t.name
+ ((t.params != null && t.params.length > 0) ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")
+ (tthis == null ? "" : "(" + printComplexType(tthis) + ")")
+ (from == null ? "" : [for (f in from) " from " + printComplexType(f)].join(""))
+ (to == null ? "" : [for (t in to) " to " + printComplexType(t)].join(""))
+ " {\n"
+ [
for (f in t.fields) {
tabs + printFieldWithDelimiter(f);
}
].join("\n")
+ "\n}";
case TDField(kind, access):
tabs = old;
(access != null && access.length > 0 ? access.map(printAccess).join(" ") + " " : "")
+ switch (kind) {
case FVar(type, eo): ((access != null && access.has(AFinal)) ? '' : 'var ') + '${t.name}' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
case FProp(get, set, type, eo): 'var ${t.name}($get, $set)' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
case FFun(func): 'function ${t.name}' + printFunction(func) + switch func.expr { case {expr: EBlock(_)}: ""; case _: ";"; };
}
} tabs = old;
return str;
}
function printFieldWithDelimiter(f:Field):String {
return printField(f) + switch (f.kind) {
case FVar(_, _), FProp(_, _, _, _): ";";
case FFun({expr: null}): ";";
case FFun({expr: {expr: EBlock(_)}}): "";
case FFun(_): ";";
case _: "";
};
}
function opt<T>(v:T, f:T->String, prefix = "")
return v == null ? "" : (prefix + f(v));
public function printExprWithPositions(e:Expr) {
var buffer = new StringBuf();
function format4(i:Int) {
return StringTools.lpad(Std.string(i), " ", 4);
}
function loop(tabs:String, e:Expr) {
function add(s:String, ?p = null) {
if (p == null) {
p = e.pos;
}
var p = #if macro haxe.macro.Context.getPosInfos(p) #else e.pos #end;
buffer.add('${format4(p.min)}-${format4(p.max)} $tabs$s\n');
}
function loopI(e:Expr)
loop(tabs + tabString, e);
switch (e.expr) {
case EConst(c):
add(printConstant(c));
case EArray(e1, e2):
add("EArray");
loopI(e1);
loopI(e2);
case EBinop(op, e1, e2):
add("EBinop " + printBinop(op));
loopI(e1);
loopI(e2);
case EField(e, field):
add("EField " + field);
loopI(e);
case EParenthesis(e):
add("EParenthesis");
loopI(e);
case EObjectDecl(fields):
add("EObjectDecl");
for (field in fields) {
add(field.field); // TODO: we don't have the field pos?
loopI(field.expr);
}
case EArrayDecl(values):
add("EArrayDecl");
values.iter(loopI);
case ECall(e, params):
add("ECall");
loopI(e);
params.iter(loopI);
case ENew(tp, params):
add("ENew " + printTypePath(tp));
params.iter(loopI);
case EUnop(op, postFix, e):
add("EUnop " + printUnop(op));
loopI(e);
case EVars(vars):
add("EVars");
for (v in vars) {
if (v.expr != null) {
add(v.name);
loopI(v.expr);
}
}
case EFunction(_, f):
add("EFunction");
if (f.expr != null) {
loopI(f.expr);
}
case EBlock(exprs):
add("EBlock");
exprs.iter(loopI);
case EFor(it, expr):
add("EFor");
loopI(it);
loopI(expr);
case EIf(econd, eif, eelse):
add("EIf");
loopI(econd);
loopI(eif);
if (eelse != null) {
loopI(eelse);
}
case EWhile(econd, e, normalWhile):
add("EWhile");
loopI(econd);
loopI(e);
case ESwitch(e, cases, edef):
add("ESwitch");
loopI(e);
for (c in cases) {
for (pat in c.values) {
loop(tabs + tabString + tabString, pat);
}
if (c.expr != null) {
loop(tabs + tabString + tabString + tabString, c.expr);
}
}
if (edef != null) {
loop(tabs + tabString + tabString + tabString, edef);
}
case ETry(e, catches):
add("ETry");
loopI(e);
for (c in catches) {
loop(tabs + tabString + tabString, c.expr);
}
case EReturn(e):
add("EReturn");
if (e != null) {
loopI(e);
}
case EBreak:
add("EBreak");
case EContinue:
add("EContinue");
case EUntyped(e):
add("EUntyped");
loopI(e);
case EThrow(e):
add("EThrow");
loopI(e);
case ECast(e, t):
add("ECast");
loopI(e);
case EIs(e, t):
add("EIs");
loopI(e);
case EDisplay(e, displayKind):
add("EDisplay");
loopI(e);
case EDisplayNew(t):
add("EDisplayNew");
case ETernary(econd, eif, eelse):
add("ETernary");
loopI(econd);
loopI(eif);
loopI(eelse);
case ECheckType(e, t):
add("ECheckType");
loopI(e);
case EMeta(s, e):
add("EMeta " + printMetadata(s));
loopI(e);
}
}
loop("", e);
return buffer.toString();
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
/**
This class can be added via `using haxe.macro.Tools` in order to enable
`using` functionality on these macro tool classes:
- `haxe.macro.ExprTools`
- `haxe.macro.ComplexTypeTools`
- `haxe.macro.TypeTools`
- `haxe.macro.MacroStringTools`
- `haxe.macro.TypedExprTools`
- `haxe.macro.PositionTools`
@see <https://haxe.org/manual/lf-static-extension.html>
**/
@:dox(hide)
typedef TExprTools = ExprTools;
@:dox(hide)
typedef TComplexTypeTools = ComplexTypeTools;
@:dox(hide)
typedef TTypeTools = TypeTools;
@:dox(hide)
typedef TMacroStringTools = MacroStringTools;
@:dox(hide)
typedef TTypedExprTools = TypedExprTools;
@:dox(hide)
typedef TPositionTools = PositionTools;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,384 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using Lambda;
/**
This class provides some utility methods to work with types. It is
best used through 'using haxe.macro.TypeTools' syntax and then provides
additional methods on haxe.macro.Type instances.
**/
#if hl
@:hlNative("macro")
#end
class TypeTools {
static function nullable(complexType:ComplexType):ComplexType
return macro:Null<$complexType>;
static function toField(cf:ClassField):Field
return {
function varAccessToString(va:VarAccess, getOrSet:String):String
return {
switch (va) {
case AccNormal | AccCtor: "default";
case AccNo: "null";
case AccNever: "never";
case AccResolve: throw "Invalid TAnonymous";
case AccCall: getOrSet;
case AccInline: "default";
case AccRequire(_, _): "default";
}
}
var access = cf.isPublic ? [APublic] : [APrivate];
if (cf.meta.has(":final")) {
access.push(AFinal);
}
if (cf.params.length == 0)
{
name: cf.name,
doc: cf.doc,
access: access,
kind: switch ([cf.kind, cf.type]) {
case [FVar(read, write), ret]:
FProp(varAccessToString(read, "get"), varAccessToString(write, "set"), toComplexType(ret), null);
case [FMethod(_), TFun(args, ret)]:
FFun({
args: [
for (a in args)
{
name: a.name,
opt: a.opt,
type: toComplexType(a.t),
}
],
ret: toComplexType(ret),
expr: null,
});
default:
throw "Invalid TAnonymous";
},
pos: cf.pos,
meta: cf.meta.get(),
} else {
throw "Invalid TAnonymous";
}
}
/**
Returns a syntax-level type corresponding to Type `t`.
This function is mostly inverse to `ComplexTypeTools.toType`, but may
lose some information on types that do not have a corresponding syntax
version, such as monomorphs. In these cases, the result is null.
If `t` is null, an internal exception is thrown.
**/
public static function toComplexType(type:Null<Type>):Null<ComplexType>
return {
#if macro
Context.toComplexType(type);
#else
switch (type) {
case null:
null;
case TMono(_.get() => t):
t == null ? null : toComplexType(t);
case TEnum(_.get() => baseType, params):
TPath(toTypePath(baseType, params));
case TInst(_.get() => classType, params):
switch (classType.kind) {
case KTypeParameter(_):
TPath({
name: classType.name,
pack: [],
});
default:
TPath(toTypePath(classType, params));
}
case TType(_.get() => baseType, params):
TPath(toTypePath(baseType, params));
case TFun(args, ret):
TFunction([for (a in args) a.opt ? nullable(toComplexType(a.t)) : toComplexType(a.t)], toComplexType(ret));
case TAnonymous(_.get() => {fields: fields}):
TAnonymous([for (cf in fields) toField(cf)]);
case TDynamic(t):
if (t == null) {
macro:Dynamic;
} else {
var ct = toComplexType(t);
macro:Dynamic<$ct>;
}
case TLazy(f):
toComplexType(f());
case TAbstract(_.get() => baseType, params):
TPath(toTypePath(baseType, params));
default:
throw "Invalid type";
}
#end
}
static function toTypeParam(type:Type):TypeParam
return {
switch (type) {
case TInst(_.get() => {kind: KExpr(e)}, _): TPExpr(e);
case _: TPType(toComplexType(type));
}
}
static function toTypePath(baseType:BaseType, params:Array<Type>):TypePath
return {
var module = baseType.module;
{
pack: baseType.pack,
name: module.substring(module.lastIndexOf(".") + 1),
sub: baseType.name,
params: [for (t in params) toTypeParam(t)],
}
}
#if (macro || display)
/**
Follows all typedefs of `t` to reach the actual type.
If `once` is true, this function does not call itself recursively,
otherwise it does. This can be useful in cases where intermediate
typedefs might be of interest.
Affected types are monomorphs `TMono` and typedefs `TType(t,pl)`.
If `t` is null, an internal exception is thrown.
Usage example:
var t = Context.typeof(macro null); // TMono(<mono>)
var ts = Context.typeof(macro "foo"); //TInst(String,[])
Context.unify(t, ts);
trace(t); // TMono(<mono>)
trace(t.follow()); //TInst(String,[])
**/
static public inline function follow(t:Type, ?once:Bool):Type
return Context.follow(t, once);
/**
Like `follow`, follows all typedefs of `t` to reach the actual type.
Will however follow also abstracts to their underlying implementation,
if they are not a @:coreType abstract
If `t` is null, an internal exception is thrown.
Usage example:
var t = Context.typeof(macro new Map<String, String>());
trace(t); // TAbstract(Map,[TInst(String,[]),TInst(String,[])])
trace(t.followWithAbstracts()); // TInst(haxe.ds.StringMap, [TInst(String,[])])
**/
static public inline function followWithAbstracts(t:Type, once:Bool = false):Type
return Context.followWithAbstracts(t, once);
/**
Returns true if `t1` and `t2` unify, false otherwise.
**/
static public inline function unify(t1:Type, t2:Type):Bool
return Context.unify(t1, t2);
/**
Tries to extract the class instance stored inside `t`.
If `t` is a class instance `TInst(c,pl)`, c is returned.
If `t` is of a different type, an exception of type String is thrown.
If `t` is null, the result is null.
**/
static public function getClass(t:Type)
return t == null ? null : switch (follow(t)) {
case TInst(c, _): c.get();
case _: throw "Class instance expected";
}
/**
Tries to extract the enum instance stored inside `t`.
If `t` is an enum instance `TEnum(e,pl)`, e is returned.
If `t` is of a different type, an exception of type String is thrown.
If `t` is null, the result is null.
**/
static public function getEnum(t:Type)
return t == null ? null : switch (follow(t)) {
case TEnum(e, _): e.get();
case _: throw "Enum instance expected";
}
/**
Applies the type parameters `typeParameters` to type `t` with the given
types `concreteTypes`.
This function replaces occurrences of type parameters in `t` if they are
part of `typeParameters`. The array index of such a type parameter is
then used to lookup the concrete type in `concreteTypes`.
If `typeParameters.length` is not equal to `concreteTypes.length`, an
exception of type `String` is thrown.
If `typeParameters.length` is 0, `t` is returned unchanged.
If either argument is `null`, the result is unspecified.
**/
static public function applyTypeParameters(t:Type, typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>):Type {
if (typeParameters.length != concreteTypes.length)
throw 'Incompatible arguments: ${typeParameters.length} type parameters and ${concreteTypes.length} concrete types';
else if (typeParameters.length == 0)
return t;
#if (neko || eval)
return Context.load("apply_params", 3)(typeParameters, concreteTypes, t);
#else
return applyParams(typeParameters, concreteTypes, t);
#end
}
#if !neko
private static function applyParams(typeParameters:Array<TypeParameter>, concreteTypes:Array<Type>, t:Type):Type {
return null;
}
#end
/**
Transforms `t` by calling `f` on each of its subtypes.
If `t` is a compound type, `f` is called on each of its components.
Otherwise `t` is returned unchanged.
The following types are considered compound:
- TInst, TEnum, TType and TAbstract with type parameters
- TFun
- TAnonymous
If `t` or `f` are null, the result is unspecified.
**/
static public function map(t:Type, f:Type->Type):Type {
return switch (t) {
case TMono(tm):
switch (tm.get()) {
case null: t;
case var t: f(t);
}
case TEnum(_, []) | TInst(_, []) | TType(_, []):
t;
case TEnum(en, tl):
TEnum(en, tl.map(f));
case TInst(cl, tl):
TInst(cl, tl.map(f));
case TType(t2, tl):
TType(t2, tl.map(f));
case TAbstract(a, tl):
TAbstract(a, tl.map(f));
case TFun(args, ret):
TFun(args.map(function(arg) return {
name: arg.name,
opt: arg.opt,
t: f(arg.t)
}), f(ret));
case TAnonymous(an):
TAnonymous(Context.load("map_anon_ref", 2)(an, f));
case TDynamic(t2):
t == t2 ? t : TDynamic(f(t2));
case TLazy(ft):
var ft = ft();
var ft2 = f(ft);
ft == ft2 ? t : ft2;
}
}
/**
Calls function `f` on each component of type `t`.
If `t` is not a compound type, this operation has no effect.
The following types are considered compound:
- TInst, TEnum, TType and TAbstract with type parameters
- TFun
- TAnonymous
If `t` or `f` are null, the result is unspecified.
**/
static public function iter(t:Type, f:Type->Void):Void {
switch (t) {
case TMono(tm):
var t = tm.get();
if (t != null)
f(t);
case TEnum(_, tl) | TInst(_, tl) | TType(_, tl) | TAbstract(_, tl):
for (t in tl)
f(t);
case TDynamic(t2):
if (t != t2)
f(t2);
case TLazy(ft):
f(ft());
case TAnonymous(an):
for (field in an.get().fields)
f(field.type);
case TFun(args, ret):
for (arg in args)
f(arg.t);
f(ret);
}
}
/**
Converts type `t` to a human-readable String representation.
**/
static public function toString(t:Type):String {
#if (neko || eval)
return Context.load("s_type", 1)(t);
#else
return null;
#end
}
#end
/**
Resolves the field named `name` on class `c`.
If `isStatic` is true, the classes' static fields are checked. Otherwise
the classes' member fields are checked.
If the field is found, it is returned. Otherwise if `c` has a super
class, `findField` recursively checks that super class. Otherwise null
is returned.
If any argument is null, the result is unspecified.
**/
static public function findField(c:ClassType, name:String, isStatic:Bool = false):Null<ClassField> {
var field = (isStatic ? c.statics : c.fields).get().find(function(field) return field.name == name);
return if (field != null) field; else if (c.superClass != null) findField(c.superClass.t.get(), name, isStatic); else null;
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (C)2005-2019 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package haxe.macro;
import haxe.macro.Type;
/**
This class provides some utility methods to work with typed expressions.
It is best used through 'using haxe.macro.TypedExprTools' syntax and then
provides additional methods on `haxe.macro.TypedExpr` instances.
**/
class TypedExprTools {
static function with(e:TypedExpr, ?edef:TypedExprDef, ?t:Type) {
return {
expr: edef == null ? e.expr : edef,
pos: e.pos,
t: t == null ? e.t : t
}
}
/**
Transforms the sub-expressions of `e` by calling `f` on each of them.
See `haxe.macro.ExprTools.map` for details on expression mapping in
general. This function works the same way, but with a different data
structure.
**/
static public function map(e:TypedExpr, f:TypedExpr->TypedExpr):TypedExpr {
return switch (e.expr) {
case TConst(_) | TLocal(_) | TBreak | TContinue | TTypeExpr(_) | TIdent(_): e;
case TArray(e1, e2): with(e, TArray(f(e1), f(e2)));
case TBinop(op, e1, e2): with(e, TBinop(op, f(e1), f(e2)));
case TFor(v, e1, e2): with(e, TFor(v, f(e1), f(e2)));
case TWhile(e1, e2, flag): with(e, TWhile(f(e1), f(e2), flag));
case TThrow(e1): with(e, TThrow(f(e1)));
case TEnumParameter(e1, ef, i): with(e, TEnumParameter(f(e1), ef, i));
case TEnumIndex(e1): with(e, TEnumIndex(f(e1)));
case TField(e1, fa): with(e, TField(f(e1), fa));
case TParenthesis(e1): with(e, TParenthesis(f(e1)));
case TUnop(op, pre, e1): with(e, TUnop(op, pre, f(e1)));
case TArrayDecl(el): with(e, TArrayDecl(el.map(f)));
case TNew(t, pl, el): with(e, TNew(t, pl, el.map(f)));
case TBlock(el): with(e, TBlock(el.map(f)));
case TObjectDecl(fl): with(e, TObjectDecl(fl.map(function(field) return {name: field.name, expr: f(field.expr)})));
case TCall(e1, el): with(e, TCall(f(e1), el.map(f)));
case TVar(v, eo): with(e, TVar(v, eo == null ? null : f(eo)));
case TFunction(fu): with(e, TFunction({t: fu.t, args: fu.args, expr: f(fu.expr)}));
case TIf(e1, e2, e3): with(e, TIf(f(e1), f(e2), e3 == null ? null : f(e3)));
case TSwitch(e1, cases,
e2): with(e, TSwitch(f(e1), cases.map(function(c) return {values: c.values.map(f), expr: f(c.expr)}), e2 == null ? null : f(e2)));
case TTry(e1, catches): with(e, TTry(f(e1), catches.map(function(c) return {v: c.v, expr: f(c.expr)})));
case TReturn(e1): with(e, TReturn(e1 == null ? null : f(e1)));
case TCast(e1, mt): with(e, TCast(f(e1), mt));
case TMeta(m, e1): with(e, TMeta(m, f(e1)));
}
}
/**
Calls function `f` on each sub-expression of `e`.
See `haxe.macro.ExprTools.iter` for details on iterating expressions in
general. This function works the same way, but with a different data
structure.
**/
static public function iter(e:TypedExpr, f:TypedExpr->Void):Void {
switch (e.expr) {
case TConst(_) | TLocal(_) | TBreak | TContinue | TTypeExpr(_) | TIdent(_):
case TArray(e1, e2) | TBinop(_, e1, e2) | TFor(_, e1, e2) | TWhile(e1, e2, _):
f(e1);
f(e2);
case TThrow(e1) | TEnumParameter(e1, _, _) | TEnumIndex(e1) | TField(e1, _) | TParenthesis(e1) | TUnop(_, _, e1) | TCast(e1, _) | TMeta(_, e1):
f(e1);
case TArrayDecl(el) | TNew(_, _, el) | TBlock(el):
for (e in el)
f(e);
case TObjectDecl(fl):
for (field in fl)
f(field.expr);
case TCall(e1, el):
f(e1);
for (e in el)
f(e);
case TVar(_, e1) | TReturn(e1):
if (e1 != null)
f(e1);
case TFunction(fu):
f(fu.expr);
case TIf(e1, e2, e3):
f(e1);
f(e2);
if (e3 != null)
f(e3);
case TSwitch(e1, cases, e2):
f(e1);
for (c in cases) {
for (v in c.values)
f(v);
f(c.expr);
}
if (e2 != null)
f(e2);
case TTry(e1, catches):
f(e1);
for (c in catches)
f(c.expr);
}
}
/**
Transforms the sub-expressions of `e` by calling `f` on each of them.
Additionally, types are mapped using `ft` and variables are mapped using
`fv`.
See `haxe.macro.ExprTools.map` for details on expression mapping in
general. This function works the same way, but with a different data
structure.
**/
static public function mapWithType(e:TypedExpr, f:TypedExpr->TypedExpr, ft:Type->Type, fv:TVar->TVar):TypedExpr {
return switch (e.expr) {
case TConst(_) | TBreak | TContinue | TTypeExpr(_) | TIdent(_): with(e, ft(e.t));
case TLocal(v): with(e, TLocal(fv(v)), ft(e.t));
case TArray(e1, e2): with(e, TArray(f(e1), f(e2)), ft(e.t));
case TBinop(op, e1, e2): with(e, TBinop(op, f(e1), f(e2)), ft(e.t));
case TFor(v, e1, e2): with(e, TFor(fv(v), f(e1), f(e2)), ft(e.t));
case TWhile(e1, e2, flag): with(e, TWhile(f(e1), f(e2), flag), ft(e.t));
case TThrow(e1): with(e, TThrow(f(e1)), ft(e.t));
case TEnumParameter(e1, ef, i): with(e, TEnumParameter(f(e1), ef, i), ft(e.t));
case TEnumIndex(e1): with(e, TEnumIndex(f(e1)), ft(e.t));
case TField(e1, fa): with(e, TField(f(e1), fa), ft(e.t));
case TParenthesis(e1): with(e, TParenthesis(e1), ft(e.t));
case TUnop(op, pre, e1): with(e, TUnop(op, pre, f(e1)), ft(e.t));
case TArrayDecl(el): with(e, TArrayDecl(el.map(f)), ft(e.t));
case TNew(t, pl, el): with(e, TNew(t, pl, el.map(f)), ft(e.t));
case TBlock(el): with(e, TBlock(el.map(f)), ft(e.t));
case TObjectDecl(fl): with(e, TObjectDecl(fl.map(function(field) return {name: field.name, expr: f(field.expr)})), ft(e.t));
case TCall(e1, el): with(e, TCall(f(e1), el.map(f)), ft(e.t));
case TVar(v, eo): with(e, TVar(fv(v), eo == null ? null : f(eo)), ft(e.t));
case TFunction(fu): with(e, TFunction({t: ft(fu.t), args: fu.args.map(function(arg) return {v: fv(arg.v), value: arg.value}), expr: f(fu.expr)}),
ft(e.t));
case TIf(e1, e2, e3): with(e, TIf(f(e1), f(e2), e3 == null ? null : f(e3)), ft(e.t));
case TSwitch(e1, cases,
e2): with(e, TSwitch(f(e1), cases.map(function(c) return {values: c.values.map(f), expr: f(c.expr)}), e2 == null ? null : f(e2)), ft(e.t));
case TTry(e1, catches): with(e, TTry(f(e1), catches.map(function(c) return {v: fv(c.v), expr: f(c.expr)})), ft(e.t));
case TReturn(e1): with(e, TReturn(e1 == null ? null : f(e1)), ft(e.t));
case TCast(e1, mt): with(e, TCast(f(e1), mt), ft(e.t));
case TMeta(m, e1): with(e, TMeta(m, f(e1)), ft(e.t));
}
}
#if (macro || display)
static public function toString(t:TypedExpr, ?pretty = false):String {
return @:privateAccess haxe.macro.Context.sExpr(t, pretty);
}
#end
}