Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ed6f15fe1 | ||
|
|
575040be65 | ||
|
|
206959adbd | ||
|
|
dd1c97b6af | ||
|
|
85463dcff0 | ||
|
|
7ff816417e | ||
|
|
3fa2f3898a | ||
|
|
514ce9eec2 | ||
|
|
5bc357b4b3 | ||
|
|
56ddc3c24e | ||
|
|
e407b2a307 | ||
|
|
1ac194ed1d | ||
|
|
62bf5d4cc9 | ||
|
|
27093064a9 | ||
|
|
860a3018a7 | ||
|
|
eb3992dbdb | ||
|
|
8c5f9cc1d3 | ||
|
|
ed78703090 | ||
|
|
29b61bcee9 | ||
|
|
0ff49845a7 | ||
|
|
c17366b2b1 | ||
|
|
5da267fb31 | ||
|
|
dffa3345d0 | ||
|
|
a23c767fb0 | ||
|
|
5d6ec51302 | ||
|
|
585f277bf5 | ||
|
|
aae5f431cb | ||
|
|
4eb84e5c32 | ||
|
|
70b11ec7ad | ||
|
|
f2944e0bae | ||
|
|
fdec33d086 | ||
|
|
0702074469 | ||
|
|
6d5cbf6974 | ||
|
|
5429a3a4d4 | ||
|
|
c124076279 | ||
|
|
0d1d811d2c | ||
|
|
8c04831339 | ||
|
|
4a8b2690aa | ||
|
|
e2bc716ea7 | ||
|
|
6c5987f9af | ||
|
|
1e79880c72 | ||
|
|
f62d891c63 | ||
|
|
930cb8853c | ||
|
|
22104ff64e | ||
|
|
17564441b5 | ||
|
|
a9140a4403 | ||
|
|
ce47ca0a8a | ||
|
|
852fee170e | ||
|
|
8463e99d13 | ||
|
|
3c016b73ee | ||
|
|
8d4173b0be | ||
|
|
502f043804 | ||
|
|
f8571fb175 | ||
|
|
7209aa8880 | ||
|
|
1a4a1217ae | ||
|
|
139c360feb | ||
|
|
be1afbdb5c | ||
|
|
8a7e8c8d7d | ||
|
|
931f79dacb | ||
|
|
261e6ee78e | ||
|
|
9eaddcdafb | ||
|
|
b8ede1a3e7 | ||
|
|
bbaacd5d28 | ||
|
|
60ed5fdfe2 | ||
|
|
512f88c63e | ||
|
|
50c01b2cfb | ||
|
|
27d3f889b5 | ||
|
|
140a9918c0 | ||
|
|
fc6a30b684 | ||
|
|
deb0ae8b8e | ||
|
|
1425479569 |
+10
-7
@@ -8,8 +8,7 @@ sudo: false
|
|||||||
dist: trusty
|
dist: trusty
|
||||||
|
|
||||||
haxe:
|
haxe:
|
||||||
- "3.2.1"
|
- "3.4.7"
|
||||||
- "3.4.2"
|
|
||||||
- development
|
- development
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
@@ -20,8 +19,6 @@ addons:
|
|||||||
- g++-multilib
|
- g++-multilib
|
||||||
# Python
|
# Python
|
||||||
- python3
|
- python3
|
||||||
# PHP
|
|
||||||
- php5-cli
|
|
||||||
# C#
|
# C#
|
||||||
- mono-devel
|
- mono-devel
|
||||||
- mono-mcs
|
- mono-mcs
|
||||||
@@ -34,25 +31,30 @@ install:
|
|||||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||||
export DISPLAY=:99.0;
|
export DISPLAY=:99.0;
|
||||||
export AUDIODEV=null;
|
export AUDIODEV=null;
|
||||||
|
phpenv global 7.0;
|
||||||
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||||
brew update;
|
brew update;
|
||||||
brew install mono;
|
brew install mono;
|
||||||
brew install python3;
|
brew upgrade python;
|
||||||
|
brew install php;
|
||||||
fi
|
fi
|
||||||
# Download and setup a standalone flash player debugger
|
# Download and setup a standalone flash player debugger
|
||||||
- haxe flash/install.hxml
|
- haxe flash/install.hxml
|
||||||
# Install haxelibs
|
# Install haxelibs
|
||||||
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ]; then
|
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ]; then
|
||||||
haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git;
|
haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git;
|
||||||
|
else
|
||||||
|
haxelib install hxcpp;
|
||||||
|
fi
|
||||||
|
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ] || [ "${TRAVIS_HAXE_VERSION}" = "3.2.1" ]; then
|
||||||
pushd $(haxelib path hxcpp | head -1);
|
pushd $(haxelib path hxcpp | head -1);
|
||||||
pushd tools/hxcpp; haxe compile.hxml; popd;
|
pushd tools/hxcpp; haxe compile.hxml; popd;
|
||||||
pushd project; neko build.n; popd;
|
pushd project; neko build.n; popd;
|
||||||
popd;
|
popd;
|
||||||
else
|
|
||||||
haxelib install hxcpp;
|
|
||||||
fi
|
fi
|
||||||
- haxelib install hxjava
|
- haxelib install hxjava
|
||||||
- haxelib install hxcs
|
- haxelib install hxcs
|
||||||
|
- haxelib install hx3compat
|
||||||
- haxelib dev hscript .
|
- haxelib dev hscript .
|
||||||
- haxelib list
|
- haxelib list
|
||||||
|
|
||||||
@@ -61,6 +63,7 @@ script:
|
|||||||
haxe build.hxml &&
|
haxe build.hxml &&
|
||||||
popd
|
popd
|
||||||
- haxe bin/build-interp.hxml
|
- haxe bin/build-interp.hxml
|
||||||
|
- haxe bin/build-interp.hxml -D hscriptPos
|
||||||
- haxe bin/build-neko.hxml && neko bin/Test.n
|
- haxe bin/build-neko.hxml && neko bin/Test.n
|
||||||
- haxe bin/build-js.hxml && node bin/Test.js
|
- haxe bin/build-js.hxml && node bin/Test.js
|
||||||
- haxe bin/build-java.hxml && java -jar bin/Test.jar
|
- haxe bin/build-java.hxml && java -jar bin/Test.jar
|
||||||
|
|||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
|
||||||
|
// Pointez pour afficher la description des attributs existants.
|
||||||
|
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "HashLink (launch)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "hl",
|
||||||
|
"hxml": "hscript.hxml",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"preLaunchTask": "Build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Build",
|
||||||
|
"type": "hxml",
|
||||||
|
"file": "hscript.hxml",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (C)2008-2017 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.
|
||||||
|
|
||||||
@@ -64,6 +64,8 @@ You can use `parser.allowJSON` to allow JSON data.
|
|||||||
|
|
||||||
You can use `parser.allowTypes` to parse types for local vars, exceptions, function args and return types. Types are ignored by the interpreter.
|
You can use `parser.allowTypes` to parse types for local vars, exceptions, function args and return types. Types are ignored by the interpreter.
|
||||||
|
|
||||||
|
You can use `parser.allowMetadata` to parse metadata before expressions on in anonymous types. Metadata are ignored by the interpreter.
|
||||||
|
|
||||||
You can use `new hscript.Macro(pos).convert(ast)` to convert an hscript AST to a Haxe macros one.
|
You can use `new hscript.Macro(pos).convert(ast)` to convert an hscript AST to a Haxe macros one.
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import haxe.ds.EnumValueMap;
|
import haxe.ds.EnumValueMap;
|
||||||
import haxe.ds.Option;
|
import haxe.ds.Option;
|
||||||
import hscript.Macro;
|
import hscript.Macro;
|
||||||
|
import hscript.Tools;
|
||||||
|
import hscript.Async;
|
||||||
|
import hscript.Printer;
|
||||||
import haxe.unit.*;
|
import haxe.unit.*;
|
||||||
|
|
||||||
class Test extends TestCase {
|
class Test extends TestCase {
|
||||||
function assertScript(x,v:Dynamic,?vars : Dynamic,allowTypes=false) {
|
function assertScript(x,v:Dynamic,?vars : Dynamic, allowTypes=false, ?pos:haxe.PosInfos) {
|
||||||
var p = new hscript.Parser();
|
var p = new hscript.Parser();
|
||||||
p.allowTypes = allowTypes;
|
p.allowTypes = allowTypes;
|
||||||
var program = p.parseString(x);
|
var program = p.parseString(x);
|
||||||
@@ -15,7 +18,7 @@ class Test extends TestCase {
|
|||||||
for( v in Reflect.fields(vars) )
|
for( v in Reflect.fields(vars) )
|
||||||
interp.variables.set(v,Reflect.field(vars,v));
|
interp.variables.set(v,Reflect.field(vars,v));
|
||||||
var ret : Dynamic = interp.execute(program);
|
var ret : Dynamic = interp.execute(program);
|
||||||
assertEquals(v, ret);
|
assertEquals(v, ret, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test():Void {
|
function test():Void {
|
||||||
@@ -96,8 +99,18 @@ class Test extends TestCase {
|
|||||||
assertScript("var a:Array<Dynamic>=[1,2,4]; a[2]", 4, null, true);
|
assertScript("var a:Array<Dynamic>=[1,2,4]; a[2]", 4, null, true);
|
||||||
assertScript("/**/0", 0);
|
assertScript("/**/0", 0);
|
||||||
assertScript("x=1;x*=-2", -2);
|
assertScript("x=1;x*=-2", -2);
|
||||||
|
assertScript("var f = x -> x + 1; f(3)", 4);
|
||||||
|
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);
|
||||||
|
assertScript("var f = (x,y:Int) -> x + y; f(3,1)", 4);
|
||||||
|
assertScript("var f = (x:Int,y:Int) -> x + y; f(3,1)", 4);
|
||||||
|
assertScript("var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testMap():Void {
|
function testMap():Void {
|
||||||
var objKey = { ok:true };
|
var objKey = { ok:true };
|
||||||
var vars = {
|
var vars = {
|
||||||
@@ -110,7 +123,7 @@ class Test extends TestCase {
|
|||||||
stringIntMap: ["foo" => 100]
|
stringIntMap: ["foo" => 100]
|
||||||
}
|
}
|
||||||
vars.enumMap.set(vars.enumKey, "ok");
|
vars.enumMap.set(vars.enumKey, "ok");
|
||||||
|
|
||||||
assertScript('stringMap["foo"]', "Foo", vars);
|
assertScript('stringMap["foo"]', "Foo", vars);
|
||||||
assertScript('intMap[100]', "one hundred", vars);
|
assertScript('intMap[100]', "one hundred", vars);
|
||||||
assertScript('objMap[objKey]', "ok", vars);
|
assertScript('objMap[objKey]', "ok", vars);
|
||||||
@@ -149,9 +162,16 @@ class Test extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static function main() {
|
static function main() {
|
||||||
|
#if ((haxe_ver < 4) && php)
|
||||||
|
// uncaught exception: The each() function is deprecated. This message will be suppressed on further calls (errno: 8192)
|
||||||
|
// in file: /Users/travis/build/andyli/hscript/bin/lib/Type.class.php line 178
|
||||||
|
untyped __php__("error_reporting(E_ALL ^ E_DEPRECATED);");
|
||||||
|
#end
|
||||||
|
|
||||||
var runner = new TestRunner();
|
var runner = new TestRunner();
|
||||||
runner.add(new Test());
|
runner.add(new Test());
|
||||||
var succeed = runner.run();
|
var succeed = runner.run();
|
||||||
|
|
||||||
#if sys
|
#if sys
|
||||||
Sys.exit(succeed ? 0 : 1);
|
Sys.exit(succeed ? 0 : 1);
|
||||||
#elseif flash
|
#elseif flash
|
||||||
|
|||||||
+7
-4
@@ -5,8 +5,10 @@ environment:
|
|||||||
HAXELIB_ROOT: C:\projects\haxelib
|
HAXELIB_ROOT: C:\projects\haxelib
|
||||||
|
|
||||||
install:
|
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)
|
# Install the haxe chocolatey package (https://chocolatey.org/packages/haxe)
|
||||||
- cinst haxe -version 3.4.2 -y
|
- cinst haxe -version 3.4.7 -y
|
||||||
- RefreshEnv
|
- RefreshEnv
|
||||||
# Setup haxelib
|
# Setup haxelib
|
||||||
- mkdir "%HAXELIB_ROOT%"
|
- mkdir "%HAXELIB_ROOT%"
|
||||||
@@ -18,9 +20,10 @@ install:
|
|||||||
# Download and setup a standalone flash player debugger
|
# Download and setup a standalone flash player debugger
|
||||||
- haxe flash/install.hxml
|
- haxe flash/install.hxml
|
||||||
# Install project dependencies
|
# Install project dependencies
|
||||||
- haxelib install hxcpp > log.txt || type log.txt && cmd /C exit 1
|
- haxelib install hxcpp > log.txt || type log.txt && cmd /C exit 1
|
||||||
- haxelib install hxjava > log.txt || type log.txt && cmd /C exit 1
|
- haxelib install hxjava > log.txt || type log.txt && cmd /C exit 1
|
||||||
- haxelib install hxcs > log.txt || type log.txt && cmd /C exit 1
|
- haxelib install hxcs > log.txt || type log.txt && cmd /C exit 1
|
||||||
|
- haxelib install hx3compat > log.txt || type log.txt && cmd /C exit 1
|
||||||
- haxelib dev hscript .
|
- haxelib dev hscript .
|
||||||
- haxelib list
|
- haxelib list
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
-main Test
|
-main Test
|
||||||
-dce no
|
-dce no
|
||||||
|
-lib hx3compat
|
||||||
+3
-1
@@ -1,3 +1,5 @@
|
|||||||
*.exe
|
*.exe
|
||||||
*.app
|
*.app
|
||||||
flashplayerdebugger
|
flashplayerdebugger
|
||||||
|
LGPL/
|
||||||
|
license.pdf
|
||||||
+6
-9
@@ -1,4 +1,5 @@
|
|||||||
import Sys.*;
|
import Sys.*;
|
||||||
|
import sys.FileSystem.*;
|
||||||
import sys.io.File.*;
|
import sys.io.File.*;
|
||||||
import haxe.*;
|
import haxe.*;
|
||||||
import haxe.io.*;
|
import haxe.io.*;
|
||||||
@@ -7,11 +8,11 @@ class Install {
|
|||||||
// https://www.adobe.com/support/flashplayer/downloads.html
|
// https://www.adobe.com/support/flashplayer/downloads.html
|
||||||
static var fpDownload(default, never) = switch (systemName()) {
|
static var fpDownload(default, never) = switch (systemName()) {
|
||||||
case "Linux":
|
case "Linux":
|
||||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flash_player_sa_linux_debug.x86_64.tar.gz";
|
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flash_player_sa_linux_debug.x86_64.tar.gz";
|
||||||
case "Mac":
|
case "Mac":
|
||||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flashplayer_25_sa_debug.dmg";
|
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.dmg";
|
||||||
case "Windows":
|
case "Windows":
|
||||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flashplayer_25_sa_debug.exe";
|
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.exe";
|
||||||
case _:
|
case _:
|
||||||
throw "unsupported system";
|
throw "unsupported system";
|
||||||
}
|
}
|
||||||
@@ -33,7 +34,7 @@ class Install {
|
|||||||
case "Mac":
|
case "Mac":
|
||||||
"/Library/Application Support/Macromedia/FlashPlayerTrust";
|
"/Library/Application Support/Macromedia/FlashPlayerTrust";
|
||||||
case "Windows":
|
case "Windows":
|
||||||
Path.join([getEnv("SYSTEMROOT"), "system32", "Macromed", "Flash", "FlashPlayerTrust"]);
|
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "#Security", "FlashPlayerTrust"]);
|
||||||
case _:
|
case _:
|
||||||
throw "unsupported system";
|
throw "unsupported system";
|
||||||
}
|
}
|
||||||
@@ -45,12 +46,8 @@ class Install {
|
|||||||
throw "failed to download flash player";
|
throw "failed to download flash player";
|
||||||
if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0)
|
if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0)
|
||||||
throw "failed to extract flash player";
|
throw "failed to extract flash player";
|
||||||
|
deleteFile(Path.withoutDirectory(fpDownload));
|
||||||
case "Mac":
|
case "Mac":
|
||||||
// https://github.com/caskroom/homebrew-cask/pull/15381
|
|
||||||
if (command("brew", ["uninstall", "--force", "brew-cask"]) != 0)
|
|
||||||
throw "failed to brew uninstall --force brew-cask";
|
|
||||||
if (command("brew", ["tap", "caskroom/versions"]) != 0)
|
|
||||||
throw "failed to brew tap caskroom/versions";
|
|
||||||
if (command("brew", ["cask", "install", "flash-player-debugger"]) != 0)
|
if (command("brew", ["cask", "install", "flash-player-debugger"]) != 0)
|
||||||
throw "failed to install flash-player-debugger";
|
throw "failed to install flash-player-debugger";
|
||||||
case "Windows":
|
case "Windows":
|
||||||
|
|||||||
+2
-2
@@ -3,7 +3,7 @@
|
|||||||
"url": "https://github.com/HaxeFoundation/hscript",
|
"url": "https://github.com/HaxeFoundation/hscript",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||||
"version": "2.1.0",
|
"version": "2.3.0",
|
||||||
"releasenote": "added metadata support",
|
"releasenote": "",
|
||||||
"contributors": ["ncannasse"]
|
"contributors": ["ncannasse"]
|
||||||
}
|
}
|
||||||
+2
-1
@@ -1 +1,2 @@
|
|||||||
bin/build-flash.hxml
|
bin/build-each.hxml
|
||||||
|
-hl bin/Test.hl
|
||||||
@@ -0,0 +1,504 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2008-2017 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 hscript;
|
||||||
|
import hscript.Expr;
|
||||||
|
|
||||||
|
class Async {
|
||||||
|
|
||||||
|
var varNames : Array<String>;
|
||||||
|
var currentFun : String;
|
||||||
|
var currentLoop : Expr;
|
||||||
|
var currentBreak : Expr -> Expr;
|
||||||
|
var uid = 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
inline static function expr( e : Expr ) {
|
||||||
|
return #if hscriptPos e.e #else e #end;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static function mk( e, inf : Expr ) : Expr {
|
||||||
|
return #if hscriptPos { e : e, pmin : inf.pmin, pmax : inf.pmax, origin : inf.origin, line : inf.line } #else e #end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convert a script into asynchronous one.
|
||||||
|
- calls such as foo(a,b,c) are translated to a_foo(function(r) ...rest, a,b,c) where r is the result value
|
||||||
|
- object access such obj.bar(a,b,c) are translated to obj.a_bar(function(r) ...rest, a, b, c)
|
||||||
|
- @async expr will execute the expression but continue without waiting for it to finish
|
||||||
|
- @split [ e1, e2, e3 ] is transformed to split(function(_) ...rest, [e1, e2, e3]) which
|
||||||
|
should execute asynchronously all expressions - until they return - before continuing the execution
|
||||||
|
- for(i in v) block; loops are translated to the following:
|
||||||
|
var _i = makeIterator(v);
|
||||||
|
function _loop() {
|
||||||
|
if( !_i.hasNext() ) return;
|
||||||
|
var v = _i.next();
|
||||||
|
block(function(_) _loop());
|
||||||
|
}
|
||||||
|
_loop()
|
||||||
|
- while loops are translated similar to for loops
|
||||||
|
- break and continue are correctly handled
|
||||||
|
- you can use @sync <expr> to disable async transformation in some code parts (for performance reason)
|
||||||
|
- a few expressions are still not supported (complex calls, try/catch, and a few others)
|
||||||
|
|
||||||
|
In these examples ...rest represents the continuation of execution of the script after the expression
|
||||||
|
**/
|
||||||
|
public static function toAsync( e : Expr, topLevelSync = false ) {
|
||||||
|
var a = new Async();
|
||||||
|
return a.build(e, topLevelSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function build( e : Expr, topLevelSync = false ) {
|
||||||
|
if( topLevelSync ) {
|
||||||
|
return buildSync(e,null);
|
||||||
|
} else {
|
||||||
|
var nothing = ignore();
|
||||||
|
return new Async().toCps(e, nothing, nothing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSync( e : Expr, exit : Expr ) : Expr {
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EFunction(_,_,name,_):
|
||||||
|
if( name != null )
|
||||||
|
return toCps(e, null, null);
|
||||||
|
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:
|
||||||
|
}
|
||||||
|
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 EBreak if( currentBreak != null ):
|
||||||
|
return currentBreak(e);
|
||||||
|
case EContinue if( currentLoop != null ):
|
||||||
|
return block([retNull(currentLoop, e), mk(EReturn(),e)],e);
|
||||||
|
case EFor(_), EWhile(_):
|
||||||
|
var oldLoop = currentLoop, oldBreak = currentBreak;
|
||||||
|
currentLoop = null;
|
||||||
|
currentBreak = null;
|
||||||
|
e = Tools.map(e, buildSync.bind(_, exit));
|
||||||
|
currentLoop = oldLoop;
|
||||||
|
currentBreak = oldBreak;
|
||||||
|
return e;
|
||||||
|
case EReturn(eret) if( exit != null ):
|
||||||
|
return block([eret == null ? retNull(exit, e) : call(exit,[eret], e), mk(EReturn(),e)], e);
|
||||||
|
default:
|
||||||
|
return Tools.map(e, buildSync.bind(_, exit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
varNames = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function ignore(?e) : Expr {
|
||||||
|
var inf = e == null ? nullExpr : e;
|
||||||
|
return fun("_", block(e == null ? [] : [e],inf));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function ident(str, e) {
|
||||||
|
return mk(EIdent(str), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function fun(arg:String, e, ?name) {
|
||||||
|
return mk(EFunction([{ name : arg, t : null }], e, name), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function funs(arg:Array<String>, e, ?name) {
|
||||||
|
return mk(EFunction([for( a in arg ) { name : a, t : null }], e, name), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function block(arr:Array<Expr>, e) {
|
||||||
|
if( arr.length == 1 && expr(arr[0]).match(EBlock(_)) )
|
||||||
|
return arr[0];
|
||||||
|
return mk(EBlock(arr), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function field(e, f, inf) {
|
||||||
|
return mk(EField(e, f), inf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function binop(op, e1, e2, inf) {
|
||||||
|
return mk(EBinop(op, e1, e2), inf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function call(e, args, inf) {
|
||||||
|
return mk(ECall(e, args), inf);
|
||||||
|
}
|
||||||
|
|
||||||
|
function retNull(e:Expr,?pos) : Expr {
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EFunction([{name:"_"}], e, _, _): return e;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return call(e, [nullId], pos == null ? e : pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCall( ecall, args : Array<Expr>, rest : Expr, exit, sync = false ) {
|
||||||
|
var names = [for( i in 0...args.length ) "_a"+uid++];
|
||||||
|
var rargs = [for( i in 0...args.length ) ident(names[i],ecall)];
|
||||||
|
if( !sync )
|
||||||
|
rargs.unshift(rest);
|
||||||
|
var rest = mk(sync ? ECall(rest,[call(ecall, rargs,ecall)]) : ECall(ecall, rargs), ecall);
|
||||||
|
var i = args.length - 1;
|
||||||
|
while( i >= 0 ) {
|
||||||
|
rest = toCps(args[i], fun(names[i], rest), exit);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
var syncFlag : Bool;
|
||||||
|
|
||||||
|
function isSync( e : Expr ) {
|
||||||
|
syncFlag = true;
|
||||||
|
checkSync(e);
|
||||||
|
return syncFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkSync( e : Expr ) {
|
||||||
|
if( !syncFlag )
|
||||||
|
return;
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case ECall(_):
|
||||||
|
syncFlag = false;
|
||||||
|
case EFunction(_,_,name,_) if( name != null ):
|
||||||
|
syncFlag = false;
|
||||||
|
case EMeta("sync" | "async", _, _):
|
||||||
|
// isolated from the sync part
|
||||||
|
default:
|
||||||
|
Tools.iter(e, checkSync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveVars() {
|
||||||
|
return varNames.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreVars(k) {
|
||||||
|
while( varNames.length > k ) varNames.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toCps( e : Expr, rest : Expr, exit : Expr ) : Expr {
|
||||||
|
if( isSync(e) )
|
||||||
|
return call(rest, [buildSync(e, exit)],e);
|
||||||
|
switch( expr(e) ) {
|
||||||
|
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:
|
||||||
|
}
|
||||||
|
while( el.length > 0 ) {
|
||||||
|
var e = toCps(el.pop(), rest, exit);
|
||||||
|
rest = ignore(e);
|
||||||
|
}
|
||||||
|
restoreVars(vold);
|
||||||
|
return retNull(rest);
|
||||||
|
case EFunction(args, body, name, t):
|
||||||
|
var vold = saveVars();
|
||||||
|
if( name != null )
|
||||||
|
varNames.push(name);
|
||||||
|
for( a in args )
|
||||||
|
varNames.push(a.name);
|
||||||
|
args.unshift( { name : "_onEnd", t : null } );
|
||||||
|
var frest = ident("_onEnd",e);
|
||||||
|
var oldFun = currentFun;
|
||||||
|
currentFun = name;
|
||||||
|
var body = toCps(body, frest, frest);
|
||||||
|
var f = mk(EFunction(args, body, name, t),e);
|
||||||
|
restoreVars(vold);
|
||||||
|
return rest == null ? f : call(rest, [f],e);
|
||||||
|
case EParent(e):
|
||||||
|
return mk(EParent(toCps(e, rest, exit)),e);
|
||||||
|
case EMeta("sync", _, e):
|
||||||
|
return call(rest,[buildSync(e,exit)],e);
|
||||||
|
case EMeta("async", _, e):
|
||||||
|
var nothing = ignore();
|
||||||
|
return block([toCps(e,nothing,nothing),retNull(rest)],e);
|
||||||
|
case EMeta("split", _, e):
|
||||||
|
var args = switch( expr(e) ) { case EArrayDecl(el): el; default: throw "@split expression should be an array"; };
|
||||||
|
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);
|
||||||
|
case ECall(expr(_) => EField(e, f), args):
|
||||||
|
return makeCall(field(e,"a_"+f,e), args, rest, exit);
|
||||||
|
case EFor(v, eit, eloop):
|
||||||
|
var id = ++uid;
|
||||||
|
var it = ident("_i" + id,e);
|
||||||
|
var oldLoop = currentLoop, oldBreak = currentBreak;
|
||||||
|
var loop = ident("_loop" + id,e);
|
||||||
|
currentLoop = loop;
|
||||||
|
currentBreak = function(inf) return block([retNull(rest, inf), mk(EReturn(),inf)], inf);
|
||||||
|
var efor = block([
|
||||||
|
mk(EVar("_i" + id, call(ident("makeIterator",eit),[eit],eit)),eit),
|
||||||
|
fun("_", block([
|
||||||
|
mk(EIf(mk(EUnop("!", true, call( field(it, "hasNext", it), [], it)),it), currentBreak(it)),it),
|
||||||
|
mk(EVar(v, call(field(it, "next",it), [], it)), it),
|
||||||
|
toCps(eloop, loop, exit),
|
||||||
|
], it),"_loop" + id),
|
||||||
|
retNull(loop, e),
|
||||||
|
], e);
|
||||||
|
currentLoop = oldLoop;
|
||||||
|
currentBreak = oldBreak;
|
||||||
|
return efor;
|
||||||
|
case EUnop(op = "!", prefix, eop):
|
||||||
|
return toCps(eop, fun("_r",call(rest, [mk(EUnop(op, prefix, ident("_r",e)),e)], e)), exit);
|
||||||
|
case EBinop(op, e1, e2):
|
||||||
|
switch( op ) {
|
||||||
|
case "=", "+=", "-=", "/=", "*=", "%=", "&=", "|=", "^=":
|
||||||
|
switch( expr(e1) ) {
|
||||||
|
case EIdent(_):
|
||||||
|
var id = "_r" + uid++;
|
||||||
|
return toCps(e2, fun(id, call(rest, [binop(op, e1, ident(id,e1),e1)], e1)), exit);
|
||||||
|
case EField(ef1, f):
|
||||||
|
var id1 = "_r" + uid++;
|
||||||
|
var id2 = "_r" + uid++;
|
||||||
|
return toCps(ef1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, field(ident(id1, e1), f, ef1), ident(id2, e2), e)], e)), exit)), exit);
|
||||||
|
case EArray(earr, eindex):
|
||||||
|
var idArr = "_r" + uid++;
|
||||||
|
var idIndex = "_r" + uid++;
|
||||||
|
var idVal = "_r" + uid++;
|
||||||
|
return toCps(earr,fun(idArr, toCps(eindex, fun(idIndex, toCps(e2,
|
||||||
|
fun(idVal, call(rest, [binop(op, mk(EArray(ident(idArr,earr), ident(idIndex,eindex)),e1), ident(idVal,e1), e)], e))
|
||||||
|
, exit)), exit)),exit);
|
||||||
|
default:
|
||||||
|
throw "assert " + e1;
|
||||||
|
}
|
||||||
|
case "||":
|
||||||
|
var id1 = "_r" + uid++;
|
||||||
|
var id2 = "_r" + uid++;
|
||||||
|
return toCps(e1, fun(id1, mk(EIf(binop("==", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("true",e1)],e1),toCps(e2, rest, exit)),e)), exit);
|
||||||
|
case "&&":
|
||||||
|
var id1 = "_r" + uid++;
|
||||||
|
var id2 = "_r" + uid++;
|
||||||
|
return toCps(e1, fun(id1, mk(EIf(binop("!=", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("false",e1)],e1),toCps(e2, rest, exit)),e)), exit);
|
||||||
|
default:
|
||||||
|
var id1 = "_r" + uid++;
|
||||||
|
var id2 = "_r" + uid++;
|
||||||
|
return toCps(e1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, ident(id1,e1), ident(id2,e2), e)], e)), exit)), exit);
|
||||||
|
}
|
||||||
|
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
|
||||||
|
return toCps(cond, fun("_c", mk(EIf(ident("_c",cond), toCps(e1, rest, exit), e2 == null ? retNull(rest) : toCps(e2, rest, exit)),e)), exit);
|
||||||
|
case EWhile(cond, ewh):
|
||||||
|
var id = ++uid;
|
||||||
|
var loop = ident("_loop" + id, cond);
|
||||||
|
var oldLoop = currentLoop, oldBreak = currentBreak;
|
||||||
|
currentLoop = loop;
|
||||||
|
currentBreak = function(e) return block([retNull(rest,e), mk(EReturn(),e)],e);
|
||||||
|
var ewhile = block([
|
||||||
|
fun("_r",
|
||||||
|
toCps(cond, fun("_c", mk(EIf(ident("_c", cond), toCps(ewh, loop, exit), retNull(rest,cond)),cond)), exit)
|
||||||
|
, "_loop"+id),
|
||||||
|
retNull(loop, cond),
|
||||||
|
],e);
|
||||||
|
currentLoop = oldLoop;
|
||||||
|
currentBreak = oldBreak;
|
||||||
|
return ewhile;
|
||||||
|
case EReturn(eret):
|
||||||
|
return eret == null ? retNull(exit, e) : toCps(eret, exit, exit);
|
||||||
|
case EObject(fields):
|
||||||
|
var id = "_o" + uid++;
|
||||||
|
var rest = call(rest, [ident(id,e)], e);
|
||||||
|
fields.reverse();
|
||||||
|
for( f in fields )
|
||||||
|
rest = toCps(f.e, fun("_r", block([
|
||||||
|
binop("=", mk(EField(ident(id,f.e), f.name),f.e), ident("_r",f.e), f.e),
|
||||||
|
rest,
|
||||||
|
],f.e)),exit);
|
||||||
|
return block([
|
||||||
|
mk(EVar(id, mk(EObject([]),e)),e),
|
||||||
|
rest,
|
||||||
|
],e);
|
||||||
|
case EArrayDecl(el):
|
||||||
|
var id = "_a" + uid++;
|
||||||
|
var rest = call(rest, [ident(id,e)], e);
|
||||||
|
var i = el.length - 1;
|
||||||
|
while( i >= 0 ) {
|
||||||
|
var e = el[i];
|
||||||
|
rest = toCps(e, fun("_r", block([
|
||||||
|
binop("=", mk(EArray(ident(id,e), mk(EConst(CInt(i)),e)),e), ident("_r",e), e),
|
||||||
|
rest,
|
||||||
|
],e)), exit);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
return block([
|
||||||
|
mk(EVar(id, mk(EArrayDecl([]),e)),e),
|
||||||
|
rest,
|
||||||
|
],e);
|
||||||
|
case EArray(earr, eindex):
|
||||||
|
var id1 = "_r" + uid++;
|
||||||
|
var id2 = "_r" + uid++;
|
||||||
|
return toCps(earr, fun(id1, toCps(eindex, fun(id2, call(rest, [mk(EArray(ident(id1,e), ident(id2,e)),e)], e)), exit)), exit);
|
||||||
|
case EVar(v, t, ev):
|
||||||
|
if( ev == null )
|
||||||
|
return block([e, retNull(rest, e)], e);
|
||||||
|
return block([
|
||||||
|
mk(EVar(v, t),e),
|
||||||
|
toCps(ev, fun("_r", block([binop("=", ident(v,e), ident("_r",e), e), retNull(rest,e)], e)), exit),
|
||||||
|
],e);
|
||||||
|
case EConst(_), EIdent(_), EUnop(_), EField(_):
|
||||||
|
return call(rest, [e], e);
|
||||||
|
case ENew(cl, args):
|
||||||
|
var names = [for( i in 0...args.length ) "_a"+uid++];
|
||||||
|
var rargs = [for( i in 0...args.length ) ident(names[i], args[i])];
|
||||||
|
var rest = call(rest,[mk(ENew(cl, rargs),e)],e);
|
||||||
|
var i = args.length - 1;
|
||||||
|
while( i >= 0 ) {
|
||||||
|
rest = toCps(args[i], fun(names[i], rest), exit);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
return rest;
|
||||||
|
case EBreak:
|
||||||
|
if( currentBreak == null ) throw "Break outside loop";
|
||||||
|
return currentBreak(e);
|
||||||
|
case EContinue:
|
||||||
|
if( currentLoop == null ) throw "Continue outside loop";
|
||||||
|
return block([retNull(currentLoop, e), mk(EReturn(),e)], e);
|
||||||
|
case ESwitch(v, cases, def):
|
||||||
|
var cases = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
|
||||||
|
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 EDoWhile(_), ETry(_), ECall(_):
|
||||||
|
default:
|
||||||
|
throw "Unsupported async expression " + Printer.toString(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncInterp extends Interp {
|
||||||
|
|
||||||
|
public function setContext( api : Dynamic ) {
|
||||||
|
|
||||||
|
var funs = new Array();
|
||||||
|
for( v in variables.keys() )
|
||||||
|
if( Reflect.isFunction(variables.get(v)) )
|
||||||
|
funs.push({ v : v, obj : null });
|
||||||
|
|
||||||
|
variables.set("split", split);
|
||||||
|
variables.set("makeIterator", makeIterator);
|
||||||
|
|
||||||
|
var c = Type.getClass(api);
|
||||||
|
for( f in (c == null ? Reflect.fields(api) : Type.getInstanceFields(c)) ) {
|
||||||
|
var fv = Reflect.field(api, f);
|
||||||
|
if( !Reflect.isFunction(fv) ) continue;
|
||||||
|
if( f.charCodeAt(0) == "_".code ) f = f.substr(1);
|
||||||
|
variables.set(f, fv);
|
||||||
|
// create the async wrapper if doesn't exists
|
||||||
|
if( f.substr(0, 2) != "a_" )
|
||||||
|
funs.push({ v : f, obj : api });
|
||||||
|
}
|
||||||
|
|
||||||
|
for( v in funs ) {
|
||||||
|
if( variables.exists("a_" + v.v) ) continue;
|
||||||
|
var fv : Dynamic = variables.get(v.v);
|
||||||
|
var obj = v.obj;
|
||||||
|
variables.set("a_" + v.v, Reflect.makeVarArgs(function(args:Array<Dynamic>) {
|
||||||
|
var onEnd = args.shift();
|
||||||
|
onEnd(Reflect.callMethod(obj, fv, args));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasMethod( name : String ) {
|
||||||
|
var v = variables.get(name);
|
||||||
|
return v != null && Reflect.isFunction(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callValue( value : Dynamic, args : Array<Dynamic>, ?onResult : Dynamic -> Void, ?vthis : {} ) {
|
||||||
|
var oldThis = variables.get("this");
|
||||||
|
if( vthis != null )
|
||||||
|
variables.set("this", vthis);
|
||||||
|
if( onResult == null )
|
||||||
|
onResult = function(_) {};
|
||||||
|
args.unshift(onResult);
|
||||||
|
Reflect.callMethod(null, value, args);
|
||||||
|
variables.set("this", oldThis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callAsync( id : String, args, ?onResult, ?vthis : {} ) {
|
||||||
|
var v = variables.get(id);
|
||||||
|
if( v == null )
|
||||||
|
throw "Missing function " + id + "()";
|
||||||
|
callValue(v, args, onResult, vthis);
|
||||||
|
}
|
||||||
|
|
||||||
|
function split( rest : Dynamic -> Void, args : Array<Dynamic> ) {
|
||||||
|
if( args.length == 0 )
|
||||||
|
rest(null);
|
||||||
|
else {
|
||||||
|
var count = args.length;
|
||||||
|
function next(_) {
|
||||||
|
if( --count == 0 ) rest(null);
|
||||||
|
}
|
||||||
|
for( a in args )
|
||||||
|
a(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override function fcall( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
|
||||||
|
var m = Reflect.field(o, f);
|
||||||
|
if( m == null ) {
|
||||||
|
if( f.substr(0, 2) == "a_" ) {
|
||||||
|
m = Reflect.field(o, f.substr(2));
|
||||||
|
// fallback on sync version
|
||||||
|
if( m != null ) {
|
||||||
|
var onEnd = args.shift();
|
||||||
|
onEnd(call(o, m, args));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// fallback on generic script
|
||||||
|
m = Reflect.field(o, "scriptCall");
|
||||||
|
if( m != null ) {
|
||||||
|
call(o, m, [args.shift(), f.substr(2), args]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fallback on generic script
|
||||||
|
m = Reflect.field(o, "scriptCall");
|
||||||
|
if( m != null ) {
|
||||||
|
var result : Dynamic = null;
|
||||||
|
call(o, m, [function(r) result = r, f, args]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error(ECustom(o + " has no method " + f));
|
||||||
|
}
|
||||||
|
return call(o, m, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+37
-25
@@ -1,26 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Nicolas Cannasse
|
* Copyright (C)2008-2017 Haxe Foundation
|
||||||
* All rights reserved.
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
*
|
||||||
* - Redistributions of source code must retain the above copyright
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* - Redistributions in binary form must reproduce the above copyright
|
* to deal in the Software without restriction, including without limitation
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
* documentation and/or other materials provided with the distribution.
|
* 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:
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
* all copies or substantial portions of the Software.
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
*
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
* DEALINGS IN THE SOFTWARE.
|
||||||
* DAMAGE.
|
|
||||||
*/
|
*/
|
||||||
package hscript;
|
package hscript;
|
||||||
import hscript.Expr;
|
import hscript.Expr;
|
||||||
@@ -75,10 +72,7 @@ class Bytes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function doEncodeInt(v: Int) {
|
function doEncodeInt(v: Int) {
|
||||||
bout.addByte(v & 0xFF);
|
bout.addInt32(v);
|
||||||
bout.addByte((v >> 8) & 0xFF);
|
|
||||||
bout.addByte((v >> 16) & 0xFF);
|
|
||||||
bout.addByte(v >>> 24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function doEncodeConst( c : Const ) {
|
function doEncodeConst( c : Const ) {
|
||||||
@@ -110,7 +104,7 @@ class Bytes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function doDecodeInt() {
|
function doDecodeInt() {
|
||||||
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16) | (bin.get(pin+3) << 24);
|
var i = bin.getInt32(pin);
|
||||||
pin += 4;
|
pin += 4;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -247,11 +241,22 @@ class Bytes {
|
|||||||
}
|
}
|
||||||
bout.addByte(255);
|
bout.addByte(255);
|
||||||
if( def == null ) bout.addByte(255) else doEncode(def);
|
if( def == null ) bout.addByte(255) else doEncode(def);
|
||||||
|
case EMeta(name,args,e):
|
||||||
|
doEncodeString(name);
|
||||||
|
bout.addByte(args == null ? 0 : args.length + 1);
|
||||||
|
if( args != null ) for( e in args ) doEncode(e);
|
||||||
|
doEncode(e);
|
||||||
|
case ECheckType(e,_):
|
||||||
|
doEncode(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function doDecode() : Expr {
|
function doDecode() : Expr {
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
|
if (bin.get(pin) == 255) {
|
||||||
|
pin++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var origin = doDecodeString();
|
var origin = doDecodeString();
|
||||||
var line = doDecodeInt();
|
var line = doDecodeInt();
|
||||||
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
|
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
|
||||||
@@ -366,6 +371,13 @@ class Bytes {
|
|||||||
case 24:
|
case 24:
|
||||||
var cond = doDecode();
|
var cond = doDecode();
|
||||||
EDoWhile(cond,doDecode());
|
EDoWhile(cond,doDecode());
|
||||||
|
case 25:
|
||||||
|
var name = doDecodeString();
|
||||||
|
var count = bin.get(pin++);
|
||||||
|
var args = count == 0 ? null : [for( i in 0...count - 1 ) doDecode()];
|
||||||
|
EMeta(name, args, doDecode());
|
||||||
|
case 26:
|
||||||
|
ECheckType(doDecode(), CTPath(["Void"]));
|
||||||
case 255:
|
case 255:
|
||||||
null;
|
null;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -0,0 +1,940 @@
|
|||||||
|
package hscript;
|
||||||
|
import hscript.Expr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This is a special type that can be used in API.
|
||||||
|
It will be type-checked as `Script` but will compile/execute as `Real`
|
||||||
|
**/
|
||||||
|
typedef TypeCheck<Real,Script> = Real;
|
||||||
|
|
||||||
|
enum TType {
|
||||||
|
TMono( r : { r : TType } );
|
||||||
|
TVoid;
|
||||||
|
TInt;
|
||||||
|
TFloat;
|
||||||
|
TBool;
|
||||||
|
TDynamic;
|
||||||
|
TParam( name : String );
|
||||||
|
TUnresolved( name : String );
|
||||||
|
TNull( t : TType );
|
||||||
|
TInst( c : CClass, args : Array<TType> );
|
||||||
|
TEnum( e : CEnum, args : Array<TType> );
|
||||||
|
TType( t : CTypedef, args : Array<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 }> );
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum WithType {
|
||||||
|
NoValue;
|
||||||
|
Value;
|
||||||
|
WithType( t : TType );
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CTypedecl {
|
||||||
|
CTClass( c : CClass );
|
||||||
|
CTEnum( e : CEnum );
|
||||||
|
CTTypedef( t : CTypedef );
|
||||||
|
CTAlias( t : TType );
|
||||||
|
CTAbstract( a : CAbstract );
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CNamedType = {
|
||||||
|
var name : String;
|
||||||
|
var params : Array<TType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CClass = {> CNamedType,
|
||||||
|
@:optional var superClass : TType;
|
||||||
|
@:optional var constructor : CField;
|
||||||
|
var fields : Map<String,CField>;
|
||||||
|
var statics : Map<String,CField>;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CField = {
|
||||||
|
var isPublic : Bool;
|
||||||
|
var params : Array<TType>;
|
||||||
|
var name : String;
|
||||||
|
var t : TType;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CEnum = {> CNamedType,
|
||||||
|
var constructors : Map<String,TType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CTypedef = {> CNamedType,
|
||||||
|
var t : TType;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef CAbstract = {> CNamedType,
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(hscript.Checker)
|
||||||
|
class CheckerTypes {
|
||||||
|
|
||||||
|
var types : Map<String,CTypedecl> = new Map();
|
||||||
|
var t_string : TType;
|
||||||
|
var localParams : Map<String,TType>;
|
||||||
|
|
||||||
|
public function new() {
|
||||||
|
types = new Map();
|
||||||
|
types.set("Void",CTAlias(TVoid));
|
||||||
|
types.set("Int",CTAlias(TInt));
|
||||||
|
types.set("Float",CTAlias(TFloat));
|
||||||
|
types.set("Bool",CTAlias(TBool));
|
||||||
|
types.set("Dynamic",CTAlias(TDynamic));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addXmlApi( api : Xml ) {
|
||||||
|
var types = new haxe.rtti.XmlParser();
|
||||||
|
types.process(api, "");
|
||||||
|
var todo = [];
|
||||||
|
for( v in types.root )
|
||||||
|
addXmlType(v,todo);
|
||||||
|
for( f in todo )
|
||||||
|
f();
|
||||||
|
t_string = getType("String");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addXmlType(x:haxe.rtti.CType.TypeTree,todo:Array<Void->Void>) {
|
||||||
|
switch (x) {
|
||||||
|
case TPackage(name, full, subs):
|
||||||
|
for( s in subs ) addXmlType(s,todo);
|
||||||
|
case TClassdecl(c):
|
||||||
|
if( types.exists(c.path) ) return;
|
||||||
|
var cl : CClass = {
|
||||||
|
name : c.path,
|
||||||
|
params : [],
|
||||||
|
fields : new Map(),
|
||||||
|
statics : new Map(),
|
||||||
|
};
|
||||||
|
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)]);
|
||||||
|
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" ) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( skip ) continue;
|
||||||
|
var fl : CField = { isPublic : f.isPublic, params : [], name : f.name, t : null };
|
||||||
|
for( p in f.params ) {
|
||||||
|
var pt = TParam(p);
|
||||||
|
var key = f.name+"."+p;
|
||||||
|
pkeys.push(key);
|
||||||
|
fl.params.push(pt);
|
||||||
|
localParams.set(key, pt);
|
||||||
|
}
|
||||||
|
fl.t = makeXmlType(f.type);
|
||||||
|
while( pkeys.length > 0 )
|
||||||
|
localParams.remove(pkeys.pop());
|
||||||
|
if( fl.name == "new" )
|
||||||
|
cl.constructor = fl;
|
||||||
|
else
|
||||||
|
cl.fields.set(f.name, fl);
|
||||||
|
}
|
||||||
|
localParams = null;
|
||||||
|
});
|
||||||
|
types.set(cl.name, CTClass(cl));
|
||||||
|
case TEnumdecl(e):
|
||||||
|
if( types.exists(e.path) ) return;
|
||||||
|
var en : CEnum = {
|
||||||
|
name : e.path,
|
||||||
|
params : [],
|
||||||
|
constructors: new Map(),
|
||||||
|
};
|
||||||
|
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];
|
||||||
|
localParams = null;
|
||||||
|
});
|
||||||
|
types.set(en.name, CTEnum(en));
|
||||||
|
case TTypedecl(t):
|
||||||
|
if( types.exists(t.path) ) return;
|
||||||
|
var td : CTypedef = {
|
||||||
|
name : t.path,
|
||||||
|
params : [],
|
||||||
|
t : null,
|
||||||
|
};
|
||||||
|
for( p in t.params )
|
||||||
|
td.params.push(TParam(p));
|
||||||
|
if( t.path == "hscript.TypeCheck" )
|
||||||
|
td.params.reverse();
|
||||||
|
todo.push(function() {
|
||||||
|
localParams = [for( pt in td.params ) t.path+"."+Checker.typeStr(pt) => pt];
|
||||||
|
td.t = makeXmlType(t.type);
|
||||||
|
localParams = null;
|
||||||
|
});
|
||||||
|
types.set(t.path, CTTypedef(td));
|
||||||
|
case TAbstractdecl(a):
|
||||||
|
if( types.exists(a.path) ) return;
|
||||||
|
var ta : CAbstract = {
|
||||||
|
name : a.path,
|
||||||
|
params : [],
|
||||||
|
};
|
||||||
|
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];
|
||||||
|
localParams = null;
|
||||||
|
});
|
||||||
|
types.set(a.path, CTAbstract(ta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeXmlType( t : haxe.rtti.CType.CType ) : TType {
|
||||||
|
return switch (t) {
|
||||||
|
case CUnknown: TUnresolved("Unknown");
|
||||||
|
case CEnum(name, params): getType(name,[for( t in params ) makeXmlType(t)]);
|
||||||
|
case CClass(name, params): getType(name,[for( t in params ) makeXmlType(t)]);
|
||||||
|
case CTypedef(name, params): getType(name,[for( t in params ) makeXmlType(t)]);
|
||||||
|
case CFunction(args, ret): TFun([for( a in args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }], makeXmlType(ret));
|
||||||
|
case CAnonymous(fields):
|
||||||
|
inline function isOpt(m:haxe.rtti.CType.MetaData) {
|
||||||
|
if( m == null ) return false;
|
||||||
|
var b = false;
|
||||||
|
for( m in m ) if( m.name == ":optional" ) { b = true; break; }
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
TAnon([for( f in fields ) { name : f.name, t : makeXmlType(f.type), opt : isOpt(f.meta) }]);
|
||||||
|
case CDynamic(t): TDynamic;
|
||||||
|
case CAbstract(name, params):
|
||||||
|
switch( name ) {
|
||||||
|
default:
|
||||||
|
getType(name,[for( t in params ) makeXmlType(t)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getType( name : String, ?args : Array<TType> ) : TType {
|
||||||
|
if( localParams != null ) {
|
||||||
|
var t = localParams.get(name);
|
||||||
|
if( t != null ) return t;
|
||||||
|
}
|
||||||
|
var t = resolve(name,args);
|
||||||
|
if( t == null )
|
||||||
|
return TUnresolved(name); // most likely private class
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resolve( name : String, ?args : Array<TType> ) : TType {
|
||||||
|
if( name == "Null" ) {
|
||||||
|
if( args == null || args.length != 1 ) throw "Missing Null<T> parameter";
|
||||||
|
return TNull(args[0]);
|
||||||
|
}
|
||||||
|
var t = types.get(name);
|
||||||
|
if( t == null ) return null;
|
||||||
|
if( args == null ) args = [];
|
||||||
|
return switch( t ) {
|
||||||
|
case CTClass(c): TInst(c,args);
|
||||||
|
case CTEnum(e): TEnum(e,args);
|
||||||
|
case CTTypedef(t): TType(t,args);
|
||||||
|
case CTAbstract(a): TAbstract(a, args);
|
||||||
|
case CTAlias(t): t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Checker {
|
||||||
|
|
||||||
|
public var types : CheckerTypes;
|
||||||
|
var locals : Map<String,TType>;
|
||||||
|
var globals : Map<String,TType> = new Map();
|
||||||
|
var events : Map<String,TType> = new Map();
|
||||||
|
public var allowAsync : Bool;
|
||||||
|
public var allowReturn : Null<TType>;
|
||||||
|
|
||||||
|
public function new( ?types ) {
|
||||||
|
if( types == null ) types = new CheckerTypes();
|
||||||
|
this.types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGlobal( name : String, type : TType ) {
|
||||||
|
globals.set(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setEvent( name : String, type : TType ) {
|
||||||
|
events.set(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGlobals() {
|
||||||
|
return globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check( expr : Expr, ?withType : WithType ) {
|
||||||
|
if( withType == null ) withType = NoValue;
|
||||||
|
locals = new Map();
|
||||||
|
return typeExpr(expr,withType);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function edef( e : Expr ) {
|
||||||
|
#if hscriptPos
|
||||||
|
return e.e;
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveLocals() {
|
||||||
|
return [for( k in locals.keys() ) k => locals.get(k)];
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return ct;
|
||||||
|
case CTFun(args, ret):
|
||||||
|
var i = 0;
|
||||||
|
return TFun([for( a in args ) { name : "p"+(i++), opt : false, t : makeType(a,e) }], makeType(ret,e));
|
||||||
|
case CTAnon(fields):
|
||||||
|
return TAnon([for( f in fields ) { name : f.name, opt : false, t : makeType(f.t,e) }]);
|
||||||
|
case CTParent(t):
|
||||||
|
return makeType(t,e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function typeStr( t : TType ) {
|
||||||
|
inline function makeArgs(args:Array<TType>) return args.length==0 ? "" : "<"+[for( t in args ) typeStr(t)].join(",")+">";
|
||||||
|
return switch (t) {
|
||||||
|
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 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(", ")+"}";
|
||||||
|
case TParam(name): name;
|
||||||
|
case TNull(t): "Null<"+typeStr(t)+">";
|
||||||
|
case TUnresolved(name): "?"+name;
|
||||||
|
default: t.getName().substr(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function linkLoop( a : TType, t : TType ) {
|
||||||
|
if( t == a ) return true;
|
||||||
|
switch( t ) {
|
||||||
|
case TMono(r):
|
||||||
|
if( r.r == null ) return false;
|
||||||
|
return linkLoop(a,r.r);
|
||||||
|
case TEnum(_,tl), TInst(_,tl), TType(_,tl), TAbstract(_,tl):
|
||||||
|
for( t in tl )
|
||||||
|
if( linkLoop(a,t) )
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
case TFun(args,ret):
|
||||||
|
for( arg in args )
|
||||||
|
if( linkLoop(a,arg.t) )
|
||||||
|
return true;
|
||||||
|
return linkLoop(a,ret);
|
||||||
|
case TDynamic:
|
||||||
|
if( t == TDynamic )
|
||||||
|
return false;
|
||||||
|
return linkLoop(a,TDynamic);
|
||||||
|
case TAnon(fl):
|
||||||
|
for( f in fl )
|
||||||
|
if( linkLoop(a, f.t) )
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function link( a : TType, b : TType, r : { r : TType } ) {
|
||||||
|
if( linkLoop(a,b) )
|
||||||
|
return follow(b) == a;
|
||||||
|
if( b == TDynamic )
|
||||||
|
return true;
|
||||||
|
r.r = b;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeEq( t1 : TType, t2 : TType ) {
|
||||||
|
if( t1 == t2 )
|
||||||
|
return true;
|
||||||
|
switch( [t1,t2] ) {
|
||||||
|
case [TMono(r), _]:
|
||||||
|
if( r.r == null ) {
|
||||||
|
if( !link(t1,t2,r) )
|
||||||
|
return false;
|
||||||
|
r.r = t2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return typeEq(r.r, t2);
|
||||||
|
case [_, TMono(r)]:
|
||||||
|
if( r.r == null ) {
|
||||||
|
if( !link(t2,t1,r) )
|
||||||
|
return false;
|
||||||
|
r.r = t1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return typeEq(t1, r.r);
|
||||||
|
case [TType(t1,pl1),TType(t2,pl2)] if( t1 == t2 ):
|
||||||
|
for( i in 0...pl1.length )
|
||||||
|
if( !typeEq(pl1[i],pl2[i]) )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
case [TType(t1,pl1), _]:
|
||||||
|
return typeEq(apply(t1.t, t1.params, pl1), t2);
|
||||||
|
case [_,TType(t2,pl2)]:
|
||||||
|
return typeEq(t1, apply(t2.t, t2.params, pl2));
|
||||||
|
case [TInst(cl1,pl1), TInst(cl2,pl2)] if( cl1 == cl2 ):
|
||||||
|
for( i in 0...pl1.length )
|
||||||
|
if( !typeEq(pl1[i],pl2[i]) )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
case [TEnum(e1,pl1), TEnum(e2,pl2)] if( e1 == e2 ):
|
||||||
|
for( i in 0...pl1.length )
|
||||||
|
if( !typeEq(pl1[i],pl2[i]) )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
case [TAbstract(a1,pl1), TAbstract(a2,pl2)] if( a1 == a2 ):
|
||||||
|
for( i in 0...pl1.length )
|
||||||
|
if( !typeEq(pl1[i],pl2[i]) )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
case [TNull(t1), TNull(t2)]:
|
||||||
|
return typeEq(t1,t2);
|
||||||
|
case [TNull(t1), _]:
|
||||||
|
return typeEq(t1,t2);
|
||||||
|
case [_, TNull(t2)]:
|
||||||
|
return typeEq(t1,t2);
|
||||||
|
case [TFun(args1,r1), TFun(args2,r2)] if( args1.length == args2.length ):
|
||||||
|
for( i in 0...args1.length )
|
||||||
|
if( !typeEq(args1[i].t, args2[i].t) )
|
||||||
|
return false;
|
||||||
|
return typeEq(r1, r2);
|
||||||
|
case [TAnon(a1),TAnon(a2)] if( a1.length == a2.length ):
|
||||||
|
var m = new Map();
|
||||||
|
for( f in a2 )
|
||||||
|
m.set(f.name, f);
|
||||||
|
for( f1 in a1 ) {
|
||||||
|
var f2 = m.get(f1.name);
|
||||||
|
if( f2 == null ) return false;
|
||||||
|
if( !typeEq(f1.t,f2.t) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryUnify( t1 : TType, t2 : TType ) {
|
||||||
|
if( t1 == t2 )
|
||||||
|
return true;
|
||||||
|
switch( [t1,t2] ) {
|
||||||
|
case [TMono(r), _]:
|
||||||
|
if( r.r == null ) {
|
||||||
|
if( !link(t1,t2,r) )
|
||||||
|
return false;
|
||||||
|
r.r = t2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return tryUnify(r.r, t2);
|
||||||
|
case [_, TMono(r)]:
|
||||||
|
if( r.r == null ) {
|
||||||
|
if( !link(t2,t1,r) )
|
||||||
|
return false;
|
||||||
|
r.r = t1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return tryUnify(t1, r.r);
|
||||||
|
case [TType(t1,pl1), _]:
|
||||||
|
return tryUnify(apply(t1.t, t1.params, pl1), t2);
|
||||||
|
case [_,TType(t2,pl2)]:
|
||||||
|
return tryUnify(t1, apply(t2.t, t2.params, pl2));
|
||||||
|
case [TNull(t1), _]:
|
||||||
|
return tryUnify(t1,t2);
|
||||||
|
case [_, TNull(t2)]:
|
||||||
|
return tryUnify(t1,t2);
|
||||||
|
case [TFun(args1,r1),TFun(args2,r2)] if( args1.length == args2.length ):
|
||||||
|
for( i in 0...args1.length ) {
|
||||||
|
var a1 = args1[i];
|
||||||
|
var a2 = args2[i];
|
||||||
|
if( a2.opt && !a1.opt ) return false;
|
||||||
|
if( !tryUnify(a2.t, a1.t) ) return false;
|
||||||
|
}
|
||||||
|
return tryUnify(r1,r2);
|
||||||
|
case [_, TDynamic]:
|
||||||
|
return true;
|
||||||
|
case [TDynamic, _]:
|
||||||
|
return true;
|
||||||
|
case [TAnon(a1),TAnon(a2)]:
|
||||||
|
var m = new Map();
|
||||||
|
for( f in a1 )
|
||||||
|
m.set(f.name, f);
|
||||||
|
for( f2 in a2 ) {
|
||||||
|
var f1 = m.get(f2.name);
|
||||||
|
if( f1 == null ) {
|
||||||
|
if( f2.opt ) continue;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if( !typeEq(f1.t,f2.t) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case [TInst(cl1,pl1), TInst(cl2,pl2)]:
|
||||||
|
while( cl1 != cl2 ) {
|
||||||
|
switch( cl1.superClass ) {
|
||||||
|
case null: return false;
|
||||||
|
case TInst(c, args):
|
||||||
|
pl1 = [for( a in args ) apply(a,cl1.params,pl1)];
|
||||||
|
cl1 = c;
|
||||||
|
default: throw "assert";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for( i in 0...pl1.length )
|
||||||
|
if( !typeEq(pl1[i],pl2[i]) )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
case [TInt, TFloat]:
|
||||||
|
return true;
|
||||||
|
case [TFun(_), TAbstract({ name : "haxe.Function" },_)]:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return typeEq(t1,t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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> ) {
|
||||||
|
if( args.length != params.length ) throw "Invalid number of type parameters";
|
||||||
|
if( args.length == 0 )
|
||||||
|
return t;
|
||||||
|
var subst = new Map();
|
||||||
|
for( i in 0...params.length )
|
||||||
|
subst.set(params[i], args[i]);
|
||||||
|
function map(t:TType) {
|
||||||
|
var st = subst.get(t);
|
||||||
|
if( st != null ) return st;
|
||||||
|
return mapType(t,map);
|
||||||
|
}
|
||||||
|
return map(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mapType( t : TType, f : TType -> TType ) {
|
||||||
|
switch (t) {
|
||||||
|
case TMono(r):
|
||||||
|
if( r.r == null ) return t;
|
||||||
|
return f(t);
|
||||||
|
case TVoid, TInt, TFloat,TBool,TDynamic,TParam(_), TUnresolved(_):
|
||||||
|
return t;
|
||||||
|
case TEnum(_,[]), TInst(_,[]), TAbstract(_,[]), TType(_,[]):
|
||||||
|
return t;
|
||||||
|
case TNull(t):
|
||||||
|
return TNull(f(t));
|
||||||
|
case TInst(c, args):
|
||||||
|
return TInst(c, [for( t in args ) f(t)]);
|
||||||
|
case TEnum(e, args):
|
||||||
|
return TEnum(e, [for( t in args ) f(t)]);
|
||||||
|
case TType(t, args):
|
||||||
|
return TType(t, [for( t in args ) f(t)]);
|
||||||
|
case TAbstract(a, args):
|
||||||
|
return TAbstract(a, [for( t in args ) f(t)]);
|
||||||
|
case TFun(args, ret):
|
||||||
|
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) }]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 TNull(t): follow(t);
|
||||||
|
default: t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFields( t : TType ) {
|
||||||
|
var fields = [];
|
||||||
|
while( t != null ) {
|
||||||
|
t = follow(t);
|
||||||
|
switch( t ) {
|
||||||
|
case TInst(c, args):
|
||||||
|
for( fname in c.fields.keys() ) {
|
||||||
|
var f = c.fields.get(fname);
|
||||||
|
fields.push({ name : f.name, t : f.t });
|
||||||
|
}
|
||||||
|
t = c.superClass;
|
||||||
|
case TAnon(fl):
|
||||||
|
for( f in fl )
|
||||||
|
fields.push({ name : f.name, t : f.t });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getField( t : TType, f : String, e : Expr ) {
|
||||||
|
switch( follow(t) ) {
|
||||||
|
case TInst(c, args):
|
||||||
|
var cf = c.fields.get(f);
|
||||||
|
if( cf == null && allowAsync ) {
|
||||||
|
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) };
|
||||||
|
if( cf.t == null ) cf = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( cf == null ) {
|
||||||
|
if( c.superClass == null ) return null;
|
||||||
|
var ft = getField(c.superClass, f, e);
|
||||||
|
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);
|
||||||
|
case TDynamic:
|
||||||
|
return makeMono();
|
||||||
|
case TAnon(fields):
|
||||||
|
for( af in fields )
|
||||||
|
if( af.name == f )
|
||||||
|
return af.t;
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unasync( t : TType ) : TType {
|
||||||
|
switch( follow(t) ) {
|
||||||
|
case TFun(args, ret) if( args.length > 0 ):
|
||||||
|
var rargs = args.copy();
|
||||||
|
switch( follow(rargs.shift().t) ) {
|
||||||
|
case TFun([r],_): return TFun(rargs,r.t);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeExprWith( expr : Expr, t : TType ) {
|
||||||
|
var et = typeExpr(expr, WithType(t));
|
||||||
|
unify(et, t, expr);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeMono() {
|
||||||
|
return TMono({r:null});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeIterator(t) : TType {
|
||||||
|
return TAnon([{ name : "next", opt : false, t : TFun([],t) }, { name : "hasNext", opt : false, t : TFun([],TBool) }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mk(e,p) : Expr {
|
||||||
|
#if hscriptPos
|
||||||
|
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
function isString( t : TType ) {
|
||||||
|
t = follow(t);
|
||||||
|
return t.match(TInst({name:"String"},_));
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeExpr( expr : Expr, withType : WithType ) : TType {
|
||||||
|
switch( edef(expr) ) {
|
||||||
|
case EConst(c):
|
||||||
|
return switch (c) {
|
||||||
|
case CInt(_): TInt;
|
||||||
|
case CFloat(_): TFloat;
|
||||||
|
case CString(_): types.t_string;
|
||||||
|
}
|
||||||
|
case EIdent(v):
|
||||||
|
var l = locals.get(v);
|
||||||
|
if( l != null ) return l;
|
||||||
|
var g = globals.get(v);
|
||||||
|
if( g != null ) return g;
|
||||||
|
if( allowAsync ) {
|
||||||
|
g = globals.get("a_"+v);
|
||||||
|
if( g != null ) g = unasync(g);
|
||||||
|
if( g != null ) return g;
|
||||||
|
}
|
||||||
|
switch( v ) {
|
||||||
|
case "null":
|
||||||
|
return makeMono();
|
||||||
|
case "true", "false":
|
||||||
|
return TBool;
|
||||||
|
case "trace":
|
||||||
|
return TDynamic;
|
||||||
|
default:
|
||||||
|
error("Unknown identifier "+v, expr);
|
||||||
|
}
|
||||||
|
case EBlock(el):
|
||||||
|
var t = TVoid;
|
||||||
|
var locals = saveLocals();
|
||||||
|
for( e in el )
|
||||||
|
t = typeExpr(e, e == el[el.length-1] ? withType : NoValue);
|
||||||
|
this.locals = locals;
|
||||||
|
return t;
|
||||||
|
case EVar(n, t, init):
|
||||||
|
var vt = t == null ? makeMono() : makeType(t, expr);
|
||||||
|
if( init != null ) {
|
||||||
|
var et = typeExpr(init, t == null ? Value : WithType(vt));
|
||||||
|
if( t == null ) vt = et else unify(et,vt, init);
|
||||||
|
}
|
||||||
|
locals.set(n, vt);
|
||||||
|
return TVoid;
|
||||||
|
case EParent(e):
|
||||||
|
return typeExpr(e,withType);
|
||||||
|
case ECall(e, params):
|
||||||
|
var ft = typeExpr(e, Value);
|
||||||
|
switch( follow(ft) ) {
|
||||||
|
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));
|
||||||
|
unify(t, a.t, params[i]);
|
||||||
|
}
|
||||||
|
for( i in params.length...args.length )
|
||||||
|
if( !args[i].opt )
|
||||||
|
error("Missing argument '"+args[i].name+"'", expr);
|
||||||
|
return ret;
|
||||||
|
case TDynamic:
|
||||||
|
for( p in params ) typeExpr(p,Value);
|
||||||
|
return TDynamic;
|
||||||
|
default:
|
||||||
|
error(typeStr(ft)+" cannot be called", e);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
case ECheckType(v, t):
|
||||||
|
var ct = makeType(t, expr);
|
||||||
|
var vt = typeExpr(v, WithType(ct));
|
||||||
|
unify(vt, ct, v);
|
||||||
|
return ct;
|
||||||
|
case EMeta(_, _, e):
|
||||||
|
return typeExpr(e, withType);
|
||||||
|
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
|
||||||
|
typeExprWith(cond, TBool);
|
||||||
|
var t1 = typeExpr(e1, withType);
|
||||||
|
if( e2 == null )
|
||||||
|
return t1;
|
||||||
|
var t2 = typeExpr(e2, withType);
|
||||||
|
if( withType == NoValue )
|
||||||
|
return TVoid;
|
||||||
|
if( tryUnify(t2,t1) )
|
||||||
|
return t1;
|
||||||
|
if( tryUnify(t1,t2) )
|
||||||
|
return t2;
|
||||||
|
unify(t2,t1,e2); // error
|
||||||
|
case EWhile(cond, e), EDoWhile(cond, e):
|
||||||
|
typeExprWith(cond,TBool);
|
||||||
|
typeExpr(e, NoValue);
|
||||||
|
return TVoid;
|
||||||
|
case EObject(fl):
|
||||||
|
switch( withType ) {
|
||||||
|
case WithType(follow(_) => TAnon(tfields)):
|
||||||
|
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 }
|
||||||
|
}]);
|
||||||
|
default:
|
||||||
|
return TAnon([for( f in fl ) { t : typeExpr(f.e, Value), opt : false, name : f.name }]);
|
||||||
|
}
|
||||||
|
case EBreak, EContinue:
|
||||||
|
return TVoid;
|
||||||
|
case EReturn(v):
|
||||||
|
var et = v == null ? TVoid : typeExpr(v, allowReturn == null ? Value : WithType(allowReturn));
|
||||||
|
if( allowReturn == null )
|
||||||
|
error("Return not allowed here", expr);
|
||||||
|
else
|
||||||
|
unify(et, allowReturn, v == null ? expr : v);
|
||||||
|
return TDynamic;
|
||||||
|
case EArrayDecl(el):
|
||||||
|
var et = null;
|
||||||
|
for( v in el ) {
|
||||||
|
var t = typeExpr(v, et == null ? Value : WithType(et));
|
||||||
|
if( et == null ) et = t else if( !tryUnify(t,et) ) {
|
||||||
|
if( tryUnify(et,t) ) et = t else unify(t,et,v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( et == null ) et = TDynamic;
|
||||||
|
return types.getType("Array",[et]);
|
||||||
|
case EArray(a, index):
|
||||||
|
typeExprWith(index, TInt);
|
||||||
|
var at = typeExpr(a, Value);
|
||||||
|
switch( follow(at) ) {
|
||||||
|
case TInst({ name : "Array"},[et]): return et;
|
||||||
|
default: error(typeStr(at)+" is not an Array", a);
|
||||||
|
}
|
||||||
|
case EThrow(e):
|
||||||
|
typeExpr(e, Value);
|
||||||
|
return TDynamic;
|
||||||
|
case EFunction(args, body, name, ret):
|
||||||
|
var tret = ret == null ? makeMono() : makeType(ret, expr);
|
||||||
|
var locals = saveLocals();
|
||||||
|
var oldRet = allowReturn;
|
||||||
|
allowReturn = tret;
|
||||||
|
var withArgs = null;
|
||||||
|
if( name != null && !withType.match(WithType(follow(_) => TFun(_))) ) {
|
||||||
|
var ev = events.get(name);
|
||||||
|
if( ev != null ) withType = WithType(ev);
|
||||||
|
}
|
||||||
|
switch( withType ) {
|
||||||
|
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 };
|
||||||
|
}];
|
||||||
|
|
||||||
|
typeExpr(body,NoValue);
|
||||||
|
allowReturn = oldRet;
|
||||||
|
this.locals = locals;
|
||||||
|
var ft = TFun(targs,tret);
|
||||||
|
if( name != null )
|
||||||
|
locals.set(name, ft);
|
||||||
|
return ft;
|
||||||
|
case EUnop(op, _, e):
|
||||||
|
var et = typeExpr(e, Value);
|
||||||
|
switch( op ) {
|
||||||
|
case "++", "--":
|
||||||
|
unify(et,TInt,e);
|
||||||
|
return et;
|
||||||
|
case "!":
|
||||||
|
unify(et,TBool,e);
|
||||||
|
return et;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
case EFor(v, it, e):
|
||||||
|
var locals = saveLocals();
|
||||||
|
var itt = typeExpr(it, Value);
|
||||||
|
var vt = switch( follow(itt) ) {
|
||||||
|
case TInst({name:"Array"},[t]):
|
||||||
|
t;
|
||||||
|
default:
|
||||||
|
var t = makeMono();
|
||||||
|
var iter = makeIterator(t);
|
||||||
|
unify(itt,iter,it);
|
||||||
|
t;
|
||||||
|
}
|
||||||
|
this.locals.set(v, vt);
|
||||||
|
typeExpr(e, Value);
|
||||||
|
this.locals = locals;
|
||||||
|
return TVoid;
|
||||||
|
case EBinop(op, e1, e2):
|
||||||
|
switch( op ) {
|
||||||
|
case "&", "|", "^", ">>", ">>>", "<<":
|
||||||
|
typeExprWith(e1,TInt);
|
||||||
|
typeExprWith(e2,TInt);
|
||||||
|
return TInt;
|
||||||
|
case "=":
|
||||||
|
var vt = typeExpr(e1, Value);
|
||||||
|
typeExprWith(e2,vt);
|
||||||
|
return vt;
|
||||||
|
case "+":
|
||||||
|
var t1 = typeExpr(e1,WithType(TInt));
|
||||||
|
var t2 = typeExpr(e2,WithType(t1));
|
||||||
|
tryUnify(t1,t2);
|
||||||
|
switch( [follow(t1), follow(t2)]) {
|
||||||
|
case [TInt, TInt]:
|
||||||
|
return TInt;
|
||||||
|
case [TFloat, TInt], [TInt, TFloat], [TFloat, TFloat]:
|
||||||
|
return TFloat;
|
||||||
|
case [TDynamic, _], [_, TDynamic]:
|
||||||
|
return TDynamic;
|
||||||
|
case [t1,t2]:
|
||||||
|
if( isString(t1) || isString(t2) )
|
||||||
|
return types.t_string;
|
||||||
|
unify(t1, TFloat, e1);
|
||||||
|
unify(t2, TFloat, e2);
|
||||||
|
}
|
||||||
|
case "-", "*", "/", "%":
|
||||||
|
var t1 = typeExpr(e1,WithType(TInt));
|
||||||
|
var t2 = typeExpr(e2,WithType(t1));
|
||||||
|
if( !tryUnify(t1,t2) )
|
||||||
|
unify(t2,t1,e2);
|
||||||
|
switch( [follow(t1), follow(t2)]) {
|
||||||
|
case [TInt, TInt]:
|
||||||
|
if( op == "/" ) return TFloat;
|
||||||
|
return TInt;
|
||||||
|
case [TFloat|TDynamic, TInt|TDynamic], [TInt|TDynamic, TFloat|TDynamic], [TFloat, TFloat]:
|
||||||
|
return TFloat;
|
||||||
|
default:
|
||||||
|
unify(t1, TFloat, e1);
|
||||||
|
unify(t2, TFloat, e2);
|
||||||
|
}
|
||||||
|
case "&&", "||":
|
||||||
|
typeExprWith(e1,TBool);
|
||||||
|
typeExprWith(e2,TBool);
|
||||||
|
return TBool;
|
||||||
|
case "...":
|
||||||
|
typeExprWith(e1,TInt);
|
||||||
|
typeExprWith(e2,TInt);
|
||||||
|
return makeIterator(TInt);
|
||||||
|
case "==", "!=":
|
||||||
|
var t1 = typeExpr(e1,Value);
|
||||||
|
var t2 = typeExpr(e2,WithType(t1));
|
||||||
|
if( !tryUnify(t1,t2) )
|
||||||
|
unify(t2,t1,e2);
|
||||||
|
return TBool;
|
||||||
|
case ">", "<", ">=", "<=":
|
||||||
|
var t1 = typeExpr(e1,Value);
|
||||||
|
var t2 = typeExpr(e2,WithType(t1));
|
||||||
|
if( !tryUnify(t1,t2) )
|
||||||
|
unify(t2,t1,e2);
|
||||||
|
switch( follow(t1) ) {
|
||||||
|
case TInt, TFloat, TBool, TInst({name:"String"},_):
|
||||||
|
default:
|
||||||
|
error("Cannot compare "+typeStr(t1), expr);
|
||||||
|
}
|
||||||
|
return TBool;
|
||||||
|
default:
|
||||||
|
if( op.charCodeAt(op.length-1) == "=".code ) {
|
||||||
|
var t = typeExpr(mk(EBinop(op.substr(0,op.length-1),e1,e2),expr),withType);
|
||||||
|
return typeExpr(mk(EBinop("=",e1,e2),expr), withType);
|
||||||
|
}
|
||||||
|
error("Unsupported operation "+op, expr);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+85
-53
@@ -1,26 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Nicolas Cannasse
|
* Copyright (C)2008-2017 Haxe Foundation
|
||||||
* All rights reserved.
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
*
|
||||||
* - Redistributions of source code must retain the above copyright
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* - Redistributions in binary form must reproduce the above copyright
|
* to deal in the Software without restriction, including without limitation
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
* documentation and/or other materials provided with the distribution.
|
* 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:
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
* all copies or substantial portions of the Software.
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
*
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
* DEALINGS IN THE SOFTWARE.
|
||||||
* DAMAGE.
|
|
||||||
*/
|
*/
|
||||||
package hscript;
|
package hscript;
|
||||||
|
|
||||||
@@ -43,6 +40,7 @@ typedef Expr = {
|
|||||||
}
|
}
|
||||||
enum ExprDef {
|
enum ExprDef {
|
||||||
#else
|
#else
|
||||||
|
typedef ExprDef = Expr;
|
||||||
enum Expr {
|
enum Expr {
|
||||||
#end
|
#end
|
||||||
EConst( c : Const );
|
EConst( c : Const );
|
||||||
@@ -71,9 +69,10 @@ enum 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);
|
EDoWhile( cond : Expr, e : Expr);
|
||||||
EMeta( name : String, args : Array<Expr>, e : Expr );
|
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||||
|
ECheckType( e : Expr, t : CType );
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef Argument = { name : String, ?t : CType, ?opt : Bool };
|
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
|
||||||
|
|
||||||
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
|
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
|
||||||
|
|
||||||
@@ -82,6 +81,8 @@ enum CType {
|
|||||||
CTFun( args : Array<CType>, ret : CType );
|
CTFun( args : Array<CType>, ret : CType );
|
||||||
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
|
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
|
||||||
CTParent( t : CType );
|
CTParent( t : CType );
|
||||||
|
CTOpt( t : CType );
|
||||||
|
CTNamed( n : String, t : CType );
|
||||||
}
|
}
|
||||||
|
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
@@ -98,39 +99,8 @@ class Error {
|
|||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function errorDefToString(): String {
|
|
||||||
switch (e) {
|
|
||||||
case EInvalidChar(c):
|
|
||||||
return "Invalid character: '"+String.fromCharCode(c)+"' ("+c+")";
|
|
||||||
|
|
||||||
case EUnexpected(s):
|
|
||||||
return "Unexpected token: \""+s+"\"";
|
|
||||||
|
|
||||||
case EUnterminatedString:
|
|
||||||
return "Unterminated string";
|
|
||||||
|
|
||||||
case EUnterminatedComment:
|
|
||||||
return "Unterminated comment";
|
|
||||||
|
|
||||||
case EUnknownVariable(v):
|
|
||||||
return "Unknown variable: "+v;
|
|
||||||
|
|
||||||
case EInvalidIterator(v):
|
|
||||||
return "Invalid iterator: "+v;
|
|
||||||
|
|
||||||
case EInvalidOp(op):
|
|
||||||
return "Invalid operator: "+op;
|
|
||||||
|
|
||||||
case EInvalidAccess(f):
|
|
||||||
return "Invalid access to field "+f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toString(): String {
|
public function toString(): String {
|
||||||
var message = errorDefToString();
|
return Printer.errorToString(this);
|
||||||
message = origin + ":" + line + ": " + message;
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
enum ErrorDef {
|
enum ErrorDef {
|
||||||
@@ -141,8 +111,70 @@ enum Error {
|
|||||||
EUnexpected( s : String );
|
EUnexpected( s : String );
|
||||||
EUnterminatedString;
|
EUnterminatedString;
|
||||||
EUnterminatedComment;
|
EUnterminatedComment;
|
||||||
|
EInvalidPreprocessor( msg : String );
|
||||||
EUnknownVariable( v : String );
|
EUnknownVariable( v : String );
|
||||||
EInvalidIterator( v : String );
|
EInvalidIterator( v : String );
|
||||||
EInvalidOp( op : String );
|
EInvalidOp( op : String );
|
||||||
EInvalidAccess( f : String );
|
EInvalidAccess( f : String );
|
||||||
|
ECustom( msg : String );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum ModuleDecl {
|
||||||
|
DPackage( path : Array<String> );
|
||||||
|
DImport( path : Array<String>, ?everything : Bool );
|
||||||
|
DClass( c : ClassDecl );
|
||||||
|
DTypedef( c : TypeDecl );
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ModuleType = {
|
||||||
|
var name : String;
|
||||||
|
var params : {}; // TODO : not yet parsed
|
||||||
|
var meta : Metadata;
|
||||||
|
var isPrivate : Bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ClassDecl = {> ModuleType,
|
||||||
|
var extend : Null<CType>;
|
||||||
|
var implement : Array<CType>;
|
||||||
|
var fields : Array<FieldDecl>;
|
||||||
|
var isExtern : Bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef TypeDecl = {> ModuleType,
|
||||||
|
var t : CType;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef FieldDecl = {
|
||||||
|
var name : String;
|
||||||
|
var meta : Metadata;
|
||||||
|
var kind : FieldKind;
|
||||||
|
var access : Array<FieldAccess>;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FieldAccess {
|
||||||
|
APublic;
|
||||||
|
APrivate;
|
||||||
|
AInline;
|
||||||
|
AOverride;
|
||||||
|
AStatic;
|
||||||
|
AMacro;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FieldKind {
|
||||||
|
KFunction( f : FunctionDecl );
|
||||||
|
KVar( v : VarDecl );
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef FunctionDecl = {
|
||||||
|
var args : Array<Argument>;
|
||||||
|
var expr : Expr;
|
||||||
|
var ret : Null<CType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef VarDecl = {
|
||||||
|
var get : Null<String>;
|
||||||
|
var set : Null<String>;
|
||||||
|
var expr : Null<Expr>;
|
||||||
|
var type : Null<CType>;
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-43
@@ -1,26 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Nicolas Cannasse
|
* Copyright (C)2008-2017 Haxe Foundation
|
||||||
* All rights reserved.
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
*
|
||||||
* - Redistributions of source code must retain the above copyright
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* - Redistributions in binary form must reproduce the above copyright
|
* to deal in the Software without restriction, including without limitation
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
* documentation and/or other materials provided with the distribution.
|
* 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:
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
* all copies or substantial portions of the Software.
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
*
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
* DEALINGS IN THE SOFTWARE.
|
||||||
* DAMAGE.
|
|
||||||
*/
|
*/
|
||||||
package hscript;
|
package hscript;
|
||||||
import haxe.PosInfos;
|
import haxe.PosInfos;
|
||||||
@@ -29,7 +26,7 @@ import hscript.Expr;
|
|||||||
private enum Stop {
|
private enum Stop {
|
||||||
SBreak;
|
SBreak;
|
||||||
SContinue;
|
SContinue;
|
||||||
SReturn( v : Dynamic );
|
SReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Interp {
|
class Interp {
|
||||||
@@ -47,6 +44,7 @@ class Interp {
|
|||||||
var depth : Int;
|
var depth : Int;
|
||||||
var inTry : Bool;
|
var inTry : Bool;
|
||||||
var declared : Array<{ n : String, old : { r : Dynamic } }>;
|
var declared : Array<{ n : String, old : { r : Dynamic } }>;
|
||||||
|
var returnValue : Dynamic;
|
||||||
|
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
var curExpr : Expr;
|
var curExpr : Expr;
|
||||||
@@ -64,7 +62,12 @@ class Interp {
|
|||||||
variables.set("null",null);
|
variables.set("null",null);
|
||||||
variables.set("true",true);
|
variables.set("true",true);
|
||||||
variables.set("false",false);
|
variables.set("false",false);
|
||||||
variables.set("trace",function(e) haxe.Log.trace(Std.string(e), posInfos()));
|
variables.set("trace", Reflect.makeVarArgs(function(el) {
|
||||||
|
var inf = posInfos();
|
||||||
|
var v = el.shift();
|
||||||
|
if( el.length > 0 ) inf.customParams = el;
|
||||||
|
haxe.Log.trace(Std.string(v), inf);
|
||||||
|
}));
|
||||||
initOps();
|
initOps();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +122,7 @@ class Interp {
|
|||||||
|
|
||||||
function assign( e1 : Expr, e2 : Expr ) : Dynamic {
|
function assign( e1 : Expr, e2 : Expr ) : Dynamic {
|
||||||
var v = expr(e2);
|
var v = expr(e2);
|
||||||
switch( edef(e1) ) {
|
switch( Tools.expr(e1) ) {
|
||||||
case EIdent(id):
|
case EIdent(id):
|
||||||
var l = locals.get(id);
|
var l = locals.get(id);
|
||||||
if( l == null )
|
if( l == null )
|
||||||
@@ -151,7 +154,7 @@ class Interp {
|
|||||||
|
|
||||||
function evalAssignOp(op,fop,e1,e2) : Dynamic {
|
function evalAssignOp(op,fop,e1,e2) : Dynamic {
|
||||||
var v;
|
var v;
|
||||||
switch( edef(e1) ) {
|
switch( Tools.expr(e1) ) {
|
||||||
case EIdent(id):
|
case EIdent(id):
|
||||||
var l = locals.get(id);
|
var l = locals.get(id);
|
||||||
v = fop(expr(e1),expr(e2));
|
v = fop(expr(e1),expr(e2));
|
||||||
@@ -250,7 +253,10 @@ class Interp {
|
|||||||
switch( e ) {
|
switch( e ) {
|
||||||
case SBreak: throw "Invalid break";
|
case SBreak: throw "Invalid break";
|
||||||
case SContinue: throw "Invalid continue";
|
case SContinue: throw "Invalid continue";
|
||||||
case SReturn(v): return v;
|
case SReturn:
|
||||||
|
var v = returnValue;
|
||||||
|
returnValue = null;
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -274,21 +280,18 @@ class Interp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline function edef( e : Expr ) {
|
inline function error(e : #if hscriptPos ErrorDef #else Error #end, rethrow=false ) : Dynamic {
|
||||||
#if hscriptPos
|
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
|
||||||
return e.e;
|
if( rethrow ) this.rethrow(e) else throw e;
|
||||||
#else
|
return null;
|
||||||
return e;
|
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline function error(e : #if hscriptPos ErrorDef #else Error #end ) : Dynamic {
|
inline function rethrow( e : Dynamic ) {
|
||||||
#if hscriptPos
|
#if hl
|
||||||
throw new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line);
|
hl.Api.rethrow(e);
|
||||||
#else
|
#else
|
||||||
throw e;
|
throw e;
|
||||||
#end
|
#end
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolve( id : String ) : Dynamic {
|
function resolve( id : String ) : Dynamic {
|
||||||
@@ -361,7 +364,7 @@ class Interp {
|
|||||||
for( p in params )
|
for( p in params )
|
||||||
args.push(expr(p));
|
args.push(expr(p));
|
||||||
|
|
||||||
switch( edef(e) ) {
|
switch( Tools.expr(e) ) {
|
||||||
case EField(e,f):
|
case EField(e,f):
|
||||||
var obj = expr(e);
|
var obj = expr(e);
|
||||||
if( obj == null ) error(EInvalidAccess(f));
|
if( obj == null ) error(EInvalidAccess(f));
|
||||||
@@ -385,7 +388,8 @@ class Interp {
|
|||||||
case EContinue:
|
case EContinue:
|
||||||
throw SContinue;
|
throw SContinue;
|
||||||
case EReturn(e):
|
case EReturn(e):
|
||||||
throw SReturn((e == null)?null:expr(e));
|
returnValue = e == null ? null : expr(e);
|
||||||
|
throw SReturn;
|
||||||
case EFunction(params,fexpr,name,_):
|
case EFunction(params,fexpr,name,_):
|
||||||
var capturedLocals = duplicate(locals);
|
var capturedLocals = duplicate(locals);
|
||||||
var me = this;
|
var me = this;
|
||||||
@@ -456,7 +460,7 @@ class Interp {
|
|||||||
}
|
}
|
||||||
return f;
|
return f;
|
||||||
case EArrayDecl(arr):
|
case EArrayDecl(arr):
|
||||||
if (arr.length > 0 && edef(arr[0]).match(EBinop("=>", _))) {
|
if (arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _))) {
|
||||||
var isAllString:Bool = true;
|
var isAllString:Bool = true;
|
||||||
var isAllInt:Bool = true;
|
var isAllInt:Bool = true;
|
||||||
var isAllObject:Bool = true;
|
var isAllObject:Bool = true;
|
||||||
@@ -464,7 +468,7 @@ class Interp {
|
|||||||
var keys:Array<Dynamic> = [];
|
var keys:Array<Dynamic> = [];
|
||||||
var values:Array<Dynamic> = [];
|
var values:Array<Dynamic> = [];
|
||||||
for (e in arr) {
|
for (e in arr) {
|
||||||
switch(edef(e)) {
|
switch(Tools.expr(e)) {
|
||||||
case EBinop("=>", eKey, eValue): {
|
case EBinop("=>", eKey, eValue): {
|
||||||
var key:Dynamic = expr(eKey);
|
var key:Dynamic = expr(eKey);
|
||||||
var value:Dynamic = expr(eValue);
|
var value:Dynamic = expr(eValue);
|
||||||
@@ -562,6 +566,8 @@ class Interp {
|
|||||||
return val;
|
return val;
|
||||||
case EMeta(_, _, e):
|
case EMeta(_, _, e):
|
||||||
return expr(e);
|
return expr(e);
|
||||||
|
case ECheckType(e,_):
|
||||||
|
return expr(e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -575,7 +581,7 @@ class Interp {
|
|||||||
switch(err) {
|
switch(err) {
|
||||||
case SContinue:
|
case SContinue:
|
||||||
case SBreak: break;
|
case SBreak: break;
|
||||||
case SReturn(_): throw err;
|
case SReturn: throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -592,7 +598,7 @@ class Interp {
|
|||||||
switch(err) {
|
switch(err) {
|
||||||
case SContinue:
|
case SContinue:
|
||||||
case SBreak: break;
|
case SBreak: break;
|
||||||
case SReturn(_): throw err;
|
case SReturn: throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -600,7 +606,7 @@ class Interp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeIterator( v : Dynamic ) : Iterator<Dynamic> {
|
function makeIterator( v : Dynamic ) : Iterator<Dynamic> {
|
||||||
#if ((flash && !flash9) || php)
|
#if ((flash && !flash9) || (php && !php7 && haxe_ver < '4.0.0'))
|
||||||
if ( v.iterator != null ) v = v.iterator();
|
if ( v.iterator != null ) v = v.iterator();
|
||||||
#else
|
#else
|
||||||
try v = v.iterator() catch( e : Dynamic ) {};
|
try v = v.iterator() catch( e : Dynamic ) {};
|
||||||
@@ -621,7 +627,7 @@ class Interp {
|
|||||||
switch( err ) {
|
switch( err ) {
|
||||||
case SContinue:
|
case SContinue:
|
||||||
case SBreak: break;
|
case SBreak: break;
|
||||||
case SReturn(_): throw err;
|
case SReturn: throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-29
@@ -1,26 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, Nicolas Cannasse
|
* Copyright (C)2008-2017 Haxe Foundation
|
||||||
* All rights reserved.
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
*
|
||||||
* - Redistributions of source code must retain the above copyright
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* - Redistributions in binary form must reproduce the above copyright
|
* to deal in the Software without restriction, including without limitation
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
* documentation and/or other materials provided with the distribution.
|
* 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:
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
* all copies or substantial portions of the Software.
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
*
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
* DEALINGS IN THE SOFTWARE.
|
||||||
* DAMAGE.
|
|
||||||
*/
|
*/
|
||||||
package hscript;
|
package hscript;
|
||||||
import hscript.Expr.Error;
|
import hscript.Expr.Error;
|
||||||
@@ -79,6 +76,9 @@ class Macro {
|
|||||||
#if haxe3
|
#if haxe3
|
||||||
case OpArrow: "=>";
|
case OpArrow: "=>";
|
||||||
#end
|
#end
|
||||||
|
#if (haxe_ver >= 4)
|
||||||
|
case OpIn: "in";
|
||||||
|
#end
|
||||||
};
|
};
|
||||||
binops.set(str, op);
|
binops.set(str, op);
|
||||||
if( assign )
|
if( assign )
|
||||||
@@ -113,6 +113,7 @@ class Macro {
|
|||||||
|
|
||||||
function convertType( t : Expr.CType ) : ComplexType {
|
function convertType( t : Expr.CType ) : ComplexType {
|
||||||
return switch( t ) {
|
return switch( t ) {
|
||||||
|
case CTOpt(t): TOptional(convertType(t));
|
||||||
case CTPath(pack, args):
|
case CTPath(pack, args):
|
||||||
var params = [];
|
var params = [];
|
||||||
if( args != null )
|
if( args != null )
|
||||||
@@ -127,10 +128,18 @@ class Macro {
|
|||||||
case CTParent(t): TParent(convertType(t));
|
case CTParent(t): TParent(convertType(t));
|
||||||
case CTFun(args, ret):
|
case CTFun(args, ret):
|
||||||
TFunction(map(args,convertType), convertType(ret));
|
TFunction(map(args,convertType), convertType(ret));
|
||||||
|
case CTNamed(name, convertType(_) => ct):
|
||||||
|
#if (haxe_ver >= 4)
|
||||||
|
TNamed(name, ct);
|
||||||
|
#else
|
||||||
|
ct;
|
||||||
|
#end
|
||||||
case CTAnon(fields):
|
case CTAnon(fields):
|
||||||
var tf = [];
|
var tf = [];
|
||||||
for( f in fields )
|
for( f in fields ) {
|
||||||
tf.push( { name : f.name, meta : [], doc : null, access : [], kind : FVar(convertType(f.t),null), pos : p } );
|
var meta = f.meta == null ? [] : [for( m in f.meta ) { name : m.name, params : m.params == null ? [] : [for( e in m.params ) convert(e)], pos : p }];
|
||||||
|
tf.push( { name : f.name, meta : meta, doc : null, access : [], kind : FVar(convertType(f.t), null), pos : p } );
|
||||||
|
}
|
||||||
TAnonymous(tf);
|
TAnonymous(tf);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -182,14 +191,16 @@ class Macro {
|
|||||||
EWhile(convert(c), convert(e), true);
|
EWhile(convert(c), convert(e), true);
|
||||||
case EDoWhile(c, e):
|
case EDoWhile(c, e):
|
||||||
EWhile(convert(c), convert(e), false);
|
EWhile(convert(c), convert(e), false);
|
||||||
#if (haxe_211 || haxe3)
|
|
||||||
case EFor(v, it, efor):
|
case EFor(v, it, efor):
|
||||||
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
#if (haxe_ver >= 4)
|
||||||
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||||
#else
|
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||||
case EFor(v, it, e):
|
#elseif (haxe_211 || haxe3)
|
||||||
EFor(v, convert(it), convert(e));
|
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||||
#end
|
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
|
||||||
|
#else
|
||||||
|
EFor(v, convert(it), convert(efor));
|
||||||
|
#end
|
||||||
case EBreak:
|
case EBreak:
|
||||||
EBreak;
|
EBreak;
|
||||||
case EContinue:
|
case EContinue:
|
||||||
@@ -234,6 +245,8 @@ class Macro {
|
|||||||
case EMeta(m, params, esub):
|
case EMeta(m, params, esub):
|
||||||
var mpos = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
var mpos = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||||
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
|
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
|
||||||
|
case ECheckType(e, t):
|
||||||
|
ECheckType(convert(e), convertType(t));
|
||||||
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
|
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+605
-122
@@ -1,26 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Nicolas Cannasse
|
* Copyright (C)2008-2017 Haxe Foundation
|
||||||
* All rights reserved.
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
*
|
||||||
* - Redistributions of source code must retain the above copyright
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* notice, this list of conditions and the following disclaimer.
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
* - Redistributions in binary form must reproduce the above copyright
|
* to deal in the Software without restriction, including without limitation
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
* documentation and/or other materials provided with the distribution.
|
* 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:
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
|
* The above copyright notice and this permission notice shall be included in
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
* all copies or substantial portions of the Software.
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
*
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
* DEALINGS IN THE SOFTWARE.
|
||||||
* DAMAGE.
|
|
||||||
*/
|
*/
|
||||||
package hscript;
|
package hscript;
|
||||||
import hscript.Expr;
|
import hscript.Expr;
|
||||||
@@ -42,6 +39,7 @@ enum Token {
|
|||||||
TQuestion;
|
TQuestion;
|
||||||
TDoubleDot;
|
TDoubleDot;
|
||||||
TMeta( s : String );
|
TMeta( s : String );
|
||||||
|
TPrepro( s : String );
|
||||||
}
|
}
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
@@ -60,6 +58,11 @@ class Parser {
|
|||||||
public var unops : Hash<Bool>; // true if allow postfix
|
public var unops : Hash<Bool>; // true if allow postfix
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
/**
|
||||||
|
allows to check for #if / #else in code
|
||||||
|
**/
|
||||||
|
public var preprocesorValues : Map<String,Dynamic> = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
activate JSON compatiblity
|
activate JSON compatiblity
|
||||||
**/
|
**/
|
||||||
@@ -147,16 +150,12 @@ class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function invalidChar(c) {
|
public function invalidChar(c) {
|
||||||
error(EInvalidChar(c), readPos, readPos);
|
error(EInvalidChar(c), readPos-1, readPos-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parseString( s : String, ?origin : String = "hscript" ) {
|
function initParser( origin ) {
|
||||||
uid = 0;
|
// line=1 - don't reset line : it might be set manualy
|
||||||
return parse( new haxe.io.StringInput(s), origin );
|
preprocStack = [];
|
||||||
}
|
|
||||||
|
|
||||||
public function parse( s : haxe.io.Input, ?origin : String = "hscript" ) {
|
|
||||||
line = 1;
|
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
readPos = 0;
|
readPos = 0;
|
||||||
@@ -169,19 +168,28 @@ class Parser {
|
|||||||
tokens = new haxe.FastList<Token>();
|
tokens = new haxe.FastList<Token>();
|
||||||
#end
|
#end
|
||||||
char = -1;
|
char = -1;
|
||||||
input = s;
|
|
||||||
ops = new Array();
|
ops = new Array();
|
||||||
idents = new Array();
|
idents = new Array();
|
||||||
|
uid = 0;
|
||||||
for( i in 0...opChars.length )
|
for( i in 0...opChars.length )
|
||||||
ops[opChars.charCodeAt(i)] = true;
|
ops[opChars.charCodeAt(i)] = true;
|
||||||
for( i in 0...identChars.length )
|
for( i in 0...identChars.length )
|
||||||
idents[identChars.charCodeAt(i)] = true;
|
idents[identChars.charCodeAt(i)] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseString( s : String, ?origin : String = "hscript" ) {
|
||||||
|
return parse( new haxe.io.StringInput(s), origin );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parse( s : haxe.io.Input, ?origin : String = "hscript" ) {
|
||||||
|
initParser(origin);
|
||||||
|
input = s;
|
||||||
var a = new Array();
|
var a = new Array();
|
||||||
while( true ) {
|
while( true ) {
|
||||||
var tk = token();
|
var tk = token();
|
||||||
if( tk == TEof ) break;
|
if( tk == TEof ) break;
|
||||||
push(tk);
|
push(tk);
|
||||||
a.push(parseFullExpr());
|
parseFullExpr(a);
|
||||||
}
|
}
|
||||||
return if( a.length == 1 ) a[0] else mk(EBlock(a),0);
|
return if( a.length == 1 ) a[0] else mk(EBlock(a),0);
|
||||||
}
|
}
|
||||||
@@ -206,6 +214,19 @@ class Parser {
|
|||||||
if( t != tk ) unexpected(t);
|
if( t != tk ) unexpected(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline function ensureToken(tk) {
|
||||||
|
var t = token();
|
||||||
|
if( !Type.enumEq(t,tk) ) unexpected(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybe(tk) {
|
||||||
|
var t = token();
|
||||||
|
if( Type.enumEq(t, tk) )
|
||||||
|
return true;
|
||||||
|
push(t);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function getIdent() {
|
function getIdent() {
|
||||||
var tk = token();
|
var tk = token();
|
||||||
switch( tk ) {
|
switch( tk ) {
|
||||||
@@ -255,7 +276,7 @@ class Parser {
|
|||||||
return switch( expr(e) ) {
|
return switch( expr(e) ) {
|
||||||
case EBlock(_), EObject(_), ESwitch(_): true;
|
case EBlock(_), EObject(_), ESwitch(_): true;
|
||||||
case EFunction(_,e,_,_): isBlock(e);
|
case EFunction(_,e,_,_): isBlock(e);
|
||||||
case EVar(_,_,e): e != null && isBlock(e);
|
case EVar(_, t, e): e != null ? isBlock(e) : t != null ? t.match(CTAnon(_)) : false;
|
||||||
case EIf(_,e1,e2): if( e2 != null ) isBlock(e2) else isBlock(e1);
|
case EIf(_,e1,e2): if( e2 != null ) isBlock(e2) else isBlock(e1);
|
||||||
case EBinop(_,_,e): isBlock(e);
|
case EBinop(_,_,e): isBlock(e);
|
||||||
case EUnop(_,prefix,e): !prefix && isBlock(e);
|
case EUnop(_,prefix,e): !prefix && isBlock(e);
|
||||||
@@ -264,20 +285,29 @@ class Parser {
|
|||||||
case EFor(_,_,e): isBlock(e);
|
case EFor(_,_,e): isBlock(e);
|
||||||
case EReturn(e): e != null && isBlock(e);
|
case EReturn(e): e != null && isBlock(e);
|
||||||
case ETry(_, _, _, e): isBlock(e);
|
case ETry(_, _, _, e): isBlock(e);
|
||||||
|
case EMeta(_, _, e): isBlock(e);
|
||||||
default: false;
|
default: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFullExpr() {
|
function parseFullExpr( exprs : Array<Expr> ) {
|
||||||
var e = parseExpr();
|
var e = parseExpr();
|
||||||
|
exprs.push(e);
|
||||||
|
|
||||||
var tk = token();
|
var tk = token();
|
||||||
|
// this is a hack to support var a,b,c; with a single EVar
|
||||||
|
while( tk == TComma && expr(e).match(EVar(_)) ) {
|
||||||
|
e = parseStructure("var"); // next variable
|
||||||
|
exprs.push(e);
|
||||||
|
tk = token();
|
||||||
|
}
|
||||||
|
|
||||||
if( tk != TSemicolon && tk != TEof ) {
|
if( tk != TSemicolon && tk != TEof ) {
|
||||||
if( isBlock(e) )
|
if( isBlock(e) )
|
||||||
push(tk);
|
push(tk);
|
||||||
else
|
else
|
||||||
unexpected(tk);
|
unexpected(tk);
|
||||||
}
|
}
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseObject(p1) {
|
function parseObject(p1) {
|
||||||
@@ -329,8 +359,31 @@ class Parser {
|
|||||||
return parseExprNext(mk(EConst(c)));
|
return parseExprNext(mk(EConst(c)));
|
||||||
case TPOpen:
|
case TPOpen:
|
||||||
var e = parseExpr();
|
var e = parseExpr();
|
||||||
ensure(TPClose);
|
tk = token();
|
||||||
return parseExprNext(mk(EParent(e),p1,tokenMax));
|
switch( tk ) {
|
||||||
|
case TPClose:
|
||||||
|
return parseExprNext(mk(EParent(e),p1,tokenMax));
|
||||||
|
case TDoubleDot:
|
||||||
|
var t = parseType();
|
||||||
|
tk = token();
|
||||||
|
switch( tk ) {
|
||||||
|
case TPClose:
|
||||||
|
return parseExprNext(mk(ECheckType(e,t),p1,tokenMax));
|
||||||
|
case TComma:
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EIdent(v): return parseLambda([{ name : v, t : t }], pmin(e));
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
case TComma:
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EIdent(v): return parseLambda([{name:v}], pmin(e));
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return unexpected(tk);
|
||||||
case TBrOpen:
|
case TBrOpen:
|
||||||
tk = token();
|
tk = token();
|
||||||
switch( tk ) {
|
switch( tk ) {
|
||||||
@@ -367,7 +420,7 @@ class Parser {
|
|||||||
}
|
}
|
||||||
var a = new Array();
|
var a = new Array();
|
||||||
while( true ) {
|
while( true ) {
|
||||||
a.push(parseFullExpr());
|
parseFullExpr(a);
|
||||||
tk = token();
|
tk = token();
|
||||||
if( tk == TBrClose )
|
if( tk == TBrClose )
|
||||||
break;
|
break;
|
||||||
@@ -375,8 +428,19 @@ class Parser {
|
|||||||
}
|
}
|
||||||
return mk(EBlock(a),p1);
|
return mk(EBlock(a),p1);
|
||||||
case TOp(op):
|
case TOp(op):
|
||||||
if( unops.exists(op) )
|
if( unops.exists(op) ) {
|
||||||
return makeUnop(op,parseExpr());
|
var start = tokenMin;
|
||||||
|
var e = parseExpr();
|
||||||
|
if( op == "-" )
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EConst(CInt(i)):
|
||||||
|
return mk(EConst(CInt(-i)), start, pmax(e));
|
||||||
|
case EConst(CFloat(f)):
|
||||||
|
return mk(EConst(CFloat(-f)), start, pmax(e));
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return makeUnop(op,e);
|
||||||
|
}
|
||||||
return unexpected(tk);
|
return unexpected(tk);
|
||||||
case TBkOpen:
|
case TBkOpen:
|
||||||
var a = new Array();
|
var a = new Array();
|
||||||
@@ -409,6 +473,25 @@ class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseLambda( args : Array<Argument>, pmin ) {
|
||||||
|
while( true ) {
|
||||||
|
var id = getIdent();
|
||||||
|
var t = maybe(TDoubleDot) ? parseType() : null;
|
||||||
|
args.push({ name : id, t : t });
|
||||||
|
var tk = token();
|
||||||
|
switch( tk ) {
|
||||||
|
case TComma:
|
||||||
|
case TPClose:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unexpected(tk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ensureToken(TOp("->"));
|
||||||
|
var eret = parseExpr();
|
||||||
|
return mk(EFunction(args, mk(EReturn(eret),pmin)), pmin);
|
||||||
|
}
|
||||||
|
|
||||||
function parseMetaArgs() {
|
function parseMetaArgs() {
|
||||||
var tk = token();
|
var tk = token();
|
||||||
if( tk != TPOpen ) {
|
if( tk != TPOpen ) {
|
||||||
@@ -533,8 +616,7 @@ class Parser {
|
|||||||
case "for":
|
case "for":
|
||||||
ensure(TPOpen);
|
ensure(TPOpen);
|
||||||
var vname = getIdent();
|
var vname = getIdent();
|
||||||
var tk = token();
|
ensureToken(TId("in"));
|
||||||
if( !Type.enumEq(tk,TId("in")) ) unexpected(tk);
|
|
||||||
var eiter = parseExpr();
|
var eiter = parseExpr();
|
||||||
ensure(TPClose);
|
ensure(TPClose);
|
||||||
var e = parseExpr();
|
var e = parseExpr();
|
||||||
@@ -542,6 +624,9 @@ class Parser {
|
|||||||
case "break": mk(EBreak);
|
case "break": mk(EBreak);
|
||||||
case "continue": mk(EContinue);
|
case "continue": mk(EContinue);
|
||||||
case "else": unexpected(TId(id));
|
case "else": unexpected(TId(id));
|
||||||
|
case "inline":
|
||||||
|
if( !maybe(TId("function")) ) unexpected(TId("inline"));
|
||||||
|
return parseStructure("function");
|
||||||
case "function":
|
case "function":
|
||||||
var tk = token();
|
var tk = token();
|
||||||
var name = null;
|
var name = null;
|
||||||
@@ -549,51 +634,8 @@ class Parser {
|
|||||||
case TId(id): name = id;
|
case TId(id): name = id;
|
||||||
default: push(tk);
|
default: push(tk);
|
||||||
}
|
}
|
||||||
ensure(TPOpen);
|
var inf = parseFunctionDecl();
|
||||||
var args = new Array();
|
mk(EFunction(inf.args, inf.body, name, inf.ret),p1,pmax(inf.body));
|
||||||
tk = token();
|
|
||||||
if( tk != TPClose ) {
|
|
||||||
var done = false;
|
|
||||||
while( !done ) {
|
|
||||||
var name = null, opt = false;
|
|
||||||
switch( tk ) {
|
|
||||||
case TQuestion:
|
|
||||||
opt = true;
|
|
||||||
tk = token();
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
switch( tk ) {
|
|
||||||
case TId(id): name = id;
|
|
||||||
default: unexpected(tk);
|
|
||||||
}
|
|
||||||
tk = token();
|
|
||||||
var arg : Argument = { name : name };
|
|
||||||
args.push(arg);
|
|
||||||
if( opt ) arg.opt = true;
|
|
||||||
if( tk == TDoubleDot && allowTypes ) {
|
|
||||||
arg.t = parseType();
|
|
||||||
tk = token();
|
|
||||||
}
|
|
||||||
switch( tk ) {
|
|
||||||
case TComma:
|
|
||||||
tk = token();
|
|
||||||
case TPClose:
|
|
||||||
done = true;
|
|
||||||
default:
|
|
||||||
unexpected(tk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var ret = null;
|
|
||||||
if( allowTypes ) {
|
|
||||||
tk = token();
|
|
||||||
if( tk != TDoubleDot )
|
|
||||||
push(tk);
|
|
||||||
else
|
|
||||||
ret = parseType();
|
|
||||||
}
|
|
||||||
var body = parseExpr();
|
|
||||||
mk(EFunction(args, body, name, ret),p1,pmax(body));
|
|
||||||
case "return":
|
case "return":
|
||||||
var tk = token();
|
var tk = token();
|
||||||
push(tk);
|
push(tk);
|
||||||
@@ -621,18 +663,15 @@ class Parser {
|
|||||||
mk(EThrow(e),p1,pmax(e));
|
mk(EThrow(e),p1,pmax(e));
|
||||||
case "try":
|
case "try":
|
||||||
var e = parseExpr();
|
var e = parseExpr();
|
||||||
var tk = token();
|
ensureToken(TId("catch"));
|
||||||
if( !Type.enumEq(tk, TId("catch")) ) unexpected(tk);
|
|
||||||
ensure(TPOpen);
|
ensure(TPOpen);
|
||||||
var vname = getIdent();
|
var vname = getIdent();
|
||||||
ensure(TDoubleDot);
|
ensure(TDoubleDot);
|
||||||
var t = null;
|
var t = null;
|
||||||
if( allowTypes )
|
if( allowTypes )
|
||||||
t = parseType();
|
t = parseType();
|
||||||
else {
|
else
|
||||||
tk = token();
|
ensureToken(TId("Dynamic"));
|
||||||
if( !Type.enumEq(tk, TId("Dynamic")) ) unexpected(tk);
|
|
||||||
}
|
|
||||||
ensure(TPClose);
|
ensure(TPClose);
|
||||||
var ec = parseExpr();
|
var ec = parseExpr();
|
||||||
mk(ETry(e, vname, t, ec), p1, pmax(ec));
|
mk(ETry(e, vname, t, ec), p1, pmax(ec));
|
||||||
@@ -667,7 +706,7 @@ class Parser {
|
|||||||
case TId("case"), TId("default"), TBrClose:
|
case TId("case"), TId("default"), TBrClose:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
exprs.push(parseFullExpr());
|
parseFullExpr(exprs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.expr = if( exprs.length == 1)
|
c.expr = if( exprs.length == 1)
|
||||||
@@ -687,7 +726,7 @@ class Parser {
|
|||||||
case TId("case"), TId("default"), TBrClose:
|
case TId("case"), TId("default"), TBrClose:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
exprs.push(parseFullExpr());
|
parseFullExpr(exprs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def = if( exprs.length == 1)
|
def = if( exprs.length == 1)
|
||||||
@@ -712,6 +751,21 @@ class Parser {
|
|||||||
var tk = token();
|
var tk = token();
|
||||||
switch( tk ) {
|
switch( tk ) {
|
||||||
case TOp(op):
|
case TOp(op):
|
||||||
|
|
||||||
|
if( op == "->" ) {
|
||||||
|
// single arg reinterpretation of `f -> e` , `(f) -> e` and `(f:T) -> e`
|
||||||
|
switch( expr(e1) ) {
|
||||||
|
case EIdent(i), EParent(expr(_) => EIdent(i)):
|
||||||
|
var eret = parseExpr();
|
||||||
|
return mk(EFunction([{ name : i }], mk(EReturn(eret),pmin(eret))), pmin(e1));
|
||||||
|
case ECheckType(expr(_) => EIdent(i), t):
|
||||||
|
var eret = parseExpr();
|
||||||
|
return mk(EFunction([{ name : i, t : t }], mk(EReturn(eret),pmin(eret))), pmin(e1));
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
unexpected(tk);
|
||||||
|
}
|
||||||
|
|
||||||
if( unops.get(op) ) {
|
if( unops.get(op) ) {
|
||||||
if( isBlock(e1) || switch(expr(e1)) { case EParent(_): true; default: false; } ) {
|
if( isBlock(e1) || switch(expr(e1)) { case EParent(_): true; default: false; } ) {
|
||||||
push(tk);
|
push(tk);
|
||||||
@@ -740,18 +794,81 @@ class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseFunctionArgs() {
|
||||||
|
var args = new Array();
|
||||||
|
var tk = token();
|
||||||
|
if( tk != TPClose ) {
|
||||||
|
var done = false;
|
||||||
|
while( !done ) {
|
||||||
|
var name = null, opt = false;
|
||||||
|
switch( tk ) {
|
||||||
|
case TQuestion:
|
||||||
|
opt = true;
|
||||||
|
tk = token();
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
switch( tk ) {
|
||||||
|
case TId(id): name = id;
|
||||||
|
default: unexpected(tk);
|
||||||
|
}
|
||||||
|
var arg : Argument = { name : name };
|
||||||
|
args.push(arg);
|
||||||
|
if( opt ) arg.opt = true;
|
||||||
|
if( allowTypes ) {
|
||||||
|
if( maybe(TDoubleDot) )
|
||||||
|
arg.t = parseType();
|
||||||
|
if( maybe(TOp("=")) )
|
||||||
|
arg.value = parseExpr();
|
||||||
|
}
|
||||||
|
tk = token();
|
||||||
|
switch( tk ) {
|
||||||
|
case TComma:
|
||||||
|
tk = token();
|
||||||
|
case TPClose:
|
||||||
|
done = true;
|
||||||
|
default:
|
||||||
|
unexpected(tk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFunctionDecl() {
|
||||||
|
ensure(TPOpen);
|
||||||
|
var args = parseFunctionArgs();
|
||||||
|
var ret = null;
|
||||||
|
if( allowTypes ) {
|
||||||
|
var tk = token();
|
||||||
|
if( tk != TDoubleDot )
|
||||||
|
push(tk);
|
||||||
|
else
|
||||||
|
ret = parseType();
|
||||||
|
}
|
||||||
|
return { args : args, ret : ret, body : parseExpr() };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePath() {
|
||||||
|
var path = [getIdent()];
|
||||||
|
while( true ) {
|
||||||
|
var t = token();
|
||||||
|
if( t != TDot ) {
|
||||||
|
push(t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
path.push(getIdent());
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
function parseType() : CType {
|
function parseType() : CType {
|
||||||
var t = token();
|
var t = token();
|
||||||
switch( t ) {
|
switch( t ) {
|
||||||
case TId(v):
|
case TId(v):
|
||||||
var path = [v];
|
push(t);
|
||||||
while( true ) {
|
var path = parsePath();
|
||||||
t = token();
|
|
||||||
if( t != TDot )
|
|
||||||
break;
|
|
||||||
path.push(getIdent());
|
|
||||||
}
|
|
||||||
var params = null;
|
var params = null;
|
||||||
|
t = token();
|
||||||
switch( t ) {
|
switch( t ) {
|
||||||
case TOp(op):
|
case TOp(op):
|
||||||
if( op == "<" ) {
|
if( op == "<" ) {
|
||||||
@@ -782,9 +899,53 @@ class Parser {
|
|||||||
}
|
}
|
||||||
return parseTypeNext(CTPath(path, params));
|
return parseTypeNext(CTPath(path, params));
|
||||||
case TPOpen:
|
case TPOpen:
|
||||||
var t = parseType();
|
var a = token(),
|
||||||
ensure(TPClose);
|
b = token();
|
||||||
return parseTypeNext(CTParent(t));
|
|
||||||
|
push(b);
|
||||||
|
push(a);
|
||||||
|
|
||||||
|
function withReturn(args) {
|
||||||
|
switch token() { // I think it wouldn't hurt if ensure used enumEq
|
||||||
|
case TOp('->'):
|
||||||
|
case t: unexpected(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CTFun(args, parseType());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch [a, b] {
|
||||||
|
case [TPClose, _] | [TId(_), TDoubleDot]:
|
||||||
|
|
||||||
|
var args = [for (arg in parseFunctionArgs()) {
|
||||||
|
switch arg.value {
|
||||||
|
case null:
|
||||||
|
case v:
|
||||||
|
error(ECustom('Default values not allowed in function types'), #if hscriptPos v.pmin, v.pmax #else 0, 0 #end);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTNamed(arg.name, if (arg.opt) CTOpt(arg.t) else arg.t);
|
||||||
|
}];
|
||||||
|
|
||||||
|
return withReturn(args);
|
||||||
|
default:
|
||||||
|
|
||||||
|
var t = parseType();
|
||||||
|
return switch token() {
|
||||||
|
case TComma:
|
||||||
|
var args = [t];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
args.push(parseType());
|
||||||
|
if (!maybe(TComma)) break;
|
||||||
|
}
|
||||||
|
ensure(TPClose);
|
||||||
|
withReturn(args);
|
||||||
|
case TPClose:
|
||||||
|
parseTypeNext(CTParent(t));
|
||||||
|
case t: unexpected(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
case TBrOpen:
|
case TBrOpen:
|
||||||
var fields = [];
|
var fields = [];
|
||||||
var meta = null;
|
var meta = null;
|
||||||
@@ -861,6 +1022,210 @@ class Parser {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------ module -------------------------------
|
||||||
|
|
||||||
|
public function parseModule( content : String, ?origin : String = "hscript" ) {
|
||||||
|
initParser(origin);
|
||||||
|
this.input = new haxe.io.StringInput(content);
|
||||||
|
allowTypes = true;
|
||||||
|
allowMetadata = true;
|
||||||
|
var decls = [];
|
||||||
|
while( true ) {
|
||||||
|
var tk = token();
|
||||||
|
if( tk == TEof ) break;
|
||||||
|
push(tk);
|
||||||
|
decls.push(parseModuleDecl());
|
||||||
|
}
|
||||||
|
return decls;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMetadata() : Metadata {
|
||||||
|
var meta = [];
|
||||||
|
while( true ) {
|
||||||
|
var tk = token();
|
||||||
|
switch( tk ) {
|
||||||
|
case TMeta(name):
|
||||||
|
meta.push({ name : name, params : parseMetaArgs() });
|
||||||
|
default:
|
||||||
|
push(tk);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseParams() {
|
||||||
|
if( maybe(TOp("<")) )
|
||||||
|
error(EInvalidOp("Unsupported class type parameters"), readPos, readPos);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseModuleDecl() : ModuleDecl {
|
||||||
|
var meta = parseMetadata();
|
||||||
|
var ident = getIdent();
|
||||||
|
var isPrivate = false, isExtern = false;
|
||||||
|
while( true ) {
|
||||||
|
switch( ident ) {
|
||||||
|
case "private":
|
||||||
|
isPrivate = true;
|
||||||
|
case "extern":
|
||||||
|
isExtern = true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ident = getIdent();
|
||||||
|
}
|
||||||
|
switch( ident ) {
|
||||||
|
case "package":
|
||||||
|
var path = parsePath();
|
||||||
|
ensure(TSemicolon);
|
||||||
|
return DPackage(path);
|
||||||
|
case "import":
|
||||||
|
var path = [getIdent()];
|
||||||
|
var star = false;
|
||||||
|
while( true ) {
|
||||||
|
var t = token();
|
||||||
|
if( t != TDot ) {
|
||||||
|
push(t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t = token();
|
||||||
|
switch( t ) {
|
||||||
|
case TId(id):
|
||||||
|
path.push(id);
|
||||||
|
case TOp("*"):
|
||||||
|
star = true;
|
||||||
|
default:
|
||||||
|
unexpected(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ensure(TSemicolon);
|
||||||
|
return DImport(path, star);
|
||||||
|
case "class":
|
||||||
|
var name = getIdent();
|
||||||
|
var params = parseParams();
|
||||||
|
var extend = null;
|
||||||
|
var implement = [];
|
||||||
|
|
||||||
|
while( true ) {
|
||||||
|
var t = token();
|
||||||
|
switch( t ) {
|
||||||
|
case TId("extends"):
|
||||||
|
extend = parseType();
|
||||||
|
case TId("implements"):
|
||||||
|
implement.push(parseType());
|
||||||
|
default:
|
||||||
|
push(t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fields = [];
|
||||||
|
ensure(TBrOpen);
|
||||||
|
while( !maybe(TBrClose) )
|
||||||
|
fields.push(parseField());
|
||||||
|
|
||||||
|
return DClass({
|
||||||
|
name : name,
|
||||||
|
meta : meta,
|
||||||
|
params : params,
|
||||||
|
extend : extend,
|
||||||
|
implement : implement,
|
||||||
|
fields : fields,
|
||||||
|
isPrivate : isPrivate,
|
||||||
|
isExtern : isExtern,
|
||||||
|
});
|
||||||
|
case "typedef":
|
||||||
|
var name = getIdent();
|
||||||
|
var params = parseParams();
|
||||||
|
ensureToken(TOp("="));
|
||||||
|
var t = parseType();
|
||||||
|
return DTypedef({
|
||||||
|
name : name,
|
||||||
|
meta : meta,
|
||||||
|
params : params,
|
||||||
|
isPrivate : isPrivate,
|
||||||
|
t : t,
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
unexpected(TId(ident));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseField() : FieldDecl {
|
||||||
|
var meta = parseMetadata();
|
||||||
|
var access = [];
|
||||||
|
while( true ) {
|
||||||
|
var id = getIdent();
|
||||||
|
switch( id ) {
|
||||||
|
case "override":
|
||||||
|
access.push(AOverride);
|
||||||
|
case "public":
|
||||||
|
access.push(APublic);
|
||||||
|
case "private":
|
||||||
|
access.push(APrivate);
|
||||||
|
case "inline":
|
||||||
|
access.push(AInline);
|
||||||
|
case "static":
|
||||||
|
access.push(AStatic);
|
||||||
|
case "macro":
|
||||||
|
access.push(AMacro);
|
||||||
|
case "function":
|
||||||
|
var name = getIdent();
|
||||||
|
var inf = parseFunctionDecl();
|
||||||
|
return {
|
||||||
|
name : name,
|
||||||
|
meta : meta,
|
||||||
|
access : access,
|
||||||
|
kind : KFunction({
|
||||||
|
args : inf.args,
|
||||||
|
expr : inf.body,
|
||||||
|
ret : inf.ret,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
case "var":
|
||||||
|
var name = getIdent();
|
||||||
|
var get = null, set = null;
|
||||||
|
if( maybe(TPOpen) ) {
|
||||||
|
get = getIdent();
|
||||||
|
ensure(TComma);
|
||||||
|
set = getIdent();
|
||||||
|
ensure(TPClose);
|
||||||
|
}
|
||||||
|
var type = maybe(TDoubleDot) ? parseType() : null;
|
||||||
|
var expr = maybe(TOp("=")) ? parseExpr() : null;
|
||||||
|
|
||||||
|
if( expr != null ) {
|
||||||
|
if( isBlock(expr) )
|
||||||
|
maybe(TSemicolon);
|
||||||
|
else
|
||||||
|
ensure(TSemicolon);
|
||||||
|
} else if( type != null && type.match(CTAnon(_)) ) {
|
||||||
|
maybe(TSemicolon);
|
||||||
|
} else
|
||||||
|
ensure(TSemicolon);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name : name,
|
||||||
|
meta : meta,
|
||||||
|
access : access,
|
||||||
|
kind : KVar({
|
||||||
|
get : get,
|
||||||
|
set : set,
|
||||||
|
type : type,
|
||||||
|
expr : expr,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
unexpected(TId(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------ lexing -------------------------------
|
||||||
|
|
||||||
inline function incPos() {
|
inline function incPos() {
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
readPos++;
|
readPos++;
|
||||||
@@ -998,10 +1363,27 @@ class Parser {
|
|||||||
switch( char ) {
|
switch( char ) {
|
||||||
case 48,49,50,51,52,53,54,55,56,57:
|
case 48,49,50,51,52,53,54,55,56,57:
|
||||||
n = n * 10 + (char - 48);
|
n = n * 10 + (char - 48);
|
||||||
case 46:
|
case "e".code, "E".code:
|
||||||
|
var tk = token();
|
||||||
|
var pow : Null<Int> = null;
|
||||||
|
switch( tk ) {
|
||||||
|
case TConst(CInt(e)): pow = e;
|
||||||
|
case TOp("-"):
|
||||||
|
tk = token();
|
||||||
|
switch( tk ) {
|
||||||
|
case TConst(CInt(e)): pow = -e;
|
||||||
|
default: push(tk);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
push(tk);
|
||||||
|
}
|
||||||
|
if( pow == null )
|
||||||
|
invalidChar(char);
|
||||||
|
return TConst(CFloat((Math.pow(10, pow) / exp) * n * 10));
|
||||||
|
case ".".code:
|
||||||
if( exp > 0 ) {
|
if( exp > 0 ) {
|
||||||
// in case of '...'
|
// in case of '0...'
|
||||||
if( exp == 10 && readChar() == 46 ) {
|
if( exp == 10 && readChar() == ".".code ) {
|
||||||
push(TOp("..."));
|
push(TOp("..."));
|
||||||
var i = Std.int(n);
|
var i = Std.int(n);
|
||||||
return TConst( (i == n) ? CInt(i) : CFloat(n) );
|
return TConst( (i == n) ? CInt(i) : CFloat(n) );
|
||||||
@@ -1009,7 +1391,7 @@ class Parser {
|
|||||||
invalidChar(char);
|
invalidChar(char);
|
||||||
}
|
}
|
||||||
exp = 1.;
|
exp = 1.;
|
||||||
case 120: // x
|
case "x".code:
|
||||||
if( n > 0 || exp > 0 )
|
if( n > 0 || exp > 0 )
|
||||||
invalidChar(char);
|
invalidChar(char);
|
||||||
// read hexa
|
// read hexa
|
||||||
@@ -1056,11 +1438,11 @@ class Parser {
|
|||||||
return TConst( (exp > 0) ? CFloat(n * 10 / exp) : ((i == n) ? CInt(i) : CFloat(n)) );
|
return TConst( (exp > 0) ? CFloat(n * 10 / exp) : ((i == n) ? CInt(i) : CFloat(n)) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 59: return TSemicolon;
|
case ";".code: return TSemicolon;
|
||||||
case 40: return TPOpen;
|
case "(".code: return TPOpen;
|
||||||
case 41: return TPClose;
|
case ")".code: return TPClose;
|
||||||
case 44: return TComma;
|
case ",".code: return TComma;
|
||||||
case 46:
|
case ".".code:
|
||||||
char = readChar();
|
char = readChar();
|
||||||
switch( char ) {
|
switch( char ) {
|
||||||
case 48,49,50,51,52,53,54,55,56,57:
|
case 48,49,50,51,52,53,54,55,56,57:
|
||||||
@@ -1077,23 +1459,22 @@ class Parser {
|
|||||||
return TConst( CFloat(n/exp) );
|
return TConst( CFloat(n/exp) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 46:
|
case ".".code:
|
||||||
char = readChar();
|
char = readChar();
|
||||||
if( char != 46 )
|
if( char != ".".code )
|
||||||
invalidChar(char);
|
invalidChar(char);
|
||||||
return TOp("...");
|
return TOp("...");
|
||||||
default:
|
default:
|
||||||
this.char = char;
|
this.char = char;
|
||||||
return TDot;
|
return TDot;
|
||||||
}
|
}
|
||||||
case 123: return TBrOpen;
|
case "{".code: return TBrOpen;
|
||||||
case 125: return TBrClose;
|
case "}".code: return TBrClose;
|
||||||
case 91: return TBkOpen;
|
case "[".code: return TBkOpen;
|
||||||
case 93: return TBkClose;
|
case "]".code: return TBkClose;
|
||||||
case 39: return TConst( CString(readString(39)) );
|
case "'".code, '"'.code: return TConst( CString(readString(char)) );
|
||||||
case 34: return TConst( CString(readString(34)) );
|
case "?".code: return TQuestion;
|
||||||
case 63: return TQuestion;
|
case ":".code: return TDoubleDot;
|
||||||
case 58: return TDoubleDot;
|
|
||||||
case '='.code:
|
case '='.code:
|
||||||
char = readChar();
|
char = readChar();
|
||||||
if( char == '='.code )
|
if( char == '='.code )
|
||||||
@@ -1116,6 +1497,20 @@ class Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
invalidChar(char);
|
invalidChar(char);
|
||||||
|
case '#'.code:
|
||||||
|
char = readChar();
|
||||||
|
if( idents[char] ) {
|
||||||
|
var id = String.fromCharCode(char);
|
||||||
|
while( true ) {
|
||||||
|
char = readChar();
|
||||||
|
if( !idents[char] ) {
|
||||||
|
this.char = char;
|
||||||
|
return preprocess(id);
|
||||||
|
}
|
||||||
|
id += String.fromCharCode(char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invalidChar(char);
|
||||||
default:
|
default:
|
||||||
if( ops[char] ) {
|
if( ops[char] ) {
|
||||||
var op = String.fromCharCode(char);
|
var op = String.fromCharCode(char);
|
||||||
@@ -1150,6 +1545,93 @@ class Parser {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function preprocValue( id : String ) : Dynamic {
|
||||||
|
return preprocesorValues.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var preprocStack : Array<{ r : Bool }>;
|
||||||
|
|
||||||
|
function parsePreproCond() {
|
||||||
|
var tk = token();
|
||||||
|
return switch( tk ) {
|
||||||
|
case TPOpen:
|
||||||
|
push(TPOpen);
|
||||||
|
parseExpr();
|
||||||
|
case TId(id):
|
||||||
|
mk(EIdent(id), tokenMin, tokenMax);
|
||||||
|
case TOp("!"):
|
||||||
|
mk(EUnop("!", true, parsePreproCond()), tokenMin, tokenMax);
|
||||||
|
default:
|
||||||
|
unexpected(tk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function evalPreproCond( e : Expr ) {
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EIdent(id):
|
||||||
|
return preprocValue(id) != null;
|
||||||
|
case EUnop("!", _, e):
|
||||||
|
return !evalPreproCond(e);
|
||||||
|
case EParent(e):
|
||||||
|
return evalPreproCond(e);
|
||||||
|
case EBinop("&&", e1, e2):
|
||||||
|
return evalPreproCond(e1) && evalPreproCond(e2);
|
||||||
|
case EBinop("||", e1, e2):
|
||||||
|
return evalPreproCond(e1) || evalPreproCond(e2);
|
||||||
|
default:
|
||||||
|
error(EInvalidPreprocessor("Can't eval " + expr(e).getName()), readPos, readPos);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function preprocess( id : String ) : Token {
|
||||||
|
switch( id ) {
|
||||||
|
case "if":
|
||||||
|
var e = parsePreproCond();
|
||||||
|
if( evalPreproCond(e) ) {
|
||||||
|
preprocStack.push({ r : true });
|
||||||
|
return token();
|
||||||
|
}
|
||||||
|
preprocStack.push({ r : false });
|
||||||
|
skipTokens();
|
||||||
|
return token();
|
||||||
|
case "else", "elseif" if( preprocStack.length > 0 ):
|
||||||
|
if( preprocStack[preprocStack.length - 1].r ) {
|
||||||
|
preprocStack[preprocStack.length - 1].r = false;
|
||||||
|
skipTokens();
|
||||||
|
return token();
|
||||||
|
} else if( id == "else" ) {
|
||||||
|
preprocStack.pop();
|
||||||
|
preprocStack.push({ r : true });
|
||||||
|
return token();
|
||||||
|
} else {
|
||||||
|
// elseif
|
||||||
|
preprocStack.pop();
|
||||||
|
return preprocess("if");
|
||||||
|
}
|
||||||
|
case "end" if( preprocStack.length > 0 ):
|
||||||
|
preprocStack.pop();
|
||||||
|
return token();
|
||||||
|
default:
|
||||||
|
return TPrepro(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function skipTokens() {
|
||||||
|
var spos = preprocStack.length - 1;
|
||||||
|
var obj = preprocStack[spos];
|
||||||
|
var pos = readPos;
|
||||||
|
while( true ) {
|
||||||
|
var tk = token();
|
||||||
|
if( tk == TEof )
|
||||||
|
error(EInvalidPreprocessor("Unclosed"), pos, pos);
|
||||||
|
if( preprocStack[spos] != obj ) {
|
||||||
|
push(tk);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function tokenComment( op : String, char : Int ) {
|
function tokenComment( op : String, char : Int ) {
|
||||||
var c = op.charCodeAt(1);
|
var c = op.charCodeAt(1);
|
||||||
var s = input;
|
var s = input;
|
||||||
@@ -1221,6 +1703,7 @@ class Parser {
|
|||||||
case TQuestion: "?";
|
case TQuestion: "?";
|
||||||
case TDoubleDot: ":";
|
case TDoubleDot: ":";
|
||||||
case TMeta(id): "@" + id;
|
case TMeta(id): "@" + id;
|
||||||
|
case TPrepro(id): "#" + id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+86
-1
@@ -1,3 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2008-2017 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 hscript;
|
package hscript;
|
||||||
import hscript.Expr;
|
import hscript.Expr;
|
||||||
|
|
||||||
@@ -16,10 +37,20 @@ class Printer {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function typeToString( t : CType ) {
|
||||||
|
buf = new StringBuf();
|
||||||
|
tabs = "";
|
||||||
|
type(t);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
inline function add<T>(s:T) buf.add(s);
|
inline function add<T>(s:T) buf.add(s);
|
||||||
|
|
||||||
function type( t : CType ) {
|
function type( t : CType ) {
|
||||||
switch( t ) {
|
switch( t ) {
|
||||||
|
case CTOpt(t):
|
||||||
|
add('?');
|
||||||
|
type(t);
|
||||||
case CTPath(path, params):
|
case CTPath(path, params):
|
||||||
add(path.join("."));
|
add(path.join("."));
|
||||||
if( params != null ) {
|
if( params != null ) {
|
||||||
@@ -31,6 +62,19 @@ class Printer {
|
|||||||
}
|
}
|
||||||
add(">");
|
add(">");
|
||||||
}
|
}
|
||||||
|
case CTNamed(name, t):
|
||||||
|
add(name);
|
||||||
|
add(':');
|
||||||
|
type(t);
|
||||||
|
case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))):
|
||||||
|
add('(');
|
||||||
|
for (a in args)
|
||||||
|
switch a {
|
||||||
|
case CTNamed(_, _): type(a);
|
||||||
|
default: type(CTNamed('_', a));
|
||||||
|
}
|
||||||
|
add(')->');
|
||||||
|
type(ret);
|
||||||
case CTFun(args, ret):
|
case CTFun(args, ret):
|
||||||
if( args.length == 0 )
|
if( args.length == 0 )
|
||||||
add("Void -> ");
|
add("Void -> ");
|
||||||
@@ -258,6 +302,26 @@ class Printer {
|
|||||||
add(";\n");
|
add(";\n");
|
||||||
}
|
}
|
||||||
add("}");
|
add("}");
|
||||||
|
case EMeta(name, args, e):
|
||||||
|
add("@");
|
||||||
|
add(name);
|
||||||
|
if( args != null && args.length > 0 ) {
|
||||||
|
add("(");
|
||||||
|
var first = true;
|
||||||
|
for( a in args ) {
|
||||||
|
if( first ) first = false else add(", ");
|
||||||
|
expr(e);
|
||||||
|
}
|
||||||
|
add(")");
|
||||||
|
}
|
||||||
|
add(" ");
|
||||||
|
expr(e);
|
||||||
|
case ECheckType(e, t):
|
||||||
|
add("(");
|
||||||
|
expr(e);
|
||||||
|
add(" : ");
|
||||||
|
addType(t);
|
||||||
|
add(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,4 +329,25 @@ class Printer {
|
|||||||
return new Printer().exprToString(e);
|
return new Printer().exprToString(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
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 EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||||
|
case EUnterminatedString: "Unterminated string";
|
||||||
|
case EUnterminatedComment: "Unterminated comment";
|
||||||
|
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
|
||||||
|
case EUnknownVariable(v): "Unknown variable: "+v;
|
||||||
|
case EInvalidIterator(v): "Invalid iterator: "+v;
|
||||||
|
case EInvalidOp(op): "Invalid operator: "+op;
|
||||||
|
case EInvalidAccess(f): "Invalid access to field " + f;
|
||||||
|
case ECustom(msg): msg;
|
||||||
|
};
|
||||||
|
#if hscriptPos
|
||||||
|
return e.origin + ":" + e.line + ": " + message;
|
||||||
|
#else
|
||||||
|
return message;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+45
-4
@@ -1,10 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C)2008-2017 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 hscript;
|
package hscript;
|
||||||
import hscript.Expr;
|
import hscript.Expr;
|
||||||
|
|
||||||
class Tools {
|
class Tools {
|
||||||
|
|
||||||
public static function iter( e : Expr, f : Expr -> Void ) {
|
public static function iter( e : Expr, f : Expr -> Void ) {
|
||||||
switch( e ) {
|
switch( expr(e) ) {
|
||||||
case EConst(_), EIdent(_):
|
case EConst(_), EIdent(_):
|
||||||
case EVar(_, _, e): if( e != null ) f(e);
|
case EVar(_, _, e): if( e != null ) f(e);
|
||||||
case EParent(e): f(e);
|
case EParent(e): f(e);
|
||||||
@@ -34,12 +55,14 @@ class Tools {
|
|||||||
f(c.expr);
|
f(c.expr);
|
||||||
}
|
}
|
||||||
if( def != null ) f(def);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function map( e : Expr, f : Expr -> Expr ) {
|
public static function map( e : Expr, f : Expr -> Expr ) {
|
||||||
return switch( e ) {
|
var edef = switch( expr(e) ) {
|
||||||
case EConst(_), EIdent(_): e;
|
case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
|
||||||
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null);
|
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null);
|
||||||
case EParent(e): EParent(f(e));
|
case EParent(e): EParent(f(e));
|
||||||
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
||||||
@@ -51,7 +74,6 @@ class Tools {
|
|||||||
case EWhile(c, e): EWhile(f(c),f(e));
|
case EWhile(c, e): EWhile(f(c),f(e));
|
||||||
case EDoWhile(c, e): EDoWhile(f(c),f(e));
|
case EDoWhile(c, e): EDoWhile(f(c),f(e));
|
||||||
case EFor(v, it, e): EFor(v, f(it), f(e));
|
case EFor(v, it, e): EFor(v, f(it), f(e));
|
||||||
case EBreak, EContinue: e;
|
|
||||||
case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
|
case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
|
||||||
case EReturn(e): EReturn(if( e != null ) f(e) else null);
|
case EReturn(e): EReturn(if( e != null ) f(e) else null);
|
||||||
case EArray(e, i): EArray(f(e),f(i));
|
case EArray(e, i): EArray(f(e),f(i));
|
||||||
@@ -62,7 +84,26 @@ class Tools {
|
|||||||
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
|
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
|
||||||
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
|
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
|
||||||
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 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);
|
||||||
}
|
}
|
||||||
|
return mk(edef, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function expr( e : Expr ) : ExprDef {
|
||||||
|
#if hscriptPos
|
||||||
|
return e.e;
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function mk( e : ExprDef, p : Expr ) {
|
||||||
|
#if hscriptPos
|
||||||
|
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
set PATH="C:\Program Files\7-Zip";%PATH%
|
||||||
rm -rf release
|
rm -rf release
|
||||||
mkdir release
|
mkdir release
|
||||||
cp haxelib.json README.md extraParams.hxml release
|
cp haxelib.json README.md extraParams.hxml release
|
||||||
|
|||||||
Reference in New Issue
Block a user