Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f2dec1634 | ||
|
|
51188173bb | ||
|
|
d0797ad5d8 | ||
|
|
9b37bd532e | ||
|
|
333352edcf | ||
|
|
05783773af | ||
|
|
59ee20ee7a | ||
|
|
87f2c1b062 | ||
|
|
b85cefea56 | ||
|
|
2e28f863f3 | ||
|
|
ddaa4d1741 | ||
|
|
732bd1520a | ||
|
|
1d67946f3f | ||
|
|
fab6d7e662 | ||
|
|
6e20578a81 | ||
|
|
dc5282c32e | ||
|
|
812abae6e5 | ||
|
|
b3b512d027 | ||
|
|
ed296c4c7f | ||
|
|
595bb47249 | ||
|
|
daad1a4fa8 | ||
|
|
daef1ac198 | ||
|
|
3b9d88b252 | ||
|
|
809f8229b4 | ||
|
|
ffb1ee2523 | ||
|
|
0903718314 | ||
|
|
e0cb883ae4 | ||
|
|
17449dd9d5 | ||
|
|
7963631a1d | ||
|
|
77d88f4b34 | ||
|
|
c5e3ab1162 | ||
|
|
75041c1db9 | ||
|
|
558a4c0782 | ||
|
|
995c3b9c8e | ||
|
|
0a64bca9ce | ||
|
|
b7b12b6cbb | ||
|
|
5684ca8671 | ||
|
|
3c81360dc2 | ||
|
|
fc50ebdf90 | ||
|
|
4d6e8db5ff | ||
|
|
938ffac57e | ||
|
|
50aaffb101 | ||
|
|
4f9b449c6e | ||
|
|
c1a36a3f4b | ||
|
|
4ca6e2a680 | ||
|
|
11db805c34 | ||
|
|
f1f700ab69 | ||
|
|
9eeb8ddb80 | ||
|
|
95de26a6d0 | ||
|
|
88c2e28dbc | ||
|
|
30b7a9e4d4 | ||
|
|
37542065d6 | ||
|
|
c9608f7760 | ||
|
|
6ba54b0e77 | ||
|
|
f9bb3105c8 | ||
|
|
4b5baa9b5f | ||
|
|
6dedd87260 | ||
|
|
91317222fb | ||
|
|
494953a8cc | ||
|
|
ae4d8be50c | ||
|
|
d34790e05c | ||
|
|
b77e15c6d6 | ||
|
|
54195d5811 | ||
|
|
9c7907f91a | ||
|
|
0601b8def6 | ||
|
|
841261f199 | ||
|
|
7124b6c4ce | ||
|
|
ad7beec559 | ||
|
|
bd4fc51392 | ||
|
|
54d994a879 | ||
|
|
891a085f2b | ||
|
|
e7ccd2e72d | ||
|
|
459a39e65d | ||
|
|
36eb0fca8b | ||
|
|
53b9e22f26 | ||
|
|
8ff3f5e351 | ||
|
|
e6a2c443cb | ||
|
|
4f25ab162d | ||
|
|
0520d9f187 | ||
|
|
cc3c21b7e3 | ||
|
|
ae9560124e | ||
|
|
085f4ec37e | ||
|
|
476c3a9cc9 | ||
|
|
1bc5ac8768 | ||
|
|
4bdff11b87 | ||
|
|
b32dde3c91 | ||
|
|
b9a566350b | ||
|
|
ce9ce674ea | ||
|
|
b8641779bd | ||
|
|
5e620d784e | ||
|
|
013bcfaffb | ||
|
|
2504c96927 | ||
|
|
e2f4d1481e | ||
|
|
ea3d68573b | ||
|
|
c6cb594ef1 | ||
|
|
948123bc7a | ||
|
|
68f5b26af9 | ||
|
|
5d493630b1 | ||
|
|
f4db6faa80 | ||
|
|
e340f7f5ad | ||
|
|
b4d923b74c | ||
|
|
965f334d59 | ||
|
|
54463b78d2 | ||
|
|
600aec1f50 | ||
|
|
bcfbe81979 | ||
|
|
31e8851bcc | ||
|
|
f464767ee7 | ||
|
|
22eb58b3de | ||
|
|
0daee20545 | ||
|
|
d98073dc33 | ||
|
|
fb199e7dea | ||
|
|
6cea0d97c2 | ||
|
|
058dd2e01e | ||
|
|
975d122491 | ||
|
|
eef4c66374 | ||
|
|
5ce4fbd74d | ||
|
|
f4d8cff26b | ||
|
|
46b6b3c151 | ||
|
|
796b217dec | ||
|
|
1ccc35f464 |
+11
-16
@@ -4,11 +4,11 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
sudo: false
|
||||
dist: trusty
|
||||
dist: bionic
|
||||
|
||||
haxe:
|
||||
- "3.4.7"
|
||||
- "4.0.3"
|
||||
- development
|
||||
|
||||
addons:
|
||||
@@ -19,27 +19,20 @@ addons:
|
||||
- g++-multilib
|
||||
# Python
|
||||
- python3
|
||||
# PHP
|
||||
- php-cli
|
||||
- php-mbstring
|
||||
# C#
|
||||
- mono-devel
|
||||
- mono-mcs
|
||||
# Flash
|
||||
- libglib2.0
|
||||
- libfreetype6
|
||||
|
||||
install:
|
||||
# os-specific config
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||
export DISPLAY=:99.0;
|
||||
export AUDIODEV=null;
|
||||
phpenv global 7.0;
|
||||
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||
brew update;
|
||||
brew install mono;
|
||||
brew upgrade python;
|
||||
brew install php;
|
||||
fi
|
||||
# Download and setup a standalone flash player debugger
|
||||
- haxe flash/install.hxml
|
||||
# Install haxelibs
|
||||
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ]; then
|
||||
haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git;
|
||||
@@ -67,8 +60,10 @@ script:
|
||||
- haxe bin/build-neko.hxml && neko bin/Test.n
|
||||
- haxe bin/build-js.hxml && node bin/Test.js
|
||||
- haxe bin/build-java.hxml && java -jar bin/Test.jar
|
||||
- haxe bin/build-cs.hxml && mono bin/bin/Test.exe
|
||||
# Only test C# for Haxe 4+
|
||||
- if [[ $(haxe -version 2&>1 | head -c 1) > 3 ]]; then
|
||||
haxe bin/build-cs.hxml && mono bin/bin/Test.exe;
|
||||
fi
|
||||
- haxe bin/build-cpp.hxml && ./bin/Test
|
||||
- haxe bin/build-flash.hxml -D fdb && haxe flash/run.hxml bin/Test.swf
|
||||
- haxe bin/build-php.hxml && php bin/index.php
|
||||
- haxe bin/build-php.hxml -D php7 && php bin/index.php
|
||||
- haxe bin/build-python.hxml && python3 bin/Test.py
|
||||
|
||||
@@ -68,24 +68,36 @@ You can use `parser.allowMetadata` to parse metadata before expressions on in an
|
||||
|
||||
You can use `new hscript.Macro(pos).convert(ast)` to convert an hscript AST to a Haxe macros one.
|
||||
|
||||
You can use `hscript.Checker` in order to type check and even get completion, using `haxe -xml` output for type information.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
Compared to Haxe, limitations are :
|
||||
|
||||
- no type declarations (classes, enums, typedefs) : only expressions
|
||||
- `switch` construct is supported but not pattern matching (no variable capture, we use strict equality to compare `case` values and `switch` value)
|
||||
- only one variable declaration is allowed in `var`
|
||||
- the parser supports optional types for `var` and `function` if `allowTypes` is set, but the interpreter ignores them
|
||||
- you can enable per-expression position tracking by compiling with `-D hscriptPos`
|
||||
- you can parse some type declarations (import, class, typedef, etc.) with parseModule
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
In order to install Haxe Script, use `haxelib install hscript` and compile your program with `-lib hscript`.
|
||||
|
||||
There are only three files in hscript :
|
||||
These are the main required files in hscript :
|
||||
|
||||
- `hscript.Expr` : contains enums declarations
|
||||
- `hscript.Parser` : a small parser that turns a string into an expression structure (AST)
|
||||
- `hscript.Interp` : a small interpreter that execute the AST and returns the latest evaluated value
|
||||
|
||||
Some other optional files :
|
||||
|
||||
- `hscript.Async` : converts Expr into asynchronous version
|
||||
- `hscript.Bytes` : Expr serializer/unserializer
|
||||
- `hscript.Checker` : type checking and completion for hscript Expr
|
||||
- `hscript.Macro` : convert Haxe macro into hscript Expr
|
||||
- `hscript.Printer` : convert hscript Expr to String
|
||||
- `hscript.Tools` : utility functions (map/iter)
|
||||
|
||||
@@ -4,9 +4,10 @@ import hscript.Macro;
|
||||
import hscript.Tools;
|
||||
import hscript.Async;
|
||||
import hscript.Printer;
|
||||
import hscript.Checker;
|
||||
import haxe.unit.*;
|
||||
|
||||
class Test extends TestCase {
|
||||
class TestHScript extends TestCase {
|
||||
function assertScript(x,v:Dynamic,?vars : Dynamic, allowTypes=false, ?pos:haxe.PosInfos) {
|
||||
var p = new hscript.Parser();
|
||||
p.allowTypes = allowTypes;
|
||||
@@ -100,6 +101,7 @@ class Test extends TestCase {
|
||||
assertScript("/**/0", 0);
|
||||
assertScript("x=1;x*=-2", -2);
|
||||
assertScript("var f = x -> x + 1; f(3)", 4);
|
||||
assertScript("var f = () -> 55; f()", 55);
|
||||
assertScript("var f = (x) -> x + 1; f(3)", 4);
|
||||
assertScript("var f = (x:Int) -> x + 1; f(3)", 4);
|
||||
assertScript("var f = (x,y) -> x + y; f(3,1)", 4);
|
||||
@@ -109,6 +111,8 @@ class Test extends TestCase {
|
||||
assertScript("var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
|
||||
assertScript("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
|
||||
assertScript("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
|
||||
assertScript("var a = 10; var b = 5; a - -b", 15);
|
||||
assertScript("var a = 10; var b = 5; a - b / 2", 7.5);
|
||||
}
|
||||
|
||||
function testMap():Void {
|
||||
@@ -169,7 +173,7 @@ class Test extends TestCase {
|
||||
#end
|
||||
|
||||
var runner = new TestRunner();
|
||||
runner.add(new Test());
|
||||
runner.add(new TestHScript());
|
||||
var succeed = runner.run();
|
||||
|
||||
#if sys
|
||||
+1
-9
@@ -8,17 +8,11 @@ install:
|
||||
# http://help.appveyor.com/discussions/problems/5616-not-able-to-build-due-to-problem-in-chocolateyinstallps1
|
||||
- ps: Set-Service wuauserv -StartupType Manual
|
||||
# Install the haxe chocolatey package (https://chocolatey.org/packages/haxe)
|
||||
- cinst haxe -version 3.4.7 -y
|
||||
- cinst haxe -version 4.0.3 -y
|
||||
- RefreshEnv
|
||||
# Setup haxelib
|
||||
- mkdir "%HAXELIB_ROOT%"
|
||||
- haxelib setup "%HAXELIB_ROOT%"
|
||||
# Install test dependencies
|
||||
- ps: Set-Service wuauserv -StartupType Manual
|
||||
- cinst php -y
|
||||
- RefreshEnv
|
||||
# Download and setup a standalone flash player debugger
|
||||
- haxe flash/install.hxml
|
||||
# Install project dependencies
|
||||
- haxelib install hxcpp > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hxjava > log.txt || type log.txt && cmd /C exit 1
|
||||
@@ -39,6 +33,4 @@ test_script:
|
||||
- haxe bin/build-java.hxml && java -jar bin\Test.jar
|
||||
- haxe bin/build-cs.hxml && bin\bin\Test.exe
|
||||
- haxe bin/build-cpp.hxml && bin\Test.exe
|
||||
- haxe bin/build-flash.hxml -D fdb && haxe flash\run.hxml bin\Test.swf
|
||||
- haxe bin/build-php.hxml && php bin\index.php
|
||||
- haxe bin/build-python.hxml && C:\Python34-x64\python.exe bin\Test.py
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
-main Test
|
||||
-main TestHScript
|
||||
-dce no
|
||||
-lib hx3compat
|
||||
@@ -1,5 +0,0 @@
|
||||
*.exe
|
||||
*.app
|
||||
flashplayerdebugger
|
||||
LGPL/
|
||||
license.pdf
|
||||
@@ -1,91 +0,0 @@
|
||||
import Sys.*;
|
||||
import sys.FileSystem.*;
|
||||
import sys.io.File.*;
|
||||
import haxe.*;
|
||||
import haxe.io.*;
|
||||
|
||||
class Install {
|
||||
// https://www.adobe.com/support/flashplayer/downloads.html
|
||||
static var fpDownload(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flash_player_sa_linux_debug.x86_64.tar.gz";
|
||||
case "Mac":
|
||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.dmg";
|
||||
case "Windows":
|
||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.exe";
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
||||
static var mmcfg(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), "mm.cfg"]);
|
||||
case "Mac":
|
||||
"/Library/Application Support/Macromedia/mm.cfg";
|
||||
case "Windows":
|
||||
Path.join([getEnv("HOMEDRIVE") + getEnv("HOMEPATH"), "mm.cfg"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
// http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7c95.html
|
||||
static var fpTrust(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/#Security/FlashPlayerTrust"]);
|
||||
case "Mac":
|
||||
"/Library/Application Support/Macromedia/FlashPlayerTrust";
|
||||
case "Windows":
|
||||
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "#Security", "FlashPlayerTrust"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
static function main() {
|
||||
switch (systemName()) {
|
||||
case "Linux":
|
||||
// Download and unzip flash player
|
||||
if (command("wget", [fpDownload]) != 0)
|
||||
throw "failed to download flash player";
|
||||
if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0)
|
||||
throw "failed to extract flash player";
|
||||
deleteFile(Path.withoutDirectory(fpDownload));
|
||||
case "Mac":
|
||||
if (command("brew", ["cask", "install", "flash-player-debugger"]) != 0)
|
||||
throw "failed to install flash-player-debugger";
|
||||
case "Windows":
|
||||
// Download flash player
|
||||
download(fpDownload, "flash\\flashplayer.exe");
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
|
||||
|
||||
// Create a configuration file so the trace log is enabled
|
||||
createDirectory(Path.directory(mmcfg));
|
||||
saveContent(mmcfg, "ErrorReportingEnable=1\nTraceOutputFileEnable=1");
|
||||
|
||||
// Add the current directory as trusted, so exit() can be used
|
||||
createDirectory(fpTrust);
|
||||
saveContent(Path.join([fpTrust, "test.cfg"]), getCwd());
|
||||
}
|
||||
static function download(url:String, saveAs:String):Void {
|
||||
var http = new Http(url);
|
||||
http.onError = function(e) {
|
||||
throw e;
|
||||
};
|
||||
http.customRequest(false, write(saveAs));
|
||||
}
|
||||
static function createDirectory(dir:String):Void {
|
||||
try {
|
||||
sys.FileSystem.createDirectory(dir);
|
||||
} catch(e:Dynamic) {
|
||||
switch (systemName()) {
|
||||
case "Mac", "Linux":
|
||||
if (command("sudo", ["mkdir", "-p", dir]) != 0)
|
||||
throw 'cannot create $dir';
|
||||
if (command("sudo", ["chmod", "a+rw", dir]) != 0)
|
||||
throw 'cannot set permission of $dir';
|
||||
case _:
|
||||
throw 'cannot create $dir: $e';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import Sys.*;
|
||||
import sys.FileSystem.*;
|
||||
import sys.io.File.*;
|
||||
import haxe.io.*;
|
||||
|
||||
class Run {
|
||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
||||
static var flashlog(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/Logs/flashlog.txt"]);
|
||||
case "Mac":
|
||||
Path.join([getEnv("HOME"), "Library/Preferences/Macromedia/Flash Player/Logs/flashlog.txt"]);
|
||||
case "Windows":
|
||||
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "Logs", "flashlog.txt"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
static function main() {
|
||||
var args = args();
|
||||
var swf = args[0];
|
||||
var exitCode = switch (systemName()) {
|
||||
case "Linux":
|
||||
// The flash player has some issues with unexplained crashes,
|
||||
// but if it runs about 16 times, it should succeed one of those...
|
||||
var c = -1;
|
||||
for (i in 0...16) {
|
||||
if ((c = command("xvfb-run", ["-a", "flash/flashplayerdebugger", swf])) == 0)
|
||||
break;
|
||||
println('retry... (${i+1})');
|
||||
sleep(1.5);
|
||||
}
|
||||
c;
|
||||
case "Mac":
|
||||
command("/Applications/Flash Player Debugger.app/Contents/MacOS/Flash Player Debugger", [fullPath(swf)]);
|
||||
case "Windows":
|
||||
command("flash\\flashplayer.exe", [fullPath(swf)]);
|
||||
case _:
|
||||
throw "unsupported platform";
|
||||
}
|
||||
if (exists(flashlog))
|
||||
println(getContent(flashlog));
|
||||
else {
|
||||
println('does not exist: $flashlog');
|
||||
var parts = Path.normalize(flashlog).split("/");
|
||||
println(parts);
|
||||
for (i in 0...parts.length-1) {
|
||||
var path = parts.splice(0, i+1).join("/");
|
||||
println('ls $path');
|
||||
command("ls", [path]);
|
||||
}
|
||||
}
|
||||
exit(exitCode);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
-cp flash
|
||||
--run Install
|
||||
@@ -1,2 +0,0 @@
|
||||
-cp flash
|
||||
--run Run
|
||||
+9
-9
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "hscript",
|
||||
"url": "https://github.com/HaxeFoundation/hscript",
|
||||
"license": "MIT",
|
||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||
"version": "2.3.0",
|
||||
"releasenote": "",
|
||||
"contributors": ["ncannasse"]
|
||||
}
|
||||
{
|
||||
"name": "hscript",
|
||||
"url": "https://github.com/HaxeFoundation/hscript",
|
||||
"license": "MIT",
|
||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||
"version": "2.4.0",
|
||||
"releasenote": "Haxe 4.0 final + type checking/completion (hscript.Checker)",
|
||||
"contributors": ["ncannasse","HaxeFoundation"]
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project version="2">
|
||||
<!-- Output SWF options -->
|
||||
<output>
|
||||
<movie outputType="Application" />
|
||||
<movie input="" />
|
||||
<movie path="hscript.swf" />
|
||||
<movie fps="30" />
|
||||
<movie width="800" />
|
||||
<movie height="600" />
|
||||
<movie version="9" />
|
||||
<movie minorVersion="0" />
|
||||
<movie platform="Flash Player" />
|
||||
<movie background="#FFFFFF" />
|
||||
</output>
|
||||
<!-- Other classes to be compiled into your SWF -->
|
||||
<classpaths>
|
||||
<!-- example: <class path="..." /> -->
|
||||
</classpaths>
|
||||
<!-- Build options -->
|
||||
<build>
|
||||
<option directives="" />
|
||||
<option flashStrict="True" />
|
||||
<option noInlineOnDebug="False" />
|
||||
<option mainClass="Test" />
|
||||
<option enabledebug="False" />
|
||||
<option additional="-dce no" />
|
||||
</build>
|
||||
<!-- haxelib libraries -->
|
||||
<haxelib>
|
||||
<!-- example: <library name="..." /> -->
|
||||
</haxelib>
|
||||
<!-- Class files to compile (other referenced classes will automatically be included) -->
|
||||
<compileTargets>
|
||||
<!-- example: <compile path="..." /> -->
|
||||
</compileTargets>
|
||||
<!-- Assets to embed into the output SWF -->
|
||||
<library>
|
||||
<!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
|
||||
</library>
|
||||
<!-- Paths to exclude from the Project Explorer tree -->
|
||||
<hiddenPaths>
|
||||
<hidden path="haxelib.xml" />
|
||||
<hidden path="hscript.hxml" />
|
||||
<hidden path="hscript.js" />
|
||||
<hidden path="hscript.n" />
|
||||
<hidden path="hscript.swf" />
|
||||
<hidden path="hscript9.swf" />
|
||||
<hidden path="release.bat" />
|
||||
<hidden path="release.zip" />
|
||||
<hidden path="obj" />
|
||||
</hiddenPaths>
|
||||
<!-- Executed before build -->
|
||||
<preBuildCommand />
|
||||
<!-- Executed after build -->
|
||||
<postBuildCommand alwaysRun="False" />
|
||||
<!-- Other project options -->
|
||||
<options>
|
||||
<option showHiddenPaths="False" />
|
||||
<option testMovie="Default" />
|
||||
<option testMovieCommand="" />
|
||||
</options>
|
||||
<!-- Plugin storage -->
|
||||
<storage />
|
||||
</project>
|
||||
+53
-21
@@ -22,13 +22,21 @@
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
|
||||
|
||||
enum VarMode {
|
||||
Defined;
|
||||
ForceSync;
|
||||
}
|
||||
|
||||
class Async {
|
||||
|
||||
var varNames : Array<String>;
|
||||
var definedVars : Array<{ n : String, prev : Null<VarMode> }>;
|
||||
var vars : Map<String,VarMode>;
|
||||
var currentFun : String;
|
||||
var currentLoop : Expr;
|
||||
var currentBreak : Expr -> Expr;
|
||||
var uid = 0;
|
||||
public var asyncIdents : Map<String,Bool>;
|
||||
|
||||
static var nullExpr : Expr = #if hscriptPos { e : null, pmin : 0, pmax : 0, origin : "<null>", line : 0 } #else null #end;
|
||||
static var nullId = mk(EIdent("null"), nullExpr);
|
||||
@@ -68,15 +76,33 @@ class Async {
|
||||
return a.build(e, topLevelSync);
|
||||
}
|
||||
|
||||
public dynamic function getTopLevelEnd() {
|
||||
return ignore();
|
||||
}
|
||||
|
||||
public function build( e : Expr, topLevelSync = false ) {
|
||||
if( topLevelSync ) {
|
||||
return buildSync(e,null);
|
||||
} else {
|
||||
var nothing = ignore();
|
||||
return new Async().toCps(e, nothing, nothing);
|
||||
var end = getTopLevelEnd();
|
||||
return toCps(e, end, end);
|
||||
}
|
||||
}
|
||||
|
||||
function defineVar( v : String, mode ) {
|
||||
definedVars.push({ n : v, prev : vars.get(v) });
|
||||
vars.set(v, mode);
|
||||
}
|
||||
|
||||
function lookupFunctions( el : Array<Expr> ) {
|
||||
for( e in el )
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_, _, name, _) if( name != null ): defineVar(name, Defined);
|
||||
case EMeta("sync",_,expr(_) => EFunction(_,_,name,_)) if( name != null ): defineVar(name, ForceSync);
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function buildSync( e : Expr, exit : Expr ) : Expr {
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_,_,name,_):
|
||||
@@ -85,16 +111,14 @@ class Async {
|
||||
return e;
|
||||
case EBlock(el):
|
||||
var v = saveVars();
|
||||
for( e in el )
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_, _, name, _) if( name != null ): varNames.push(name);
|
||||
default:
|
||||
}
|
||||
lookupFunctions(el);
|
||||
var e = block([for(e in el) buildSync(e,exit)], e);
|
||||
restoreVars(v);
|
||||
return e;
|
||||
case EMeta("async", _, e):
|
||||
return toCps(e, ignore(), ignore());
|
||||
case EMeta("sync", args, ef = expr(_) => EFunction(fargs, body, name, ret)):
|
||||
return mk(EMeta("sync",args,mk(EFunction(fargs, buildSync(body,null), name, ret),ef)),e);
|
||||
case EBreak if( currentBreak != null ):
|
||||
return currentBreak(e);
|
||||
case EContinue if( currentLoop != null ):
|
||||
@@ -115,7 +139,8 @@ class Async {
|
||||
}
|
||||
|
||||
public function new() {
|
||||
varNames = [];
|
||||
vars = new Map();
|
||||
definedVars = [];
|
||||
}
|
||||
|
||||
function ignore(?e) : Expr {
|
||||
@@ -183,11 +208,17 @@ class Async {
|
||||
return syncFlag;
|
||||
}
|
||||
|
||||
inline function isAsyncIdent( id : String ) {
|
||||
return asyncIdents == null || asyncIdents.exists(id);
|
||||
}
|
||||
|
||||
function checkSync( e : Expr ) {
|
||||
if( !syncFlag )
|
||||
return;
|
||||
switch( expr(e) ) {
|
||||
case ECall(_):
|
||||
case ECall(expr(_) => EIdent(i),_) if( isAsyncIdent(i) || vars.get(i) == Defined ):
|
||||
syncFlag = false;
|
||||
case ECall(expr(_) => EField(_,i),_) if( isAsyncIdent(i) ):
|
||||
syncFlag = false;
|
||||
case EFunction(_,_,name,_) if( name != null ):
|
||||
syncFlag = false;
|
||||
@@ -199,11 +230,14 @@ class Async {
|
||||
}
|
||||
|
||||
function saveVars() {
|
||||
return varNames.length;
|
||||
return definedVars.length;
|
||||
}
|
||||
|
||||
function restoreVars(k) {
|
||||
while( varNames.length > k ) varNames.pop();
|
||||
while( definedVars.length > k ) {
|
||||
var v = definedVars.pop();
|
||||
if( v.prev == null ) vars.remove(v.n) else vars.set(v.n, v.prev);
|
||||
}
|
||||
}
|
||||
|
||||
public function toCps( e : Expr, rest : Expr, exit : Expr ) : Expr {
|
||||
@@ -213,12 +247,7 @@ class Async {
|
||||
case EBlock(el):
|
||||
var el = el.copy();
|
||||
var vold = saveVars();
|
||||
// local recursion
|
||||
for( e in el )
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_, _, name, null): if( name != null ) varNames.push(name);
|
||||
default:
|
||||
}
|
||||
lookupFunctions(el);
|
||||
while( el.length > 0 ) {
|
||||
var e = toCps(el.pop(), rest, exit);
|
||||
rest = ignore(e);
|
||||
@@ -228,9 +257,9 @@ class Async {
|
||||
case EFunction(args, body, name, t):
|
||||
var vold = saveVars();
|
||||
if( name != null )
|
||||
varNames.push(name);
|
||||
defineVar(name, Defined);
|
||||
for( a in args )
|
||||
varNames.push(a.name);
|
||||
defineVar(a.name, Defined);
|
||||
args.unshift( { name : "_onEnd", t : null } );
|
||||
var frest = ident("_onEnd",e);
|
||||
var oldFun = currentFun;
|
||||
@@ -251,7 +280,8 @@ class Async {
|
||||
var args = [for( a in args ) fun("_rest", toCps(block([a],a), ident("_rest",a), exit))];
|
||||
return call(ident("split",e), [rest, mk(EArrayDecl(args),e)],e);
|
||||
case ECall(expr(_) => EIdent(i), args):
|
||||
return makeCall( ident( varNames.indexOf(i) < 0 ? "a_" + i : i,e) , args, rest, exit);
|
||||
var mode = vars.get(i);
|
||||
return makeCall( ident( mode != null ? i : "a_" + i,e) , args, rest, exit, mode == ForceSync);
|
||||
case ECall(expr(_) => EField(e, f), args):
|
||||
return makeCall(field(e,"a_"+f,e), args, rest, exit);
|
||||
case EFor(v, eit, eloop):
|
||||
@@ -391,6 +421,8 @@ class Async {
|
||||
return toCps(v, mk(EFunction([ { name : "_c", t : null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
|
||||
case EThrow(v):
|
||||
return toCps(v, mk(EFunction([ { name : "_v", t : null } ], mk(EThrow(v),v)), v), exit);
|
||||
case EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata
|
||||
return toCps(e, rest, exit);
|
||||
//case EDoWhile(_), ETry(_), ECall(_):
|
||||
default:
|
||||
throw "Unsupported async expression " + Printer.toString(e);
|
||||
|
||||
@@ -140,6 +140,10 @@ class Bytes {
|
||||
#end
|
||||
bout.addByte(Type.enumIndex(e));
|
||||
switch( e ) {
|
||||
case EImport(c):
|
||||
// TODO
|
||||
case EClass(_, _, _, _):
|
||||
// TODO
|
||||
case EConst(c):
|
||||
doEncodeConst(c);
|
||||
case EIdent(v):
|
||||
|
||||
+367
-66
@@ -23,6 +23,7 @@ enum TType {
|
||||
TAbstract( a : CAbstract, args : Array<TType> );
|
||||
TFun( args : Array<{ name : String, opt : Bool, t : TType }>, ret : TType );
|
||||
TAnon( fields : Array<{ name : String, opt : Bool, t : TType }> );
|
||||
TLazy( f : Void -> TType );
|
||||
}
|
||||
|
||||
private enum WithType {
|
||||
@@ -47,19 +48,23 @@ typedef CNamedType = {
|
||||
typedef CClass = {> CNamedType,
|
||||
@:optional var superClass : TType;
|
||||
@:optional var constructor : CField;
|
||||
@:optional var interfaces : Array<TType>;
|
||||
@:optional var isInterface : Bool;
|
||||
var fields : Map<String,CField>;
|
||||
var statics : Map<String,CField>;
|
||||
}
|
||||
|
||||
typedef CField = {
|
||||
var isPublic : Bool;
|
||||
var canWrite : Bool;
|
||||
var complete : Bool;
|
||||
var params : Array<TType>;
|
||||
var name : String;
|
||||
var t : TType;
|
||||
}
|
||||
|
||||
typedef CEnum = {> CNamedType,
|
||||
var constructors : Map<String,TType>;
|
||||
var constructors : Array<{ name : String, ?args : Array<{ name : String, opt : Bool, t : TType }> }>;
|
||||
}
|
||||
|
||||
typedef CTypedef = {> CNamedType,
|
||||
@@ -67,6 +72,16 @@ typedef CTypedef = {> CNamedType,
|
||||
}
|
||||
|
||||
typedef CAbstract = {> CNamedType,
|
||||
var t : TType;
|
||||
}
|
||||
|
||||
class Completion {
|
||||
public var expr : Expr;
|
||||
public var t : TType;
|
||||
public function new(expr,t) {
|
||||
this.expr = expr;
|
||||
this.t = t;
|
||||
}
|
||||
}
|
||||
|
||||
@:allow(hscript.Checker)
|
||||
@@ -108,23 +123,34 @@ class CheckerTypes {
|
||||
fields : new Map(),
|
||||
statics : new Map(),
|
||||
};
|
||||
if( c.isInterface )
|
||||
cl.isInterface = true;
|
||||
for( p in c.params )
|
||||
cl.params.push(TParam(p));
|
||||
todo.push(function() {
|
||||
localParams = [for( t in cl.params ) c.path+"."+Checker.typeStr(t) => t];
|
||||
if( c.superClass != null )
|
||||
cl.superClass = getType(c.superClass.path, [for( t in c.superClass.params ) makeXmlType(t)]);
|
||||
if( c.interfaces != null ) {
|
||||
cl.interfaces = [];
|
||||
for( i in c.interfaces )
|
||||
cl.interfaces.push(getType(i.path, [for( t in i.params ) makeXmlType(t)]));
|
||||
}
|
||||
var pkeys = [];
|
||||
for( f in c.fields ) {
|
||||
if( f.isOverride || f.name.substr(0,4) == "get_" || f.name.substr(0,4) == "set_" ) continue;
|
||||
var skip = false;
|
||||
for( m in f.meta )
|
||||
if( m.name == ":noScript" ) {
|
||||
var complete = !StringTools.startsWith(f.name,"__"); // __uid, etc. (no metadata in such fields)
|
||||
for( m in f.meta ) {
|
||||
if( m.name == ":noScript" ) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if( m.name == ":noCompletion" )
|
||||
complete = false;
|
||||
}
|
||||
if( skip ) continue;
|
||||
var fl : CField = { isPublic : f.isPublic, params : [], name : f.name, t : null };
|
||||
var fl : CField = { isPublic : f.isPublic, canWrite : f.set.match(RNormal | RCall(_) | RDynamic), complete : complete, params : [], name : f.name, t : null };
|
||||
for( p in f.params ) {
|
||||
var pt = TParam(p);
|
||||
var key = f.name+"."+p;
|
||||
@@ -148,12 +174,14 @@ class CheckerTypes {
|
||||
var en : CEnum = {
|
||||
name : e.path,
|
||||
params : [],
|
||||
constructors: new Map(),
|
||||
constructors: [],
|
||||
};
|
||||
for( p in e.params )
|
||||
en.params.push(TParam(p));
|
||||
todo.push(function() {
|
||||
localParams = [for( t in en.params ) e.path+"."+Checker.typeStr(t) => t];
|
||||
for( c in e.constructors )
|
||||
en.constructors.push({ name : c.name, args : c.args == null ? null : [for( a in c.args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }] });
|
||||
localParams = null;
|
||||
});
|
||||
types.set(en.name, CTEnum(en));
|
||||
@@ -179,11 +207,13 @@ class CheckerTypes {
|
||||
var ta : CAbstract = {
|
||||
name : a.path,
|
||||
params : [],
|
||||
t : null,
|
||||
};
|
||||
for( p in a.params )
|
||||
ta.params.push(TParam(p));
|
||||
todo.push(function() {
|
||||
localParams = [for( t in ta.params ) a.path+"."+Checker.typeStr(t) => t];
|
||||
ta.t = makeXmlType(a.athis);
|
||||
localParams = null;
|
||||
});
|
||||
types.set(a.path, CTAbstract(ta));
|
||||
@@ -220,8 +250,18 @@ class CheckerTypes {
|
||||
if( t != null ) return t;
|
||||
}
|
||||
var t = resolve(name,args);
|
||||
if( t == null )
|
||||
if( t == null ) {
|
||||
var pack = name.split(".");
|
||||
if( pack.length > 1 ) {
|
||||
// bugfix for some args reported as pack._Name.Name while they are not private
|
||||
var priv = pack[pack.length-2];
|
||||
if( priv.charCodeAt(0) == "_".code ) {
|
||||
pack.remove(priv);
|
||||
return getType(pack.join("."), args);
|
||||
}
|
||||
}
|
||||
return TUnresolved(name); // most likely private class
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -250,14 +290,37 @@ class Checker {
|
||||
var locals : Map<String,TType>;
|
||||
var globals : Map<String,TType> = new Map();
|
||||
var events : Map<String,TType> = new Map();
|
||||
var currentFunType : TType;
|
||||
var isCompletion : Bool;
|
||||
var allowDefine : Bool;
|
||||
public var allowAsync : Bool;
|
||||
public var allowReturn : Null<TType>;
|
||||
public var allowGlobalsDefine : Bool;
|
||||
public var allowUntypedMeta : Bool;
|
||||
|
||||
public function new( ?types ) {
|
||||
if( types == null ) types = new CheckerTypes();
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
public function setGlobals( cl : CClass ) {
|
||||
while( true ) {
|
||||
for( f in cl.fields )
|
||||
if( f.isPublic )
|
||||
setGlobal(f.name, f.params.length == 0 ? f.t : TLazy(function() return apply(f.t,f.params,[for( i in 0...f.params.length) makeMono()])));
|
||||
if( cl.superClass == null )
|
||||
break;
|
||||
cl = switch( cl.superClass ) {
|
||||
case TInst(c,_): c;
|
||||
default: throw "assert";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function removeGlobal( name : String ) {
|
||||
globals.remove(name);
|
||||
}
|
||||
|
||||
public function setGlobal( name : String, type : TType ) {
|
||||
globals.set(name, type);
|
||||
}
|
||||
@@ -270,9 +333,54 @@ class Checker {
|
||||
return globals;
|
||||
}
|
||||
|
||||
public function check( expr : Expr, ?withType : WithType ) {
|
||||
function typeArgs( args : Array<Argument>, pos : Expr ) {
|
||||
return [for( i in 0...args.length ) {
|
||||
var a = args[i];
|
||||
var at = a.t == null ? makeMono() : makeType(a.t, pos);
|
||||
{ name : a.name, opt : a.opt, t : at };
|
||||
}];
|
||||
}
|
||||
|
||||
public function check( expr : Expr, ?withType : WithType, ?isCompletion = false ) {
|
||||
if( withType == null ) withType = NoValue;
|
||||
locals = new Map();
|
||||
allowDefine = allowGlobalsDefine;
|
||||
this.isCompletion = isCompletion;
|
||||
switch( edef(expr) ) {
|
||||
case EBlock(el):
|
||||
var delayed = [];
|
||||
var last = TVoid;
|
||||
for( e in el ) {
|
||||
while( true ) {
|
||||
switch( edef(e) ) {
|
||||
case EMeta(_,_,e2): e = e2;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
switch( edef(e) ) {
|
||||
case EFunction(args,_,name,ret) if( name != null ):
|
||||
var tret = ret == null ? makeMono() : makeType(ret, e);
|
||||
var ft = TFun(typeArgs(args,e),tret);
|
||||
locals.set(name, ft);
|
||||
delayed.push(function() {
|
||||
currentFunType = ft;
|
||||
typeExpr(e, NoValue);
|
||||
return ft;
|
||||
});
|
||||
default:
|
||||
for( f in delayed ) f();
|
||||
delayed = [];
|
||||
if( el[el.length-1] == e )
|
||||
last = typeExpr(e, withType);
|
||||
else
|
||||
typeExpr(e, NoValue);
|
||||
}
|
||||
}
|
||||
for( f in delayed )
|
||||
last = f();
|
||||
return last;
|
||||
default:
|
||||
}
|
||||
return typeExpr(expr,withType);
|
||||
}
|
||||
|
||||
@@ -287,7 +395,7 @@ class Checker {
|
||||
inline function error( msg : String, curExpr : Expr ) {
|
||||
var e = ECustom(msg);
|
||||
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
|
||||
throw e;
|
||||
if( !isCompletion ) throw e;
|
||||
}
|
||||
|
||||
function saveLocals() {
|
||||
@@ -297,8 +405,11 @@ class Checker {
|
||||
function makeType( t : CType, e : Expr ) : TType {
|
||||
return switch (t) {
|
||||
case CTPath(path, params):
|
||||
var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(t,e)]);
|
||||
if( ct == null ) error("Unknown type "+path, e);
|
||||
var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(p,e)]);
|
||||
if( ct == null ) {
|
||||
error("Unknown type "+path, e);
|
||||
ct = TDynamic;
|
||||
}
|
||||
return ct;
|
||||
case CTFun(args, ret):
|
||||
var i = 0;
|
||||
@@ -307,6 +418,10 @@ class Checker {
|
||||
return TAnon([for( f in fields ) { name : f.name, opt : false, t : makeType(f.t,e) }]);
|
||||
case CTParent(t):
|
||||
return makeType(t,e);
|
||||
case CTNamed(n, t):
|
||||
return makeType(t,e);
|
||||
case CTOpt(t):
|
||||
return makeType(t,e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +431,11 @@ class Checker {
|
||||
case TMono(r): r.r == null ? "Unknown" : typeStr(r.r);
|
||||
case TInst(c, args): c.name + makeArgs(args);
|
||||
case TEnum(e, args): e.name + makeArgs(args);
|
||||
case TType(t, args): t.name + makeArgs(args);
|
||||
case TType(t, args):
|
||||
if( t.name == "hscript.TypeCheck" )
|
||||
typeStr(args[1]);
|
||||
else
|
||||
t.name + makeArgs(args);
|
||||
case TAbstract(a, args): a.name + makeArgs(args);
|
||||
case TFun(args, ret): "(" + [for( a in args ) (a.opt?"?":"")+(a.name == "" ? "" : a.name+":")+typeStr(a.t)].join(", ")+") -> "+typeStr(ret);
|
||||
case TAnon(fields): "{" + [for( f in fields ) (f.opt?"?":"")+f.name+":"+typeStr(f.t)].join(", ")+"}";
|
||||
@@ -478,6 +597,8 @@ class Checker {
|
||||
case [TDynamic, _]:
|
||||
return true;
|
||||
case [TAnon(a1),TAnon(a2)]:
|
||||
if( a2.length == 0 ) // always unify with {}
|
||||
return true;
|
||||
var m = new Map();
|
||||
for( f in a1 )
|
||||
m.set(f.name, f);
|
||||
@@ -493,6 +614,18 @@ class Checker {
|
||||
return true;
|
||||
case [TInst(cl1,pl1), TInst(cl2,pl2)]:
|
||||
while( cl1 != cl2 ) {
|
||||
if( cl1.interfaces != null ) {
|
||||
for( i in cl1.interfaces ) {
|
||||
switch( i ) {
|
||||
case TInst(cli, args):
|
||||
var i = TInst(cli, [for( a in args ) apply(a, cl1.params, pl1)]);
|
||||
if( tryUnify(i, t2) )
|
||||
return true;
|
||||
default:
|
||||
throw "assert";
|
||||
}
|
||||
}
|
||||
}
|
||||
switch( cl1.superClass ) {
|
||||
case null: return false;
|
||||
case TInst(c, args):
|
||||
@@ -505,6 +638,25 @@ class Checker {
|
||||
if( !typeEq(pl1[i],pl2[i]) )
|
||||
return false;
|
||||
return true;
|
||||
case [TInst(cl1,pl1),TAnon(fl)]:
|
||||
for( i in 0...fl.length ) {
|
||||
var f2 = fl[i];
|
||||
var f1 = null;
|
||||
var cl = cl1;
|
||||
while( true ) {
|
||||
f1 = cl.fields.get(f2.name);
|
||||
if( f1 != null ) break;
|
||||
if( cl.superClass == null )
|
||||
return false;
|
||||
cl = switch( cl.superClass ) {
|
||||
case TInst(c,_): c;
|
||||
default: throw "assert";
|
||||
}
|
||||
}
|
||||
if( !typeEq(f1.t,f2.t) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case [TInt, TFloat]:
|
||||
return true;
|
||||
case [TFun(_), TAbstract({ name : "haxe.Function" },_)]:
|
||||
@@ -514,12 +666,12 @@ class Checker {
|
||||
return typeEq(t1,t2);
|
||||
}
|
||||
|
||||
function unify( t1 : TType, t2 : TType, e : Expr ) {
|
||||
public function unify( t1 : TType, t2 : TType, e : Expr ) {
|
||||
if( !tryUnify(t1,t2) )
|
||||
error(typeStr(t1)+" should be "+typeStr(t2),e);
|
||||
}
|
||||
|
||||
function apply( t : TType, params : Array<TType>, args : Array<TType> ) {
|
||||
public function apply( t : TType, params : Array<TType>, args : Array<TType> ) {
|
||||
if( args.length != params.length ) throw "Invalid number of type parameters";
|
||||
if( args.length == 0 )
|
||||
return t;
|
||||
@@ -557,40 +709,62 @@ class Checker {
|
||||
return TFun([for( a in args ) { name : a.name, opt : a.opt, t : f(a.t) }], f(ret));
|
||||
case TAnon(fields):
|
||||
return TAnon([for( af in fields ) { name : af.name, opt : af.opt, t : f(af.t) }]);
|
||||
case TLazy(l):
|
||||
return f(l());
|
||||
}
|
||||
}
|
||||
|
||||
public function follow( t : TType ) {
|
||||
return switch( t ) {
|
||||
case TMono(r): if( r.r != null ) follow(r.r) else t;
|
||||
case TType(t,args): apply(t.t, t.params, args);
|
||||
case TType(t,args): follow(apply(t.t, t.params, args));
|
||||
case TNull(t): follow(t);
|
||||
case TLazy(f): follow(f());
|
||||
default: t;
|
||||
}
|
||||
}
|
||||
|
||||
public function getFields( t : TType ) {
|
||||
public function getFields( t : TType ) : Array<{ name : String, t : TType }> {
|
||||
var fields = [];
|
||||
while( t != null ) {
|
||||
t = follow(t);
|
||||
switch( t ) {
|
||||
case TInst(c, args):
|
||||
switch( follow(t) ) {
|
||||
case TInst(c, args):
|
||||
var map = (t) -> apply(t,c.params,args);
|
||||
while( c != null ) {
|
||||
for( fname in c.fields.keys() ) {
|
||||
var f = c.fields.get(fname);
|
||||
fields.push({ name : f.name, t : f.t });
|
||||
if( !f.isPublic || !f.complete ) continue;
|
||||
var name = f.name, t = map(f.t);
|
||||
if( allowAsync && StringTools.startsWith(name,"a_") ) {
|
||||
t = unasync(t);
|
||||
name = name.substr(2);
|
||||
}
|
||||
fields.push({ name : name, t : t });
|
||||
}
|
||||
if( c.isInterface && c.interfaces != null ) {
|
||||
for( i in c.interfaces ) {
|
||||
for( f in getFields(i) )
|
||||
fields.push({ name : f.name, t : map(f.t) });
|
||||
}
|
||||
}
|
||||
if( c.superClass == null ) break;
|
||||
switch( c.superClass ) {
|
||||
case TInst(csup,args):
|
||||
var curMap = map;
|
||||
map = (t) -> curMap(apply(t,csup.params,args));
|
||||
c = csup;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
t = c.superClass;
|
||||
case TAnon(fl):
|
||||
for( f in fl )
|
||||
fields.push({ name : f.name, t : f.t });
|
||||
break;
|
||||
default:
|
||||
}
|
||||
case TAnon(fl):
|
||||
for( f in fl )
|
||||
fields.push({ name : f.name, t : f.t });
|
||||
default:
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
function getField( t : TType, f : String, e : Expr ) {
|
||||
function getField( t : TType, f : String, e : Expr, forWrite = false ) {
|
||||
switch( follow(t) ) {
|
||||
case TInst(c, args):
|
||||
var cf = c.fields.get(f);
|
||||
@@ -598,19 +772,30 @@ class Checker {
|
||||
cf = c.fields.get("a_"+f);
|
||||
if( cf != null ) {
|
||||
var isPublic = true; // consider a_ prefixed as script specific
|
||||
cf = { isPublic : isPublic, params : cf.params, name : cf.name, t : unasync(cf.t) };
|
||||
cf = { isPublic : isPublic, canWrite : false, params : cf.params, name : cf.name, t : unasync(cf.t), complete : cf.complete };
|
||||
if( cf.t == null ) cf = null;
|
||||
}
|
||||
}
|
||||
if( cf == null && c.isInterface && c.interfaces != null ) {
|
||||
for( i in c.interfaces ) {
|
||||
var ft = getField(i, f, e, forWrite);
|
||||
if( ft != null )
|
||||
return apply(ft, c.params, args);
|
||||
}
|
||||
}
|
||||
if( cf == null ) {
|
||||
if( c.superClass == null ) return null;
|
||||
var ft = getField(c.superClass, f, e);
|
||||
var ft = getField(c.superClass, f, e, forWrite);
|
||||
if( ft != null ) ft = apply(ft, c.params, args);
|
||||
return ft;
|
||||
}
|
||||
if( !cf.isPublic )
|
||||
error("Can't access private field "+f+" on "+c.name, e);
|
||||
return apply(cf.t, c.params, args);
|
||||
if( forWrite && !cf.canWrite )
|
||||
error("Can't write readonly field "+f+" on "+c.name, e);
|
||||
var t = cf.t;
|
||||
if( cf.params != null ) t = apply(t, cf.params, [for( i in 0...cf.params.length ) makeMono()]);
|
||||
return apply(t, c.params, args);
|
||||
case TDynamic:
|
||||
return makeMono();
|
||||
case TAnon(fields):
|
||||
@@ -663,7 +848,28 @@ class Checker {
|
||||
return t.match(TInst({name:"String"},_));
|
||||
}
|
||||
|
||||
function onCompletion( expr : Expr, t : TType ) {
|
||||
if( isCompletion ) throw new Completion(expr, t);
|
||||
}
|
||||
|
||||
function typeField( o : Expr, f : String, expr : Expr, forWrite : Bool ) {
|
||||
var ot = typeExpr(o, Value);
|
||||
if( f == null )
|
||||
onCompletion(expr, ot);
|
||||
var ft = getField(ot, f, expr, forWrite);
|
||||
if( ft == null ) {
|
||||
error(typeStr(ot)+" has no field "+f, expr);
|
||||
ft = TDynamic;
|
||||
}
|
||||
return ft;
|
||||
}
|
||||
|
||||
function typeExpr( expr : Expr, withType : WithType ) : TType {
|
||||
if( expr == null && isCompletion )
|
||||
return switch( withType ) {
|
||||
case WithType(t): t;
|
||||
default: TDynamic;
|
||||
}
|
||||
switch( edef(expr) ) {
|
||||
case EConst(c):
|
||||
return switch (c) {
|
||||
@@ -675,7 +881,12 @@ class Checker {
|
||||
var l = locals.get(v);
|
||||
if( l != null ) return l;
|
||||
var g = globals.get(v);
|
||||
if( g != null ) return g;
|
||||
if( g != null ) {
|
||||
return switch( g ) {
|
||||
case TLazy(f): f();
|
||||
default: g;
|
||||
}
|
||||
}
|
||||
if( allowAsync ) {
|
||||
g = globals.get("a_"+v);
|
||||
if( g != null ) g = unasync(g);
|
||||
@@ -689,6 +900,7 @@ class Checker {
|
||||
case "trace":
|
||||
return TDynamic;
|
||||
default:
|
||||
if( isCompletion) return TDynamic;
|
||||
error("Unknown identifier "+v, expr);
|
||||
}
|
||||
case EBlock(el):
|
||||
@@ -714,32 +926,34 @@ class Checker {
|
||||
case TFun(args, ret):
|
||||
for( i in 0...params.length ) {
|
||||
var a = args[i];
|
||||
if( a == null ) error("Too many arguments", params[i]);
|
||||
var t = typeExpr(params[i], WithType(a.t));
|
||||
if( a == null ) {
|
||||
error("Too many arguments", params[i]);
|
||||
break;
|
||||
}
|
||||
var t = typeExpr(params[i], a == null ? Value : WithType(a.t));
|
||||
unify(t, a.t, params[i]);
|
||||
}
|
||||
for( i in params.length...args.length )
|
||||
if( !args[i].opt )
|
||||
error("Missing argument '"+args[i].name+"'", expr);
|
||||
error("Missing argument "+args[i].name+":"+typeStr(args[i].t), expr);
|
||||
return ret;
|
||||
case TDynamic:
|
||||
for( p in params ) typeExpr(p,Value);
|
||||
return TDynamic;
|
||||
return makeMono();
|
||||
default:
|
||||
error(typeStr(ft)+" cannot be called", e);
|
||||
return makeMono();
|
||||
}
|
||||
case EField(o, f):
|
||||
var ot = typeExpr(o, Value);
|
||||
var ft = getField(ot, f, expr);
|
||||
if( ft == null )
|
||||
error(typeStr(ot)+" has no field "+f, expr);
|
||||
return ft;
|
||||
return typeField(o,f,expr,false);
|
||||
case ECheckType(v, t):
|
||||
var ct = makeType(t, expr);
|
||||
var vt = typeExpr(v, WithType(ct));
|
||||
unify(vt, ct, v);
|
||||
return ct;
|
||||
case EMeta(_, _, e):
|
||||
case EMeta(m, _, e):
|
||||
if( m == ":untyped" && allowUntypedMeta )
|
||||
return makeMono();
|
||||
return typeExpr(e, withType);
|
||||
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
|
||||
typeExprWith(cond, TBool);
|
||||
@@ -760,17 +974,20 @@ class Checker {
|
||||
return TVoid;
|
||||
case EObject(fl):
|
||||
switch( withType ) {
|
||||
case WithType(follow(_) => TAnon(tfields)):
|
||||
case WithType(follow(_) => TAnon(tfields)) if( tfields.length > 0 ):
|
||||
var map = [for( f in tfields ) f.name => f];
|
||||
return TAnon([for( f in fl ) {
|
||||
var ft = map.get(f.name);
|
||||
if( ft == null ) error("Extra field "+f.name, f.e);
|
||||
{ t : typeExprWith(f.e, ft.t), opt : false, name : f.name }
|
||||
var ft = if( ft == null ) {
|
||||
error("Extra field "+f.name, f.e);
|
||||
TDynamic;
|
||||
} else ft.t;
|
||||
{ t : typeExprWith(f.e, ft), opt : false, name : f.name }
|
||||
}]);
|
||||
default:
|
||||
return TAnon([for( f in fl ) { t : typeExpr(f.e, Value), opt : false, name : f.name }]);
|
||||
}
|
||||
case EBreak, EContinue:
|
||||
case EBreak, EContinue, EImport(_):
|
||||
return TVoid;
|
||||
case EReturn(v):
|
||||
var et = v == null ? TVoid : typeExpr(v, allowReturn == null ? Value : WithType(allowReturn));
|
||||
@@ -778,7 +995,7 @@ class Checker {
|
||||
error("Return not allowed here", expr);
|
||||
else
|
||||
unify(et, allowReturn, v == null ? expr : v);
|
||||
return TDynamic;
|
||||
return makeMono();
|
||||
case EArrayDecl(el):
|
||||
var et = null;
|
||||
for( v in el ) {
|
||||
@@ -787,7 +1004,7 @@ class Checker {
|
||||
if( tryUnify(et,t) ) et = t else unify(t,et,v);
|
||||
}
|
||||
}
|
||||
if( et == null ) et = TDynamic;
|
||||
if( et == null ) et = makeMono();
|
||||
return types.getType("Array",[et]);
|
||||
case EArray(a, index):
|
||||
typeExprWith(index, TInt);
|
||||
@@ -798,12 +1015,26 @@ class Checker {
|
||||
}
|
||||
case EThrow(e):
|
||||
typeExpr(e, Value);
|
||||
return TDynamic;
|
||||
return makeMono();
|
||||
case EFunction(args, body, name, ret):
|
||||
var tret = ret == null ? makeMono() : makeType(ret, expr);
|
||||
var ft = null, tret = null, targs = null;
|
||||
if( currentFunType != null ) {
|
||||
switch( currentFunType ) {
|
||||
case TFun(args,ret):
|
||||
ft = currentFunType;
|
||||
tret = ret; targs = args;
|
||||
default:
|
||||
throw "assert";
|
||||
}
|
||||
currentFunType = null;
|
||||
} else {
|
||||
tret = ret == null ? makeMono() : makeType(ret, expr);
|
||||
}
|
||||
var locals = saveLocals();
|
||||
var oldRet = allowReturn;
|
||||
var oldGDef = allowDefine;
|
||||
allowReturn = tret;
|
||||
allowDefine = false;
|
||||
var withArgs = null;
|
||||
if( name != null && !withType.match(WithType(follow(_) => TFun(_))) ) {
|
||||
var ev = events.get(name);
|
||||
@@ -813,26 +1044,33 @@ class Checker {
|
||||
case WithType(follow(_) => TFun(args,ret)): withArgs = args; unify(tret,ret,expr);
|
||||
default:
|
||||
}
|
||||
var targs = [for( i in 0...args.length ) {
|
||||
var a = args[i];
|
||||
var at = a.t == null ? makeMono() : makeType(a.t, expr);
|
||||
if( withArgs != null && withArgs.length > i )
|
||||
unify(withArgs[i].t, at, expr);
|
||||
this.locals.set(a.name, at);
|
||||
{ name : a.name, opt : a.opt, t : at };
|
||||
}];
|
||||
|
||||
if( targs == null )
|
||||
targs = typeArgs(args,expr);
|
||||
for( i in 0...targs.length ) {
|
||||
var a = targs[i];
|
||||
if( withArgs != null ) {
|
||||
if( i < withArgs.length )
|
||||
unify(withArgs[i].t, a.t, expr);
|
||||
else
|
||||
error("Extra argument "+a.name, expr);
|
||||
}
|
||||
this.locals.set(a.name, a.t);
|
||||
}
|
||||
if( withArgs != null && targs.length < withArgs.length )
|
||||
error("Missing "+(withArgs.length - targs.length)+" arguments ("+[for( i in targs.length...withArgs.length ) typeStr(withArgs[i].t)].join(",")+")", expr);
|
||||
typeExpr(body,NoValue);
|
||||
allowDefine = oldGDef;
|
||||
allowReturn = oldRet;
|
||||
this.locals = locals;
|
||||
var ft = TFun(targs,tret);
|
||||
if( name != null )
|
||||
if( ft == null ) {
|
||||
ft = TFun(targs, tret);
|
||||
locals.set(name, ft);
|
||||
}
|
||||
return ft;
|
||||
case EUnop(op, _, e):
|
||||
var et = typeExpr(e, Value);
|
||||
switch( op ) {
|
||||
case "++", "--":
|
||||
case "++", "--", "-":
|
||||
unify(et,TInt,e);
|
||||
return et;
|
||||
case "!":
|
||||
@@ -847,13 +1085,27 @@ class Checker {
|
||||
case TInst({name:"Array"},[t]):
|
||||
t;
|
||||
default:
|
||||
var ft = getField(itt,"iterator", it);
|
||||
if( ft == null )
|
||||
switch( itt ) {
|
||||
case TAbstract(a, args):
|
||||
// special case : we allow unconditional access
|
||||
// to an abstract iterator() underlying value (eg: ArrayProxy)
|
||||
ft = getField(apply(a.t,a.params,args),"iterator",it);
|
||||
default:
|
||||
}
|
||||
if( ft != null )
|
||||
switch( ft ) {
|
||||
case TFun([],ret): ft = ret;
|
||||
default: ft = null;
|
||||
}
|
||||
var t = makeMono();
|
||||
var iter = makeIterator(t);
|
||||
unify(itt,iter,it);
|
||||
unify(ft != null ? ft : itt,iter,it);
|
||||
t;
|
||||
}
|
||||
this.locals.set(v, vt);
|
||||
typeExpr(e, Value);
|
||||
typeExpr(e, NoValue);
|
||||
this.locals = locals;
|
||||
return TVoid;
|
||||
case EBinop(op, e1, e2):
|
||||
@@ -863,7 +1115,19 @@ class Checker {
|
||||
typeExprWith(e2,TInt);
|
||||
return TInt;
|
||||
case "=":
|
||||
var vt = typeExpr(e1, Value);
|
||||
if( allowDefine ) {
|
||||
switch( edef(e1) ) {
|
||||
case EIdent(i) if( !locals.exists(i) && !globals.exists(i) ):
|
||||
var vt = typeExpr(e2,Value);
|
||||
locals.set(i, vt);
|
||||
return vt;
|
||||
default:
|
||||
}
|
||||
}
|
||||
var vt = switch( edef(e1) ) {
|
||||
case EField(o,f): typeField(o, f, e1, true);
|
||||
default: typeExpr(e1,Value);
|
||||
}
|
||||
typeExprWith(e2,vt);
|
||||
return vt;
|
||||
case "+":
|
||||
@@ -930,11 +1194,48 @@ class Checker {
|
||||
}
|
||||
error("Unsupported operation "+op, expr);
|
||||
}
|
||||
case ETry(etry, v, et, ecatch):
|
||||
var vt = typeExpr(etry, withType);
|
||||
|
||||
var old = locals.get(v);
|
||||
locals.set(v, makeType(et, ecatch));
|
||||
var ct = typeExpr(ecatch, withType);
|
||||
if( old != null ) locals.set(v,old) else locals.remove(v);
|
||||
|
||||
if( withType == NoValue )
|
||||
return TVoid;
|
||||
if( tryUnify(vt,ct) )
|
||||
return ct;
|
||||
unify(ct,vt,ecatch);
|
||||
return vt;
|
||||
case ESwitch(value, cases, defaultExpr):
|
||||
var tmin = null;
|
||||
var vt = typeExpr(value, Value);
|
||||
inline function mergeType(t,p) {
|
||||
if( withType != NoValue ) {
|
||||
if( tmin == null )
|
||||
tmin = t;
|
||||
else if( !tryUnify(t,tmin) ) {
|
||||
unify(tmin,t, p);
|
||||
tmin = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
for( c in cases ) {
|
||||
for( v in c.values ) {
|
||||
var ct = typeExpr(v, WithType(vt));
|
||||
unify(ct, vt, v);
|
||||
}
|
||||
var et = typeExpr(c.expr, withType);
|
||||
mergeType(et, c.expr);
|
||||
}
|
||||
if( defaultExpr != null )
|
||||
mergeType( typeExpr(defaultExpr, withType), defaultExpr);
|
||||
return withType == NoValue ? TVoid : tmin == null ? makeMono() : tmin;
|
||||
case ENew(cl, params):
|
||||
case ETry(e, v, t, ecatch):
|
||||
case ESwitch(e, cases, defaultExpr):
|
||||
}
|
||||
error("Don't know how to type "+edef(expr).getName(), expr);
|
||||
return TDynamic;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package hscript;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class CustomClassHandler implements IHScriptCustomConstructor {
|
||||
public static var staticHandler = new StaticHandler();
|
||||
|
||||
public var ogInterp:Interp;
|
||||
public var name:String;
|
||||
public var fields:Array<Expr>;
|
||||
public var extend:String;
|
||||
public var interfaces:Array<String>;
|
||||
|
||||
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
|
||||
this.ogInterp = ogInterp;
|
||||
this.name = name;
|
||||
this.fields = fields;
|
||||
this.extend = extend;
|
||||
this.interfaces = interfaces;
|
||||
}
|
||||
|
||||
public function hnew(args:Array<Dynamic>):Dynamic {
|
||||
var interp = new Interp();
|
||||
|
||||
interp.errorHandler = ogInterp.errorHandler;
|
||||
|
||||
var cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX');
|
||||
var _class = Type.createInstance(cl, args);
|
||||
|
||||
var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||
var capturedLocals:Map<String, {r:Dynamic, depth:Int}> = [];
|
||||
for(k=>e in __capturedLocals)
|
||||
if (e != null && e.depth <= 0)
|
||||
capturedLocals.set(k, e);
|
||||
|
||||
var disallowCopy = Type.getInstanceFields(cl);
|
||||
|
||||
//trace("Locals");
|
||||
for (key => value in capturedLocals) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.locals.set(key, {r: value, depth: -1});
|
||||
//trace(key, value);
|
||||
}
|
||||
}
|
||||
//trace("Variables");
|
||||
for (key => value in ogInterp.variables) {
|
||||
if(!disallowCopy.contains(key)) {
|
||||
interp.variables.set(key, value);
|
||||
//trace(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
for(expr in fields) {
|
||||
@:privateAccess
|
||||
interp.exprReturn(expr);
|
||||
}
|
||||
|
||||
interp.variables.set("super", staticHandler);
|
||||
|
||||
_class.__interp = interp;
|
||||
interp.scriptObject = _class;
|
||||
|
||||
var newFunc = interp.variables.get("new");
|
||||
if(newFunc != null) {
|
||||
Reflect.callMethod(null, newFunc, args);
|
||||
}
|
||||
|
||||
for(variable => value in interp.variables) {
|
||||
if(variable == "this") continue;
|
||||
}
|
||||
|
||||
return _class;
|
||||
}
|
||||
|
||||
public function toString():String {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateClass implements IHScriptCustomBehaviour {
|
||||
public var __interp:Interp;
|
||||
|
||||
public function hset(name:String, val:Dynamic):Dynamic {
|
||||
if(this.__interp.variables.exists("set_" + name)) {
|
||||
return this.__interp.variables.get("set_" + name)(val); // TODO: Prevent recursion from setting it in the function
|
||||
}
|
||||
if (this.__interp.variables.exists(name)) {
|
||||
this.__interp.variables.set(name, val);
|
||||
return val;
|
||||
}
|
||||
Reflect.setProperty(this, name, val);
|
||||
return Reflect.field(this, name);
|
||||
}
|
||||
public function hget(name:String):Dynamic {
|
||||
if(this.__interp.variables.exists("get_" + name))
|
||||
return this.__interp.variables.get("get_" + name)();
|
||||
if (this.__interp.variables.exists(name))
|
||||
return this.__interp.variables.get(name);
|
||||
return Reflect.getProperty(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
class StaticHandler {
|
||||
public function new() {}
|
||||
}
|
||||
+10
-5
@@ -45,10 +45,10 @@ enum Expr {
|
||||
#end
|
||||
EConst( c : Const );
|
||||
EIdent( v : String );
|
||||
EVar( n : String, ?t : CType, ?e : Expr );
|
||||
EVar( n : String, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool );
|
||||
EParent( e : Expr );
|
||||
EBlock( e : Array<Expr> );
|
||||
EField( e : Expr, f : String );
|
||||
EField( e : Expr, f : String , ?safe : Bool );
|
||||
EBinop( op : String, e1 : Expr, e2 : Expr );
|
||||
EUnop( op : String, prefix : Bool, e : Expr );
|
||||
ECall( e : Expr, params : Array<Expr> );
|
||||
@@ -57,19 +57,22 @@ enum Expr {
|
||||
EFor( v : String, it : Expr, e : Expr );
|
||||
EBreak;
|
||||
EContinue;
|
||||
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType );
|
||||
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool );
|
||||
EReturn( ?e : Expr );
|
||||
EArray( e : Expr, index : Expr );
|
||||
EArrayDecl( e : Array<Expr> );
|
||||
EArrayDecl( e : Array<Expr>, ?wantedType: CType );
|
||||
ENew( cl : String, params : Array<Expr> );
|
||||
EThrow( e : Expr );
|
||||
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
|
||||
EObject( fl : Array<{ name : String, e : Expr }> );
|
||||
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
|
||||
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr);
|
||||
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr );
|
||||
EDoWhile( cond : Expr, e : Expr);
|
||||
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||
ECheckType( e : Expr, t : CType );
|
||||
|
||||
EImport( c : String, ?asname:String );
|
||||
EClass( name:String, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
|
||||
}
|
||||
|
||||
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
|
||||
@@ -117,6 +120,8 @@ enum Error {
|
||||
EInvalidOp( op : String );
|
||||
EInvalidAccess( f : String );
|
||||
ECustom( msg : String );
|
||||
EInvalidClass( className : String);
|
||||
EAlreadyExistingClass( className : String);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomBehaviour {
|
||||
public function hset(name:String, val:Dynamic):Dynamic;
|
||||
public function hget(name:String):Dynamic;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package hscript;
|
||||
|
||||
interface IHScriptCustomConstructor {
|
||||
public function hnew(args:Array<Dynamic>):Dynamic;
|
||||
}
|
||||
+806
-468
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -79,6 +79,8 @@ class Macro {
|
||||
#if (haxe_ver >= 4)
|
||||
case OpIn: "in";
|
||||
#end
|
||||
default:
|
||||
continue;
|
||||
};
|
||||
binops.set(str, op);
|
||||
if( assign )
|
||||
@@ -92,6 +94,9 @@ class Macro {
|
||||
case OpNegBits: "~";
|
||||
case OpIncrement: "++";
|
||||
case OpDecrement: "--";
|
||||
#if (haxe_ver >= 4.2)
|
||||
case OpSpread: continue;
|
||||
#end
|
||||
}
|
||||
unops.set(str, op);
|
||||
}
|
||||
@@ -214,7 +219,7 @@ class Macro {
|
||||
opt : false,
|
||||
value : null,
|
||||
});
|
||||
EFunction(name, {
|
||||
EFunction(#if haxe4 FNamed(name,false) #else name #end, {
|
||||
params : [],
|
||||
args : targs,
|
||||
expr : convert(e),
|
||||
|
||||
+916
-545
File diff suppressed because it is too large
Load Diff
+28
-7
@@ -48,7 +48,7 @@ class Printer {
|
||||
|
||||
function type( t : CType ) {
|
||||
switch( t ) {
|
||||
case CTOpt(t):
|
||||
case CTOpt(t):
|
||||
add('?');
|
||||
type(t);
|
||||
case CTPath(path, params):
|
||||
@@ -114,6 +114,25 @@ class Printer {
|
||||
return;
|
||||
}
|
||||
switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EImport(c, n):
|
||||
add("import " + c);
|
||||
if(n != null)
|
||||
add(' as $n');
|
||||
case EClass(name, fields, extend, interfaces):
|
||||
add('class $name');
|
||||
if (extend != null)
|
||||
add(' extends $extend');
|
||||
for(_interface in interfaces) {
|
||||
add(' implements $_interface');
|
||||
}
|
||||
add(' {\n');
|
||||
tabs += "\t";
|
||||
//for(field in fields) {
|
||||
// expr(field);
|
||||
//}
|
||||
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
case EConst(c):
|
||||
switch( c ) {
|
||||
case CInt(i): add(i);
|
||||
@@ -122,7 +141,7 @@ class Printer {
|
||||
}
|
||||
case EIdent(v):
|
||||
add(v);
|
||||
case EVar(n, t, e):
|
||||
case EVar(n, t, e): // TODO: static, public, override
|
||||
add("var " + n);
|
||||
addType(t);
|
||||
if( e != null ) {
|
||||
@@ -145,9 +164,9 @@ class Printer {
|
||||
tabs = tabs.substr(1);
|
||||
add("}");
|
||||
}
|
||||
case EField(e, f):
|
||||
case EField(e, f, s):
|
||||
expr(e);
|
||||
add("." + f);
|
||||
add((s == true ? "?." : ".") + f);
|
||||
case EBinop(op, e1, e2):
|
||||
expr(e1);
|
||||
add(" " + op + " ");
|
||||
@@ -207,7 +226,7 @@ class Printer {
|
||||
add("break");
|
||||
case EContinue:
|
||||
add("continue");
|
||||
case EFunction(params, e, name, ret):
|
||||
case EFunction(params, e, name, ret): // TODO: static, public, override
|
||||
add("function");
|
||||
if( name != null )
|
||||
add(" " + name);
|
||||
@@ -234,7 +253,7 @@ class Printer {
|
||||
add("[");
|
||||
expr(index);
|
||||
add("]");
|
||||
case EArrayDecl(el):
|
||||
case EArrayDecl(el, _):
|
||||
add("[");
|
||||
var first = true;
|
||||
for( e in el ) {
|
||||
@@ -331,7 +350,7 @@ class Printer {
|
||||
|
||||
public static function errorToString( e : Expr.Error ) {
|
||||
var message = switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EInvalidChar(c): "Invalid character: '"+String.fromCharCode(c)+"' ("+c+")";
|
||||
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
|
||||
case EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||
case EUnterminatedString: "Unterminated string";
|
||||
case EUnterminatedComment: "Unterminated comment";
|
||||
@@ -341,6 +360,8 @@ class Printer {
|
||||
case EInvalidOp(op): "Invalid operator: "+op;
|
||||
case EInvalidAccess(f): "Invalid access to field " + f;
|
||||
case ECustom(msg): msg;
|
||||
case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
|
||||
case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
|
||||
};
|
||||
#if hscriptPos
|
||||
return e.origin + ":" + e.line + ": " + message;
|
||||
|
||||
@@ -27,6 +27,8 @@ class Tools {
|
||||
public static function iter( e : Expr, f : Expr -> Void ) {
|
||||
switch( expr(e) ) {
|
||||
case EConst(_), EIdent(_):
|
||||
case EImport(c): f(e);
|
||||
case EClass(_, e, _, _): for( a in e ) f(a);
|
||||
case EVar(_, _, e): if( e != null ) f(e);
|
||||
case EParent(e): f(e);
|
||||
case EBlock(el): for( e in el ) f(e);
|
||||
@@ -57,6 +59,7 @@ class Tools {
|
||||
if( def != null ) f(def);
|
||||
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
|
||||
case ECheckType(e,_): f(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +89,8 @@ class Tools {
|
||||
case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) { values : [for( v in c.values ) f(v)], expr : f(c.expr) } ], def == null ? null : f(def));
|
||||
case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
|
||||
case ECheckType(e,t): ECheckType(f(e), t);
|
||||
case EImport(c): EImport(c);
|
||||
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
||||
}
|
||||
return mk(edef, e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
package hscript;
|
||||
|
||||
import Type.ValueType;
|
||||
import haxe.macro.ComplexTypeTools;
|
||||
#if macro
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Printer;
|
||||
import haxe.macro.Compiler;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class UsingHandler {
|
||||
public var usedClass:Class<Dynamic>;
|
||||
public var className:String;
|
||||
|
||||
public function new(className:String, usedClass:Class<Dynamic>) {
|
||||
this.className = className;
|
||||
this.usedClass = usedClass;
|
||||
}
|
||||
|
||||
public static function init() {
|
||||
Compiler.addGlobalMetadata('flixel', '@:build(hscript.UsingHandler.build())');
|
||||
Compiler.addGlobalMetadata('openfl.display.BlendMode', '@:build(hscript.UsingHandler.build())');
|
||||
}
|
||||
|
||||
public static function build():Array<Field> {
|
||||
var fields = Context.getBuildFields();
|
||||
var clRef = Context.getLocalClass();
|
||||
if (clRef == null) return fields;
|
||||
var cl = clRef.get();
|
||||
|
||||
if (/* cl.name.startsWith("Flx") && */ cl.name.endsWith("_Impl_") && cl.params.length <= 0 && !cl.meta.has(":multiType")) {
|
||||
var metas = cl.meta.get();
|
||||
|
||||
var shadowClass = macro class {
|
||||
|
||||
};
|
||||
shadowClass.kind = TDClass();
|
||||
shadowClass.params = switch(cl.params.length) {
|
||||
case 0:
|
||||
null;
|
||||
case 1:
|
||||
[
|
||||
{
|
||||
name: "T",
|
||||
}
|
||||
];
|
||||
default:
|
||||
[for(k=>e in cl.params) {
|
||||
name: "T" + Std.int(k+1)
|
||||
}];
|
||||
};
|
||||
shadowClass.name = '${cl.name.substr(0, cl.name.length - 6)}_HSC';
|
||||
|
||||
var imports = Context.getLocalImports().copy();
|
||||
setupMetas(shadowClass, imports);
|
||||
|
||||
for(f in fields)
|
||||
switch(f.kind) {
|
||||
case FFun(fun):
|
||||
if (f.access.contains(AStatic)) {
|
||||
if (fun.expr != null)
|
||||
shadowClass.fields.push(f);
|
||||
}
|
||||
case FProp(get, set, t, e):
|
||||
if (get == "default" && (set == "never" || set == "null"))
|
||||
shadowClass.fields.push(f);
|
||||
case FVar(t, e):
|
||||
if (f.access.contains(AStatic) || cl.meta.has(":enum") || f.name.toUpperCase() == f.name) {
|
||||
var name:String = f.name;
|
||||
var enumType:String = cl.name;
|
||||
var pack = cl.module.split(".");
|
||||
pack.pop();
|
||||
var complexType:ComplexType = t != null ? t : (name.contains("REGEX") ? TPath({
|
||||
name: "EReg",
|
||||
pack: []
|
||||
}) : TPath({
|
||||
name: cl.name.substr(0, cl.name.length - 6),
|
||||
pack: pack}));
|
||||
var field:Field = {
|
||||
pos: f.pos,
|
||||
name: f.name,
|
||||
meta: f.meta,
|
||||
kind: FVar(complexType, {
|
||||
pos: Context.currentPos(),
|
||||
expr: ECast(e, complexType)
|
||||
}),
|
||||
doc: f.doc,
|
||||
access: [APublic, AStatic]
|
||||
}
|
||||
|
||||
shadowClass.fields.push(field);
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
Context.defineModule(cl.module + "_HSC", [shadowClass], imports);
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
static function fixStdTypes(type:ComplexType) {
|
||||
switch(type) {
|
||||
case TPath({name: "StdTypes"}):
|
||||
var a:TypePath = type.getParameters()[0];
|
||||
a.name = a.sub;
|
||||
a.sub = null;
|
||||
default:
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static function setupMetas(shadowClass:TypeDefinition, imports) {
|
||||
shadowClass.meta = [{
|
||||
name: ':dox',
|
||||
pos: Context.currentPos(),
|
||||
params: [
|
||||
{
|
||||
expr: EConst(CIdent("hide")),
|
||||
pos: Context.currentPos()
|
||||
}
|
||||
]
|
||||
}];
|
||||
var module = Context.getModule(Context.getLocalModule());
|
||||
for(t in module) {
|
||||
switch(t) {
|
||||
case TInst(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
case TEnum(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
case TType(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
case TAbstract(t, params):
|
||||
if (t != null) {
|
||||
var e = t.get();
|
||||
processModule(shadowClass, e.module, e.name);
|
||||
processImport(imports, e.module, e.name);
|
||||
}
|
||||
default:
|
||||
// not needed?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function processModule(shadowClass:TypeDefinition, module:String, n:String) {
|
||||
if (n.endsWith("_Impl_"))
|
||||
n = n.substr(0, n.length - 6);
|
||||
if (module.endsWith("_Impl_"))
|
||||
module = module.substr(0, module.length - 6);
|
||||
|
||||
shadowClass.meta.push(
|
||||
{
|
||||
name: ':access',
|
||||
params: [
|
||||
Context.parse(fixModuleName(module.endsWith('.${n}') ? module : '${module}.${n}'), Context.currentPos())
|
||||
],
|
||||
pos: Context.currentPos()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*public static function getModuleName(path:Type) {
|
||||
switch(path) {
|
||||
case TPath(name, pack):// | TDClass(name, pack):
|
||||
var str = "";
|
||||
for(p in pack) {
|
||||
str += p + ".";
|
||||
}
|
||||
str += name;
|
||||
return str;
|
||||
|
||||
default:
|
||||
}
|
||||
return "INVALID";
|
||||
}*/
|
||||
|
||||
public static function fixModuleName(name:String) {
|
||||
return [for(s in name.split(".")) if (s.charAt(0) == "_") s.substr(1) else s].join(".");
|
||||
}
|
||||
public static function processImport(imports:Array<ImportExpr>, module:String, n:String) {
|
||||
if (n.endsWith("_Impl_"))
|
||||
n = n.substr(0, n.length - 6);
|
||||
module = fixModuleName(module);
|
||||
if (module.endsWith("_Impl_"))
|
||||
module = module.substr(0, module.length - 6);
|
||||
|
||||
imports.push({
|
||||
path: [for(m in module.split(".")) {
|
||||
name: m,
|
||||
pos: Context.currentPos()
|
||||
}],
|
||||
mode: INormal
|
||||
});
|
||||
}
|
||||
|
||||
public static function cleanExpr(expr:Expr, oldFunc:String, newFunc:String) {
|
||||
if (expr == null) return;
|
||||
if (expr.expr == null) return;
|
||||
switch(expr.expr) {
|
||||
case EConst(c):
|
||||
switch(c) {
|
||||
case CIdent(s):
|
||||
if (s == oldFunc)
|
||||
expr.expr = EConst(CIdent(newFunc));
|
||||
case CString(s, b):
|
||||
if (s == oldFunc)
|
||||
expr.expr = EConst(CString(s, b));
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
case EField(e, field):
|
||||
if (field == oldFunc && e != null) {
|
||||
switch(e.expr) {
|
||||
case EConst(c):
|
||||
switch(c) {
|
||||
case CIdent(s):
|
||||
if (s == "super")
|
||||
expr.expr = EField(e, newFunc);
|
||||
default:
|
||||
|
||||
}
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
case EParenthesis(e):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EObjectDecl(fields):
|
||||
for(f in fields) {
|
||||
cleanExpr(f.expr, oldFunc, newFunc);
|
||||
}
|
||||
case EArrayDecl(values):
|
||||
for(a in values) {
|
||||
cleanExpr(a, oldFunc, newFunc);
|
||||
}
|
||||
case ECall(e, params):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EBlock(exprs):
|
||||
for(e in exprs)
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EFor(it, expr):
|
||||
cleanExpr(it, oldFunc, newFunc);
|
||||
cleanExpr(expr, oldFunc, newFunc);
|
||||
case EIf(econd, eif, eelse):
|
||||
cleanExpr(econd, oldFunc, newFunc);
|
||||
cleanExpr(eif, oldFunc, newFunc);
|
||||
cleanExpr(eelse, oldFunc, newFunc);
|
||||
case EWhile(econd, e, normalWhile):
|
||||
cleanExpr(econd, oldFunc, newFunc);
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ECast(e, t):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ECheckType(e, t):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ETry(e, catches):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
for(c in catches) {
|
||||
cleanExpr(c.expr, oldFunc, newFunc);
|
||||
}
|
||||
case EThrow(e):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case ETernary(econd, eif, eelse):
|
||||
cleanExpr(econd, oldFunc, newFunc);
|
||||
cleanExpr(eif, oldFunc, newFunc);
|
||||
cleanExpr(eelse, oldFunc, newFunc);
|
||||
case ESwitch(e, cases, edef):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
for(c in cases) {
|
||||
cleanExpr(c.expr, oldFunc, newFunc);
|
||||
}
|
||||
cleanExpr(edef, oldFunc, newFunc);
|
||||
case EReturn(e):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EIs(e, t):
|
||||
cleanExpr(e, oldFunc, newFunc);
|
||||
case EVars(vars):
|
||||
for(v in vars) {
|
||||
cleanExpr(v.expr, oldFunc, newFunc);
|
||||
}
|
||||
case ENew(t, params):
|
||||
for(p in params) {
|
||||
cleanExpr(p, oldFunc, newFunc);
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
class UsingHandler {
|
||||
public var usedClass:Class<Dynamic>;
|
||||
public var className:String;
|
||||
}
|
||||
#end
|
||||
Reference in New Issue
Block a user