Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ad52138fe | ||
|
|
a616f2affe | ||
|
|
6721a838ca | ||
|
|
826f0184bf | ||
|
|
6c20ad3a8f | ||
|
|
fd5a2d9143 | ||
|
|
8da9ed942d | ||
|
|
f31f380602 | ||
|
|
85ea6c6f44 | ||
|
|
13bdf30055 | ||
|
|
6daea2fb5b | ||
|
|
f18c7a2210 | ||
|
|
123a7de64a | ||
|
|
bc8a59877e | ||
|
|
4347fe9305 | ||
|
|
e0e1436930 | ||
|
|
a41222328a | ||
|
|
080c441f24 | ||
|
|
d6d6e7a45e | ||
|
|
cb645d6bb1 | ||
|
|
5d58ea56c9 | ||
|
|
4170a4aafc | ||
|
|
19021058c4 | ||
|
|
15bd6b038a | ||
|
|
4d274954e3 | ||
|
|
aff2d0766d | ||
|
|
38634e1528 | ||
|
|
5ef937d6d4 | ||
|
|
22bbe04f77 | ||
|
|
18382c4621 | ||
|
|
ad93c1bc18 | ||
|
|
238c24501b | ||
|
|
d38ddbb2b4 | ||
|
|
fb8f35e2a5 | ||
|
|
6c77566fea | ||
|
|
b7c12943fa | ||
|
|
50e87c504d | ||
|
|
aaac98d7b0 | ||
|
|
b6df96a4b0 | ||
|
|
c0d4540d4b | ||
|
|
ad05952685 | ||
|
|
340d63003f | ||
|
|
177bb9f75b | ||
|
|
e876db00e4 | ||
|
|
cd3bc5b194 | ||
|
|
b13d449b78 | ||
|
|
006b0e1f32 | ||
|
|
0ca6eab16e | ||
|
|
df1c5ad97c | ||
|
|
8c1aaf5454 | ||
|
|
9288a9df10 | ||
|
|
a727148447 | ||
|
|
a16e74dee6 | ||
|
|
fdde089908 | ||
|
|
6346f82edf | ||
|
|
10dd51ebe8 | ||
|
|
e97c79d14c | ||
|
|
418843c606 | ||
|
|
049daf4b0d | ||
|
|
b105ac0453 | ||
|
|
d2aa99eff7 | ||
|
|
934848ff8d | ||
|
|
f512ac4cde | ||
|
|
b96e6c8149 | ||
|
|
f6402ac638 | ||
|
|
1e55d0200b | ||
|
|
1725dfd1ea | ||
|
|
a8cc0ccc86 | ||
|
|
7dd868aac7 | ||
|
|
220e5ce007 | ||
|
|
80f6ea4502 | ||
|
|
eb820aaf6a | ||
|
|
a754c162d9 | ||
|
|
de18ceb887 | ||
|
|
51188173bb | ||
|
|
d0797ad5d8 | ||
|
|
9b37bd532e | ||
|
|
333352edcf | ||
|
|
05783773af | ||
|
|
59ee20ee7a | ||
|
|
87f2c1b062 | ||
|
|
b85cefea56 | ||
|
|
2e28f863f3 | ||
|
|
ddaa4d1741 | ||
|
|
732bd1520a | ||
|
|
1d67946f3f | ||
|
|
fab6d7e662 | ||
|
|
6e20578a81 | ||
|
|
dc5282c32e | ||
|
|
812abae6e5 | ||
|
|
b3b512d027 | ||
|
|
ed296c4c7f | ||
|
|
595bb47249 | ||
|
|
daad1a4fa8 | ||
|
|
daef1ac198 | ||
|
|
3b9d88b252 | ||
|
|
809f8229b4 | ||
|
|
ffb1ee2523 | ||
|
|
0903718314 | ||
|
|
e0cb883ae4 | ||
|
|
17449dd9d5 | ||
|
|
7963631a1d | ||
|
|
77d88f4b34 | ||
|
|
c5e3ab1162 | ||
|
|
75041c1db9 | ||
|
|
558a4c0782 | ||
|
|
995c3b9c8e | ||
|
|
0a64bca9ce | ||
|
|
b7b12b6cbb | ||
|
|
5684ca8671 | ||
|
|
3c81360dc2 | ||
|
|
fc50ebdf90 | ||
|
|
4d6e8db5ff | ||
|
|
938ffac57e | ||
|
|
50aaffb101 | ||
|
|
4f9b449c6e | ||
|
|
c1a36a3f4b | ||
|
|
4ca6e2a680 | ||
|
|
11db805c34 | ||
|
|
f1f700ab69 | ||
|
|
9eeb8ddb80 | ||
|
|
95de26a6d0 | ||
|
|
88c2e28dbc | ||
|
|
30b7a9e4d4 | ||
|
|
37542065d6 | ||
|
|
c9608f7760 | ||
|
|
6ba54b0e77 | ||
|
|
f9bb3105c8 | ||
|
|
4b5baa9b5f | ||
|
|
6dedd87260 | ||
|
|
91317222fb | ||
|
|
494953a8cc | ||
|
|
ae4d8be50c | ||
|
|
d34790e05c | ||
|
|
b77e15c6d6 | ||
|
|
54195d5811 | ||
|
|
9c7907f91a | ||
|
|
0601b8def6 | ||
|
|
841261f199 | ||
|
|
7124b6c4ce | ||
|
|
ad7beec559 | ||
|
|
bd4fc51392 | ||
|
|
54d994a879 | ||
|
|
891a085f2b | ||
|
|
e7ccd2e72d | ||
|
|
459a39e65d | ||
|
|
36eb0fca8b | ||
|
|
53b9e22f26 | ||
|
|
8ff3f5e351 | ||
|
|
e6a2c443cb | ||
|
|
4f25ab162d | ||
|
|
0520d9f187 | ||
|
|
cc3c21b7e3 | ||
|
|
ae9560124e | ||
|
|
085f4ec37e | ||
|
|
476c3a9cc9 | ||
|
|
1bc5ac8768 | ||
|
|
4bdff11b87 | ||
|
|
b32dde3c91 | ||
|
|
b9a566350b | ||
|
|
ce9ce674ea | ||
|
|
b8641779bd | ||
|
|
5e620d784e | ||
|
|
013bcfaffb | ||
|
|
2504c96927 | ||
|
|
e2f4d1481e | ||
|
|
ea3d68573b | ||
|
|
c6cb594ef1 | ||
|
|
948123bc7a | ||
|
|
68f5b26af9 | ||
|
|
5d493630b1 | ||
|
|
f4db6faa80 | ||
|
|
e340f7f5ad | ||
|
|
b4d923b74c | ||
|
|
965f334d59 | ||
|
|
54463b78d2 | ||
|
|
600aec1f50 | ||
|
|
bcfbe81979 | ||
|
|
31e8851bcc | ||
|
|
f464767ee7 | ||
|
|
22eb58b3de | ||
|
|
0daee20545 | ||
|
|
d98073dc33 | ||
|
|
fb199e7dea | ||
|
|
6cea0d97c2 | ||
|
|
058dd2e01e | ||
|
|
975d122491 | ||
|
|
eef4c66374 | ||
|
|
5ce4fbd74d | ||
|
|
f4d8cff26b | ||
|
|
46b6b3c151 | ||
|
|
796b217dec | ||
|
|
1ccc35f464 | ||
|
|
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 | ||
|
|
09923651d8 | ||
|
|
7ca5b85051 | ||
|
|
a34faf8a26 | ||
|
|
7708159e92 | ||
|
|
7851d48e82 | ||
|
|
f320b4fa9b | ||
|
|
b3ea81f4e8 |
@@ -1,2 +1,4 @@
|
|||||||
/hscript.swf
|
/hscript.swf
|
||||||
/release.zip
|
/release.zip
|
||||||
|
|
||||||
|
tests/bin/*
|
||||||
+18
-24
@@ -4,10 +4,11 @@ os:
|
|||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
|
|
||||||
osx_image: xcode6.4
|
dist: bionic
|
||||||
|
|
||||||
haxe:
|
haxe:
|
||||||
- "3.2.1"
|
- "3.4.7"
|
||||||
|
- "4.0.3"
|
||||||
- development
|
- development
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
@@ -19,44 +20,34 @@ addons:
|
|||||||
# Python
|
# Python
|
||||||
- python3
|
- python3
|
||||||
# PHP
|
# PHP
|
||||||
- php5-cli
|
- php-cli
|
||||||
|
- php-mbstring
|
||||||
# C#
|
# C#
|
||||||
- mono-devel
|
- mono-devel
|
||||||
- mono-mcs
|
- mono-mcs
|
||||||
# Flash
|
|
||||||
- libcurl3:i386
|
|
||||||
- libglib2.0-0:i386
|
|
||||||
- libx11-6:i386
|
|
||||||
- libxext6:i386
|
|
||||||
- libxt6:i386
|
|
||||||
- libxcursor1:i386
|
|
||||||
- libnss3:i386
|
|
||||||
- libgtk2.0-0:i386
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
# os-specific config
|
# os-specific config
|
||||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||||
export DISPLAY=:99.0;
|
|
||||||
export AUDIODEV=null;
|
|
||||||
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
|
||||||
brew update;
|
brew update;
|
||||||
brew install mono;
|
brew install mono;
|
||||||
brew install python3;
|
brew install php;
|
||||||
fi
|
fi
|
||||||
# Download and setup a standalone flash player debugger
|
|
||||||
- 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
|
||||||
|
|
||||||
@@ -65,11 +56,14 @@ 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
|
||||||
- haxe bin/build-cs.hxml && mono bin/bin/Test.exe
|
# Only test C# for Haxe 4+
|
||||||
|
- if [[ $(haxe -version 2&>1 | head -c 1) > 3 ]]; then
|
||||||
|
haxe bin/build-cs.hxml && mono bin/bin/Test.exe;
|
||||||
|
fi
|
||||||
- haxe bin/build-cpp.hxml && ./bin/Test
|
- haxe bin/build-cpp.hxml && ./bin/Test
|
||||||
- haxe bin/build-flash.hxml -D fdb && haxe flash/run.hxml bin/Test.swf
|
- haxe bin/build-php.hxml -D php7 && php bin/index.php
|
||||||
- haxe bin/build-php.hxml && php bin/index.php
|
|
||||||
- haxe bin/build-python.hxml && python3 bin/Test.py
|
- haxe bin/build-python.hxml && python3 bin/Test.py
|
||||||
|
|||||||
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.
|
||||||
|
|
||||||
@@ -1,8 +1,18 @@
|
|||||||
hscript
|
hscript-improved
|
||||||
=======
|
=======
|
||||||
|
|
||||||
[](https://travis-ci.org/HaxeFoundation/hscript)
|
How to install
|
||||||
[](https://ci.appveyor.com/project/HaxeFoundation/hscript)
|
```
|
||||||
|
haxelib git hscript-improved https://github.com/FNF-CNE-Devs/hscript-improved.git
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable custom classes support you have to do this in project.xml
|
||||||
|
```xml
|
||||||
|
<define name="CUSTOM_CLASSES" />
|
||||||
|
```
|
||||||
|
Warning: custom classes are sometimes broken, would like help to fix them. You can only override functions from the current class, not from the extended part, like you cant override update in FlxText because FlxText doesnt have a update function overriden
|
||||||
|
|
||||||
|
-----------
|
||||||
|
|
||||||
Parse and evalutate Haxe expressions.
|
Parse and evalutate Haxe expressions.
|
||||||
|
|
||||||
@@ -64,26 +74,40 @@ 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.
|
||||||
|
|
||||||
|
You can use `hscript.Checker` in order to type check and even get completion, using `haxe -xml` output for type information.
|
||||||
|
|
||||||
Limitations
|
Limitations
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Compared to Haxe, limitations are :
|
Compared to Haxe, limitations are :
|
||||||
|
|
||||||
- no type declarations (classes, enums, typedefs) : only expressions
|
|
||||||
- `switch` construct is supported but not pattern matching (no variable capture, we use strict equality to compare `case` values and `switch` value)
|
- `switch` construct is supported but not pattern matching (no variable capture, we use strict equality to compare `case` values and `switch` value)
|
||||||
- only one variable declaration is allowed in `var`
|
- only one variable declaration is allowed in `var`
|
||||||
- the parser supports optional types for `var` and `function` if `allowTypes` is set, but the interpreter ignores them
|
- the parser supports optional types for `var` and `function` if `allowTypes` is set, but the interpreter ignores them
|
||||||
- you can enable per-expression position tracking by compiling with `-D hscriptPos`
|
- you can enable per-expression position tracking by compiling with `-D hscriptPos`
|
||||||
|
- you can parse some type declarations (import, class, typedef, etc.) with parseModule
|
||||||
|
|
||||||
Install
|
Install
|
||||||
-------
|
-------
|
||||||
|
|
||||||
In order to install Haxe Script, use `haxelib install hscript` and compile your program with `-lib hscript`.
|
In order to install Haxe Script, use `haxelib install hscript` and compile your program with `-lib hscript`.
|
||||||
|
|
||||||
There are only three files in hscript :
|
These are the main required files in hscript :
|
||||||
|
|
||||||
- `hscript.Expr` : contains enums declarations
|
- `hscript.Expr` : contains enums declarations
|
||||||
- `hscript.Parser` : a small parser that turns a string into an expression structure (AST)
|
- `hscript.Parser` : a small parser that turns a string into an expression structure (AST)
|
||||||
- `hscript.Interp` : a small interpreter that execute the AST and returns the latest evaluated value
|
- `hscript.Interp` : a small interpreter that execute the AST and returns the latest evaluated value
|
||||||
|
|
||||||
|
Some other optional files :
|
||||||
|
|
||||||
|
- `hscript.Async` : converts Expr into asynchronous version
|
||||||
|
- `hscript.Bytes` : Expr serializer/unserializer
|
||||||
|
- `hscript.Checker` : type checking and completion for hscript Expr
|
||||||
|
- `hscript.Macro` : convert Haxe macro into hscript Expr
|
||||||
|
- `hscript.Printer` : convert hscript Expr to String
|
||||||
|
- `hscript.Tools` : utility functions (map/iter)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
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 hscript.Checker;
|
||||||
import haxe.unit.*;
|
import haxe.unit.*;
|
||||||
|
|
||||||
class Test extends TestCase {
|
class TestHScript 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,14 +19,21 @@ 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 {
|
||||||
assertScript("0",0);
|
assertScript("0",0);
|
||||||
assertScript("0xFF", 255);
|
assertScript("0xFF", 255);
|
||||||
assertScript("0xBFFFFFFF", 0xBFFFFFFF);
|
#if !(php || python)
|
||||||
assertScript("0x7FFFFFFF", 0x7FFFFFFF);
|
#if haxe3
|
||||||
|
assertScript("0xBFFFFFFF", 0xBFFFFFFF);
|
||||||
|
assertScript("0x7FFFFFFF", 0x7FFFFFFF);
|
||||||
|
#elseif !neko
|
||||||
|
assertScript("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
|
||||||
|
assertScript("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
|
||||||
|
#end
|
||||||
|
#end
|
||||||
assertScript("-123",-123);
|
assertScript("-123",-123);
|
||||||
assertScript("- 123",-123);
|
assertScript("- 123",-123);
|
||||||
assertScript("1.546",1.546);
|
assertScript("1.546",1.546);
|
||||||
@@ -89,6 +100,19 @@ 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 = () -> 55; f()", 55);
|
||||||
|
assertScript("var f = (x) -> x + 1; f(3)", 4);
|
||||||
|
assertScript("var f = (x:Int) -> x + 1; f(3)", 4);
|
||||||
|
assertScript("var f = (x,y) -> x + y; f(3,1)", 4);
|
||||||
|
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);
|
||||||
|
assertScript("var a = 10; var b = 5; a - -b", 15);
|
||||||
|
assertScript("var a = 10; var b = 5; a - b / 2", 7.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testMap():Void {
|
function testMap():Void {
|
||||||
@@ -142,9 +166,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 TestHScript());
|
||||||
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
-10
@@ -5,20 +5,19 @@ 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.2.1 -y
|
- cinst haxe -version 4.0.3 -y
|
||||||
- RefreshEnv
|
- RefreshEnv
|
||||||
# Setup haxelib
|
# Setup haxelib
|
||||||
- mkdir "%HAXELIB_ROOT%"
|
- mkdir "%HAXELIB_ROOT%"
|
||||||
- haxelib setup "%HAXELIB_ROOT%"
|
- haxelib setup "%HAXELIB_ROOT%"
|
||||||
# Install test dependencies
|
|
||||||
- cinst php -y
|
|
||||||
# Download and setup a standalone flash player debugger
|
|
||||||
- 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
|
||||||
|
|
||||||
@@ -34,6 +33,4 @@ test_script:
|
|||||||
- haxe bin/build-java.hxml && java -jar bin\Test.jar
|
- haxe bin/build-java.hxml && java -jar bin\Test.jar
|
||||||
- haxe bin/build-cs.hxml && bin\bin\Test.exe
|
- haxe bin/build-cs.hxml && bin\bin\Test.exe
|
||||||
- haxe bin/build-cpp.hxml && bin\Test.exe
|
- haxe bin/build-cpp.hxml && bin\Test.exe
|
||||||
- haxe bin/build-flash.hxml -D fdb && haxe flash\run.hxml bin\Test.swf
|
|
||||||
- haxe bin/build-php.hxml && C:\tools\php\php.exe bin\index.php
|
|
||||||
- haxe bin/build-python.hxml && C:\Python34-x64\python.exe bin\Test.py
|
- haxe bin/build-python.hxml && C:\Python34-x64\python.exe bin\Test.py
|
||||||
+2
-1
@@ -1,2 +1,3 @@
|
|||||||
-main Test
|
-main TestHScript
|
||||||
-dce no
|
-dce no
|
||||||
|
-lib hx3compat
|
||||||
@@ -1 +1,4 @@
|
|||||||
--macro keep('IntIterator')
|
--macro keep('IntIterator')
|
||||||
|
--macro hscript.macros.UsingHandler.init()
|
||||||
|
--macro hscript.macros.ClassExtendMacro.init()
|
||||||
|
--define HSCRIPT_NEW
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
*.exe
|
|
||||||
*.app
|
|
||||||
flashplayerdebugger
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
import Sys.*;
|
|
||||||
import sys.io.File.*;
|
|
||||||
import haxe.*;
|
|
||||||
import haxe.io.*;
|
|
||||||
|
|
||||||
class Install {
|
|
||||||
// https://www.adobe.com/support/flashplayer/downloads.html
|
|
||||||
static var fpDownload(default, never) = switch (systemName()) {
|
|
||||||
case "Linux":
|
|
||||||
"https://fpdownload.macromedia.com/pub/flashplayer/updaters/11/flashplayer_11_sa_debug.i386.tar.gz";
|
|
||||||
case "Mac":
|
|
||||||
"https://fpdownload.macromedia.com/pub/flashplayer/updaters/21/flashplayer_21_sa_debug.dmg";
|
|
||||||
case "Windows":
|
|
||||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/21/flashplayer_21_sa_debug.exe";
|
|
||||||
case _:
|
|
||||||
throw "unsupported system";
|
|
||||||
}
|
|
||||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
|
||||||
static var mmcfg(default, never) = switch (systemName()) {
|
|
||||||
case "Linux":
|
|
||||||
Path.join([getEnv("HOME"), "mm.cfg"]);
|
|
||||||
case "Mac":
|
|
||||||
"/Library/Application Support/Macromedia/mm.cfg";
|
|
||||||
case "Windows":
|
|
||||||
Path.join([getEnv("HOMEDRIVE") + getEnv("HOMEPATH"), "mm.cfg"]);
|
|
||||||
case _:
|
|
||||||
throw "unsupported system";
|
|
||||||
}
|
|
||||||
// http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7c95.html
|
|
||||||
static var fpTrust(default, never) = switch (systemName()) {
|
|
||||||
case "Linux":
|
|
||||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/#Security/FlashPlayerTrust"]);
|
|
||||||
case "Mac":
|
|
||||||
"/Library/Application Support/Macromedia/FlashPlayerTrust";
|
|
||||||
case "Windows":
|
|
||||||
Path.join([getEnv("SYSTEMROOT"), "system32", "Macromed", "Flash", "FlashPlayerTrust"]);
|
|
||||||
case _:
|
|
||||||
throw "unsupported system";
|
|
||||||
}
|
|
||||||
static function main() {
|
|
||||||
switch (systemName()) {
|
|
||||||
case "Linux":
|
|
||||||
// Download and unzip flash player
|
|
||||||
if (command("wget", [fpDownload]) != 0)
|
|
||||||
throw "failed to download flash player";
|
|
||||||
if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0)
|
|
||||||
throw "failed to extract flash player";
|
|
||||||
case "Mac":
|
|
||||||
if (command("brew", ["tap", "caskroom/versions"]) != 0)
|
|
||||||
throw "failed to brew tap caskroom/versions";
|
|
||||||
if (command("brew", ["cask", "install", "flash-player-debugger"]) != 0)
|
|
||||||
throw "failed to install flash-player-debugger";
|
|
||||||
case "Windows":
|
|
||||||
// Download flash player
|
|
||||||
download(fpDownload, "flash\\flashplayer.exe");
|
|
||||||
case _:
|
|
||||||
throw "unsupported system";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Create a configuration file so the trace log is enabled
|
|
||||||
createDirectory(Path.directory(mmcfg));
|
|
||||||
saveContent(mmcfg, "ErrorReportingEnable=1\nTraceOutputFileEnable=1");
|
|
||||||
|
|
||||||
// Add the current directory as trusted, so exit() can be used
|
|
||||||
createDirectory(fpTrust);
|
|
||||||
saveContent(Path.join([fpTrust, "test.cfg"]), getCwd());
|
|
||||||
}
|
|
||||||
static function download(url:String, saveAs:String):Void {
|
|
||||||
var http = new Http(url);
|
|
||||||
http.onError = function(e) {
|
|
||||||
throw e;
|
|
||||||
};
|
|
||||||
http.customRequest(false, write(saveAs));
|
|
||||||
}
|
|
||||||
static function createDirectory(dir:String):Void {
|
|
||||||
try {
|
|
||||||
sys.FileSystem.createDirectory(dir);
|
|
||||||
} catch(e:Dynamic) {
|
|
||||||
switch (systemName()) {
|
|
||||||
case "Mac", "Linux":
|
|
||||||
if (command("sudo", ["mkdir", "-p", dir]) != 0)
|
|
||||||
throw 'cannot create $dir';
|
|
||||||
if (command("sudo", ["chmod", "a+rw", dir]) != 0)
|
|
||||||
throw 'cannot set permission of $dir';
|
|
||||||
case _:
|
|
||||||
neko.Lib.rethrow(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import Sys.*;
|
|
||||||
import sys.FileSystem.*;
|
|
||||||
import sys.io.File.*;
|
|
||||||
import haxe.io.*;
|
|
||||||
|
|
||||||
class Run {
|
|
||||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
|
||||||
static var flashlog(default, never) = switch (systemName()) {
|
|
||||||
case "Linux":
|
|
||||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/Logs/flashlog.txt"]);
|
|
||||||
case "Mac":
|
|
||||||
Path.join([getEnv("HOME"), "Library/Preferences/Macromedia/Flash Player/Logs/flashlog.txt"]);
|
|
||||||
case "Windows":
|
|
||||||
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "Logs", "flashlog.txt"]);
|
|
||||||
case _:
|
|
||||||
throw "unsupported system";
|
|
||||||
}
|
|
||||||
static function main() {
|
|
||||||
var args = args();
|
|
||||||
var swf = args[0];
|
|
||||||
var exitCode = switch (systemName()) {
|
|
||||||
case "Linux":
|
|
||||||
// The flash player has some issues with unexplained crashes,
|
|
||||||
// but if it runs about 8 times, it should succeed one of those...
|
|
||||||
var c = -1;
|
|
||||||
for (i in 0...8) {
|
|
||||||
if ((c = command("xvfb-run", ["flash/flashplayerdebugger", swf])) == 0)
|
|
||||||
break;
|
|
||||||
println('retry... (${i+1})');
|
|
||||||
}
|
|
||||||
c;
|
|
||||||
case "Mac":
|
|
||||||
command("/Applications/Flash Player Debugger.app/Contents/MacOS/Flash Player Debugger", [fullPath(swf)]);
|
|
||||||
case "Windows":
|
|
||||||
command("flash\\flashplayer.exe", [fullPath(swf)]);
|
|
||||||
case _:
|
|
||||||
throw "unsupported platform";
|
|
||||||
}
|
|
||||||
if (exists(flashlog))
|
|
||||||
println(getContent(flashlog));
|
|
||||||
else {
|
|
||||||
println('does not exist: $flashlog');
|
|
||||||
var parts = Path.normalize(flashlog).split("/");
|
|
||||||
println(parts);
|
|
||||||
for (i in 0...parts.length-1) {
|
|
||||||
var path = parts.splice(0, i+1).join("/");
|
|
||||||
println('ls $path');
|
|
||||||
command("ls", [path]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit(exitCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
-cp flash
|
|
||||||
--run Install
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
-cp flash
|
|
||||||
--run Run
|
|
||||||
+3
-3
@@ -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.0.7",
|
"version": "2.4.0",
|
||||||
"releasenote": "fixes for haxe 3.3, use property access by default",
|
"releasenote": "Haxe 4.0 final + type checking/completion (hscript.Checker)",
|
||||||
"contributors": ["ncannasse"]
|
"contributors": ["ncannasse","HaxeFoundation"]
|
||||||
}
|
}
|
||||||
+2
-1
@@ -1 +1,2 @@
|
|||||||
bin/build-flash.hxml
|
bin/build-each.hxml
|
||||||
|
-hl bin/Test.hl
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<project version="2">
|
|
||||||
<!-- Output SWF options -->
|
|
||||||
<output>
|
|
||||||
<movie outputType="Application" />
|
|
||||||
<movie input="" />
|
|
||||||
<movie path="hscript.swf" />
|
|
||||||
<movie fps="30" />
|
|
||||||
<movie width="800" />
|
|
||||||
<movie height="600" />
|
|
||||||
<movie version="9" />
|
|
||||||
<movie minorVersion="0" />
|
|
||||||
<movie platform="Flash Player" />
|
|
||||||
<movie background="#FFFFFF" />
|
|
||||||
</output>
|
|
||||||
<!-- Other classes to be compiled into your SWF -->
|
|
||||||
<classpaths>
|
|
||||||
<!-- example: <class path="..." /> -->
|
|
||||||
</classpaths>
|
|
||||||
<!-- Build options -->
|
|
||||||
<build>
|
|
||||||
<option directives="" />
|
|
||||||
<option flashStrict="True" />
|
|
||||||
<option noInlineOnDebug="False" />
|
|
||||||
<option mainClass="Test" />
|
|
||||||
<option enabledebug="False" />
|
|
||||||
<option additional="-dce no" />
|
|
||||||
</build>
|
|
||||||
<!-- haxelib libraries -->
|
|
||||||
<haxelib>
|
|
||||||
<!-- example: <library name="..." /> -->
|
|
||||||
</haxelib>
|
|
||||||
<!-- Class files to compile (other referenced classes will automatically be included) -->
|
|
||||||
<compileTargets>
|
|
||||||
<!-- example: <compile path="..." /> -->
|
|
||||||
</compileTargets>
|
|
||||||
<!-- Assets to embed into the output SWF -->
|
|
||||||
<library>
|
|
||||||
<!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
|
|
||||||
</library>
|
|
||||||
<!-- Paths to exclude from the Project Explorer tree -->
|
|
||||||
<hiddenPaths>
|
|
||||||
<hidden path="haxelib.xml" />
|
|
||||||
<hidden path="hscript.hxml" />
|
|
||||||
<hidden path="hscript.js" />
|
|
||||||
<hidden path="hscript.n" />
|
|
||||||
<hidden path="hscript.swf" />
|
|
||||||
<hidden path="hscript9.swf" />
|
|
||||||
<hidden path="release.bat" />
|
|
||||||
<hidden path="release.zip" />
|
|
||||||
<hidden path="obj" />
|
|
||||||
</hiddenPaths>
|
|
||||||
<!-- Executed before build -->
|
|
||||||
<preBuildCommand />
|
|
||||||
<!-- Executed after build -->
|
|
||||||
<postBuildCommand alwaysRun="False" />
|
|
||||||
<!-- Other project options -->
|
|
||||||
<options>
|
|
||||||
<option showHiddenPaths="False" />
|
|
||||||
<option testMovie="Default" />
|
|
||||||
<option testMovieCommand="" />
|
|
||||||
</options>
|
|
||||||
<!-- Plugin storage -->
|
|
||||||
<storage />
|
|
||||||
</project>
|
|
||||||
@@ -0,0 +1,536 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
|
enum VarMode {
|
||||||
|
Defined;
|
||||||
|
ForceSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Async {
|
||||||
|
|
||||||
|
var definedVars : Array<{ n : String, prev : Null<VarMode> }>;
|
||||||
|
var vars : Map<String,VarMode>;
|
||||||
|
var currentFun : String;
|
||||||
|
var currentLoop : Expr;
|
||||||
|
var currentBreak : Expr -> Expr;
|
||||||
|
var uid = 0;
|
||||||
|
public var asyncIdents : Map<String,Bool>;
|
||||||
|
|
||||||
|
static var nullExpr : Expr = #if hscriptPos { e : null, pmin : 0, pmax : 0, origin : "<null>", line : 0 } #else null #end;
|
||||||
|
static var nullId = mk(EIdent("null"), nullExpr);
|
||||||
|
|
||||||
|
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 dynamic function getTopLevelEnd() {
|
||||||
|
return ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function build( e : Expr, topLevelSync = false ) {
|
||||||
|
if( topLevelSync ) {
|
||||||
|
return buildSync(e,null);
|
||||||
|
} else {
|
||||||
|
var end = getTopLevelEnd();
|
||||||
|
return toCps(e, end, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineVar( v : String, mode ) {
|
||||||
|
definedVars.push({ n : v, prev : vars.get(v) });
|
||||||
|
vars.set(v, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookupFunctions( el : Array<Expr> ) {
|
||||||
|
for( e in el )
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EFunction(_, _, name, _) if( name != null ): defineVar(name, Defined);
|
||||||
|
case EMeta("sync",_,expr(_) => EFunction(_,_,name,_)) if( name != null ): defineVar(name, ForceSync);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSync( e : Expr, exit : Expr ) : Expr {
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EFunction(_,_,name,_):
|
||||||
|
if( name != null )
|
||||||
|
return toCps(e, null, null);
|
||||||
|
return e;
|
||||||
|
case EBlock(el):
|
||||||
|
var v = saveVars();
|
||||||
|
lookupFunctions(el);
|
||||||
|
var e = block([for(e in el) buildSync(e,exit)], e);
|
||||||
|
restoreVars(v);
|
||||||
|
return e;
|
||||||
|
case EMeta("async", _, e):
|
||||||
|
return toCps(e, ignore(), ignore());
|
||||||
|
case EMeta("sync", args, ef = expr(_) => EFunction(fargs, body, name, ret)):
|
||||||
|
return mk(EMeta("sync",args,mk(EFunction(fargs, buildSync(body,null), name, ret),ef)),e);
|
||||||
|
case EBreak if( currentBreak != null ):
|
||||||
|
return currentBreak(e);
|
||||||
|
case EContinue if( currentLoop != null ):
|
||||||
|
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() {
|
||||||
|
vars = new Map();
|
||||||
|
definedVars = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function isAsyncIdent( id : String ) {
|
||||||
|
return asyncIdents == null || asyncIdents.exists(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkSync( e : Expr ) {
|
||||||
|
if( !syncFlag )
|
||||||
|
return;
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case ECall(expr(_) => EIdent(i),_) if( isAsyncIdent(i) || vars.get(i) == Defined ):
|
||||||
|
syncFlag = false;
|
||||||
|
case ECall(expr(_) => EField(_,i),_) if( isAsyncIdent(i) ):
|
||||||
|
syncFlag = false;
|
||||||
|
case EFunction(_,_,name,_) if( name != null ):
|
||||||
|
syncFlag = false;
|
||||||
|
case EMeta("sync" | "async", _, _):
|
||||||
|
// isolated from the sync part
|
||||||
|
default:
|
||||||
|
Tools.iter(e, checkSync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveVars() {
|
||||||
|
return definedVars.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreVars(k) {
|
||||||
|
while( definedVars.length > k ) {
|
||||||
|
var v = definedVars.pop();
|
||||||
|
if( v.prev == null ) vars.remove(v.n) else vars.set(v.n, v.prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toCps( e : Expr, rest : Expr, exit : Expr ) : Expr {
|
||||||
|
if( isSync(e) )
|
||||||
|
return call(rest, [buildSync(e, exit)],e);
|
||||||
|
switch( expr(e) ) {
|
||||||
|
case EBlock(el):
|
||||||
|
var el = el.copy();
|
||||||
|
var vold = saveVars();
|
||||||
|
lookupFunctions(el);
|
||||||
|
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 )
|
||||||
|
defineVar(name, Defined);
|
||||||
|
for( a in args )
|
||||||
|
defineVar(a.name, Defined);
|
||||||
|
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):
|
||||||
|
var mode = vars.get(i);
|
||||||
|
return makeCall( ident( mode != null ? i : "a_" + i,e) , args, rest, exit, mode == ForceSync);
|
||||||
|
case ECall(expr(_) => EField(e, f), args):
|
||||||
|
return makeCall(field(e,"a_"+f,e), args, rest, exit);
|
||||||
|
case EFor(v, eit, eloop):
|
||||||
|
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 EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata
|
||||||
|
return toCps(e, rest, exit);
|
||||||
|
//case EDoWhile(_), ETry(_), ECall(_):
|
||||||
|
default:
|
||||||
|
throw "Unsupported async expression " + Printer.toString(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+41
-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;
|
||||||
}
|
}
|
||||||
@@ -146,6 +140,10 @@ class Bytes {
|
|||||||
#end
|
#end
|
||||||
bout.addByte(Type.enumIndex(e));
|
bout.addByte(Type.enumIndex(e));
|
||||||
switch( e ) {
|
switch( e ) {
|
||||||
|
case EImport(c):
|
||||||
|
// TODO
|
||||||
|
case EClass(_, _, _, _):
|
||||||
|
// TODO
|
||||||
case EConst(c):
|
case EConst(c):
|
||||||
doEncodeConst(c);
|
doEncodeConst(c);
|
||||||
case EIdent(v):
|
case EIdent(v):
|
||||||
@@ -247,11 +245,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 +375,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:
|
||||||
|
|||||||
+1242
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
// Runs support for custom classes in these
|
||||||
|
public static final ALLOWED_CUSTOM_CLASSES = [
|
||||||
|
"flixel",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Runs support for abstract support in these
|
||||||
|
public static final ALLOWED_ABSTRACT_AND_ENUM = [
|
||||||
|
"flixel",
|
||||||
|
"openfl.display.BlendMode",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Incase any of your files fail
|
||||||
|
// These are the module names
|
||||||
|
public static final DISALLOW_CUSTOM_CLASSES = [
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
public static final DISALLOW_ABSTRACT_AND_ENUM = [
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
import hscript.Interp.DeclaredVar;
|
||||||
|
import hscript.utils.UnsafeReflect;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
class CustomClassHandler implements IHScriptCustomConstructor {
|
||||||
|
public static var staticHandler = new StaticHandler();
|
||||||
|
|
||||||
|
public var ogInterp:Interp;
|
||||||
|
public var name:String;
|
||||||
|
public var fields:Array<Expr>;
|
||||||
|
public var extend:String;
|
||||||
|
public var interfaces:Array<String>;
|
||||||
|
|
||||||
|
public var cl:Class<Dynamic>;
|
||||||
|
|
||||||
|
public function new(ogInterp:Interp, name:String, fields:Array<Expr>, ?extend:String, ?interfaces:Array<String>) {
|
||||||
|
this.ogInterp = ogInterp;
|
||||||
|
this.name = name;
|
||||||
|
this.fields = fields;
|
||||||
|
this.extend = extend;
|
||||||
|
this.interfaces = interfaces;
|
||||||
|
|
||||||
|
this.cl = extend == null ? TemplateClass : Type.resolveClass('${extend}_HSX');
|
||||||
|
if(cl == null)
|
||||||
|
ogInterp.error(EInvalidClass(extend));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hnew(args:Array<Dynamic>):Dynamic {
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
throw "todo: do all this lmao -lunar";
|
||||||
|
return null;
|
||||||
|
#else
|
||||||
|
var interp = new Interp();
|
||||||
|
interp.errorHandler = ogInterp.errorHandler;
|
||||||
|
|
||||||
|
var _class:IHScriptCustomClassBehaviour = Type.createInstance(cl, args);
|
||||||
|
|
||||||
|
var __capturedLocals = ogInterp.duplicate(ogInterp.locals);
|
||||||
|
var capturedLocals:Map<String, DeclaredVar> = [];
|
||||||
|
for(k=>e in __capturedLocals)
|
||||||
|
if (e != null && e.depth <= 0)
|
||||||
|
capturedLocals.set(k, e);
|
||||||
|
|
||||||
|
var disallowCopy = Type.getInstanceFields(cl);
|
||||||
|
|
||||||
|
for (key => value in capturedLocals) {
|
||||||
|
if(!disallowCopy.contains(key)) {
|
||||||
|
interp.locals.set(key, {r: value, depth: -1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (key => value in ogInterp.variables) {
|
||||||
|
if(!disallowCopy.contains(key)) {
|
||||||
|
interp.variables.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var comparisonMap = new Map();
|
||||||
|
for(key => value in interp.variables) {
|
||||||
|
comparisonMap.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_class.__custom__variables = interp.variables;
|
||||||
|
|
||||||
|
//trace(fields);
|
||||||
|
|
||||||
|
for(expr in fields) {
|
||||||
|
@:privateAccess
|
||||||
|
interp.exprReturn(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
interp.variables.set("super", staticHandler);
|
||||||
|
|
||||||
|
_class.__interp = interp; // TODO: Remove
|
||||||
|
interp.scriptObject = _class;
|
||||||
|
// get only variables that were not set before
|
||||||
|
var classVariables = [for(key => value in interp.variables) if(!comparisonMap.exists(key) || comparisonMap[key] != value) key => value];
|
||||||
|
for(variable => value in classVariables) {
|
||||||
|
if(variable == "this" || variable == "super") continue;
|
||||||
|
@:privateAccess
|
||||||
|
if(!interp.__instanceFields.contains(variable)) {
|
||||||
|
interp.__instanceFields.push(variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//trace([for(key => value in classVariables) key]);
|
||||||
|
//@:privateAccess
|
||||||
|
//trace(interp.__instanceFields);
|
||||||
|
|
||||||
|
_class.__allowSetGet = false;
|
||||||
|
|
||||||
|
for(variable => value in interp.variables) {
|
||||||
|
if(variable == "this") continue;
|
||||||
|
|
||||||
|
if(variable.startsWith("set_") || variable.startsWith("get_")) {
|
||||||
|
_class.__allowSetGet = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newFunc = interp.variables.get("new");
|
||||||
|
if(newFunc != null) {
|
||||||
|
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newFunc = interp.variables.get("new");
|
||||||
|
if(newFunc != null) {
|
||||||
|
UnsafeReflect.callMethodUnsafe(null, newFunc, args);
|
||||||
|
}
|
||||||
|
return _class;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString():String {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TemplateClass implements IHScriptCustomClassBehaviour implements IHScriptCustomBehaviour {
|
||||||
|
public var __interp:Interp; // TODO: Remove
|
||||||
|
public var __custom__variables:Map<String, Dynamic>;
|
||||||
|
public var __allowSetGet:Bool = true;
|
||||||
|
|
||||||
|
public function hset(name:String, val:Dynamic):Dynamic {
|
||||||
|
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||||
|
return __callSetter(name, val);
|
||||||
|
if (__custom__variables.exists(name)) {
|
||||||
|
__custom__variables.set(name, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
UnsafeReflect.setProperty(this, name, val);
|
||||||
|
return UnsafeReflect.field(this, name);
|
||||||
|
}
|
||||||
|
public function hget(name:String):Dynamic {
|
||||||
|
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||||
|
return __callGetter(name);
|
||||||
|
if (__custom__variables.exists(name))
|
||||||
|
return __custom__variables.get(name);
|
||||||
|
return UnsafeReflect.getProperty(this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __callGetter(name:String):Dynamic {
|
||||||
|
__allowSetGet = false;
|
||||||
|
var v = __custom__variables.get("get_" + name)();
|
||||||
|
__allowSetGet = true;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __callSetter(name:String, val:Dynamic):Dynamic {
|
||||||
|
__allowSetGet = false;
|
||||||
|
var v = __custom__variables.get("set_" + name)(val);
|
||||||
|
__allowSetGet = true;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class StaticHandler {
|
||||||
|
public function new() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
#if hscriptPos
|
||||||
|
class Error_ {
|
||||||
|
public var e : ErrorDef;
|
||||||
|
public var pmin : Int;
|
||||||
|
public var pmax : Int;
|
||||||
|
public var origin : String;
|
||||||
|
public var line : Int;
|
||||||
|
public function new(e, pmin, pmax, origin, line) {
|
||||||
|
this.e = e;
|
||||||
|
this.pmin = pmin;
|
||||||
|
this.pmax = pmax;
|
||||||
|
this.origin = origin;
|
||||||
|
this.line = line;
|
||||||
|
}
|
||||||
|
public function toString(): String {
|
||||||
|
return Printer.errorToString(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enum ErrorDef
|
||||||
|
#else
|
||||||
|
enum Error_
|
||||||
|
#end
|
||||||
|
{
|
||||||
|
EInvalidChar( c : Int );
|
||||||
|
EUnexpected( s : String );
|
||||||
|
EUnterminatedString;
|
||||||
|
EUnterminatedComment;
|
||||||
|
EUnterminatedRegex;
|
||||||
|
EInvalidPreprocessor( msg : String );
|
||||||
|
EUnknownVariable( v : String );
|
||||||
|
EInvalidIterator( v : String );
|
||||||
|
EInvalidType( t : String );
|
||||||
|
EInvalidOp( op : String );
|
||||||
|
EInvalidAccess( f : String, ?on : String );
|
||||||
|
ECustom( msg : String );
|
||||||
|
EPreset( msg : ErrorMessage );
|
||||||
|
EInvalidClass( className : String);
|
||||||
|
EAlreadyExistingClass( className : String);
|
||||||
|
EInvalidEscape( s : String );
|
||||||
|
}
|
||||||
|
|
||||||
|
enum abstract ErrorMessage(Expr.UInt8) from Expr.UInt8 to Expr.UInt8 {
|
||||||
|
final INVALID_CHAR_CODE_MULTI;
|
||||||
|
final FROM_CHAR_CODE_NON_INT;
|
||||||
|
final EMPTY_INTERPOLATION;
|
||||||
|
final UNKNOWN_MAP_TYPE;
|
||||||
|
final UNKNOWN_MAP_TYPE_RUNTIME;
|
||||||
|
final EXPECT_KEY_VALUE_SYNTAX;
|
||||||
|
|
||||||
|
public function toString():String {
|
||||||
|
return switch(cast this) {
|
||||||
|
case INVALID_CHAR_CODE_MULTI: "'char'.code only works on single characters";
|
||||||
|
case FROM_CHAR_CODE_NON_INT: "String.fromCharCode only works on integers";
|
||||||
|
case EMPTY_INTERPOLATION: "Invalid interpolation: Expression cannot be empty";
|
||||||
|
case UNKNOWN_MAP_TYPE: "Unknown Map Type";
|
||||||
|
case UNKNOWN_MAP_TYPE_RUNTIME: "Unknown Map Type, while parsing at runtime";
|
||||||
|
case EXPECT_KEY_VALUE_SYNTAX: "Expected a => b";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+363
-111
@@ -1,29 +1,36 @@
|
|||||||
/*
|
/*
|
||||||
* 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;
|
||||||
|
|
||||||
|
typedef Int8 = #if cpp cpp.Int8 #else Int #end;
|
||||||
|
typedef Int16 = #if cpp cpp.Int16 #else Int #end;
|
||||||
|
typedef Int32 = #if cpp cpp.Int32 #else Int #end;
|
||||||
|
typedef Int64 = #if cpp cpp.Int64 #else Int #end;
|
||||||
|
|
||||||
|
typedef UInt8 = #if cpp cpp.UInt8 #else Int #end;
|
||||||
|
typedef UInt16 = #if cpp cpp.UInt16 #else Int #end;
|
||||||
|
typedef UInt32 = #if cpp cpp.UInt32 #else Int #end;
|
||||||
|
typedef UInt64 = #if cpp cpp.UInt64 #else Int #end;
|
||||||
|
|
||||||
enum Const {
|
enum Const {
|
||||||
CInt( v : Int );
|
CInt( v : Int );
|
||||||
CFloat( f : Float );
|
CFloat( f : Float );
|
||||||
@@ -33,57 +40,20 @@ enum Const {
|
|||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
#if hscriptPos
|
enum abstract MapType(UInt8) {
|
||||||
typedef Expr = {
|
var Null;
|
||||||
var e : ExprDef;
|
var IntMap;
|
||||||
var pmin : Int;
|
var StringMap;
|
||||||
var pmax : Int;
|
var EnumMap;
|
||||||
var origin : String;
|
var ObjectMap;
|
||||||
var line : Int;
|
var UnknownMap;
|
||||||
}
|
|
||||||
enum ExprDef {
|
|
||||||
#else
|
|
||||||
enum Expr {
|
|
||||||
#end
|
|
||||||
EConst( c : Const );
|
|
||||||
EIdent( v : String );
|
|
||||||
EVar( n : String, ?t : CType, ?e : Expr );
|
|
||||||
EParent( e : Expr );
|
|
||||||
EBlock( e : Array<Expr> );
|
|
||||||
EField( e : Expr, f : String );
|
|
||||||
EBinop( op : String, e1 : Expr, e2 : Expr );
|
|
||||||
EUnop( op : String, prefix : Bool, e : Expr );
|
|
||||||
ECall( e : Expr, params : Array<Expr> );
|
|
||||||
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
|
|
||||||
EWhile( cond : Expr, e : Expr );
|
|
||||||
EFor( v : String, it : Expr, e : Expr );
|
|
||||||
EBreak;
|
|
||||||
EContinue;
|
|
||||||
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType );
|
|
||||||
EReturn( ?e : Expr );
|
|
||||||
EArray( e : Expr, index : Expr );
|
|
||||||
EArrayDecl( e : Array<Expr> );
|
|
||||||
ENew( cl : String, params : Array<Expr> );
|
|
||||||
EThrow( e : Expr );
|
|
||||||
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
|
|
||||||
EObject( fl : Array<{ name : String, e : Expr }> );
|
|
||||||
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
|
|
||||||
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr);
|
|
||||||
EDoWhile( cond : Expr, e : Expr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef Argument = { name : String, ?t : CType, ?opt : Bool };
|
typedef Error = hscript.Error.Error_;
|
||||||
|
|
||||||
enum CType {
|
|
||||||
CTPath( path : Array<String>, ?params : Array<CType> );
|
|
||||||
CTFun( args : Array<CType>, ret : CType );
|
|
||||||
CTAnon( fields : Array<{ name : String, t : CType }> );
|
|
||||||
CTParent( t : CType );
|
|
||||||
}
|
|
||||||
|
|
||||||
#if hscriptPos
|
#if hscriptPos
|
||||||
class Error {
|
class Expr {
|
||||||
public var e : ErrorDef;
|
public var e : ExprDef;
|
||||||
public var pmin : Int;
|
public var pmin : Int;
|
||||||
public var pmax : Int;
|
public var pmax : Int;
|
||||||
public var origin : String;
|
public var origin : String;
|
||||||
@@ -95,51 +65,333 @@ 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: '"+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 {
|
|
||||||
var message = errorDefToString();
|
|
||||||
message = origin + ":" + line + ": " + message;
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
enum ErrorDef {
|
enum ExprDef
|
||||||
#else
|
#else
|
||||||
enum Error {
|
typedef ExprDef = Expr;
|
||||||
|
enum Expr
|
||||||
#end
|
#end
|
||||||
EInvalidChar( c : Int );
|
{
|
||||||
EUnexpected( s : String );
|
EConst( c : Const );
|
||||||
EUnterminatedString;
|
EIdent( v : VarN );
|
||||||
EUnterminatedComment;
|
EVar( n : VarN, ?t : CType, ?e : Expr, ?isPublic : Bool, ?isStatic : Bool );
|
||||||
EUnknownVariable( v : String );
|
EParent( e : Expr, ?noOptimize : Bool );
|
||||||
EInvalidIterator( v : String );
|
EBlock( e : Array<Expr> );
|
||||||
EInvalidOp( op : String );
|
EField( e : Expr, f : String , ?safe : Bool );
|
||||||
EInvalidAccess( f : String );
|
EBinop( op : Binop, e1 : Expr, e2 : Expr );
|
||||||
|
EUnop( op : Unop, prefix : Bool, e : Expr );
|
||||||
|
ECall( e : Expr, params : Array<Expr> );
|
||||||
|
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
|
||||||
|
EWhile( cond : Expr, e : Expr );
|
||||||
|
EFor( v : VarN, it : Expr, e : Expr);
|
||||||
|
EForKeyValue( v : VarN, it : Expr, e : Expr, ithv: VarN);
|
||||||
|
EBreak;
|
||||||
|
EContinue;
|
||||||
|
EFunction( args : Array<Argument>, e : Expr, ?name : VarN, ?ret : CType, ?isPublic : Bool, ?isStatic : Bool, ?isOverride : Bool );
|
||||||
|
EReturn( ?e : Expr );
|
||||||
|
EArray( e : Expr, index : Expr );
|
||||||
|
EMapDecl( type: MapType, keys: Array<Expr>, values: Array<Expr> );
|
||||||
|
EArrayDecl( e : Array<Expr> );
|
||||||
|
ENew( cl : VarN, params : Array<Expr> );
|
||||||
|
EThrow( e : Expr );
|
||||||
|
ETry( e : Expr, v : VarN, t : Null<CType>, ecatch : Expr );
|
||||||
|
EObject( fl : Array<ObjectField> );
|
||||||
|
ETernary( cond : Expr, e1 : Expr, e2 : Expr );
|
||||||
|
ESwitch( e : Expr, cases : Array<SwitchCase>, ?defaultExpr : Expr );
|
||||||
|
EDoWhile( cond : Expr, e : Expr);
|
||||||
|
EMeta( name : String, args : Array<Expr>, e : Expr );
|
||||||
|
ECheckType( e : Expr, t : CType );
|
||||||
|
|
||||||
|
EImport( c : String, mode: KImportMode );
|
||||||
|
EClass( name:VarN, fields:Array<Expr>, ?extend:String, interfaces:Array<String> );
|
||||||
|
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
EInfo( info: InfoClass, e: Expr );
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Binop {
|
||||||
|
/**
|
||||||
|
`+`
|
||||||
|
**/
|
||||||
|
OpAdd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`*`
|
||||||
|
**/
|
||||||
|
OpMult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`/`
|
||||||
|
**/
|
||||||
|
OpDiv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`-`
|
||||||
|
**/
|
||||||
|
OpSub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`=`
|
||||||
|
**/
|
||||||
|
OpAssign;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`==`
|
||||||
|
**/
|
||||||
|
OpEq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`!=`
|
||||||
|
**/
|
||||||
|
OpNotEq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`>`
|
||||||
|
**/
|
||||||
|
OpGt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`>=`
|
||||||
|
**/
|
||||||
|
OpGte;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`<`
|
||||||
|
**/
|
||||||
|
OpLt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`<=`
|
||||||
|
**/
|
||||||
|
OpLte;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`&`
|
||||||
|
**/
|
||||||
|
OpAnd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`|`
|
||||||
|
**/
|
||||||
|
OpOr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`^`
|
||||||
|
**/
|
||||||
|
OpXor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`&&`
|
||||||
|
**/
|
||||||
|
OpBoolAnd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`||`
|
||||||
|
**/
|
||||||
|
OpBoolOr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`<<`
|
||||||
|
**/
|
||||||
|
OpShl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`>>`
|
||||||
|
**/
|
||||||
|
OpShr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`>>>`
|
||||||
|
**/
|
||||||
|
OpUShr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`%`
|
||||||
|
**/
|
||||||
|
OpMod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`+=` `-=` `/=` `*=` `<<=` `>>=` `>>>=` `|=` `&=` `^=` `%=`
|
||||||
|
**/
|
||||||
|
OpAssignOp(op:Binop);
|
||||||
|
|
||||||
|
/**
|
||||||
|
`...`
|
||||||
|
**/
|
||||||
|
OpInterval;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`=>`
|
||||||
|
**/
|
||||||
|
OpArrow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`is`
|
||||||
|
**/
|
||||||
|
OpIs; // used to be OpIn, but our system treats that differently
|
||||||
|
|
||||||
|
/**
|
||||||
|
`??`
|
||||||
|
**/
|
||||||
|
OpNullCoal;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum abstract Unop(UInt8) {
|
||||||
|
/**
|
||||||
|
`++`
|
||||||
|
**/
|
||||||
|
var OpIncrement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`--`
|
||||||
|
**/
|
||||||
|
var OpDecrement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`!`
|
||||||
|
**/
|
||||||
|
var OpNot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`-`
|
||||||
|
**/
|
||||||
|
var OpNeg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`~`
|
||||||
|
**/
|
||||||
|
var OpNegBits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
`...`
|
||||||
|
**/
|
||||||
|
var OpSpread;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
class InfoClass {
|
||||||
|
public var variables:Array<String>;
|
||||||
|
|
||||||
|
public function new(variables:Array<String>) {
|
||||||
|
this.variables = variables;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
enum KImportMode {
|
||||||
|
INormal;
|
||||||
|
IAs( name : String );
|
||||||
|
IAsReturn;
|
||||||
|
IAll;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ObjectField {
|
||||||
|
public var name:String;
|
||||||
|
public var e:Expr;
|
||||||
|
|
||||||
|
public function new(name, e) {
|
||||||
|
this.name = name;
|
||||||
|
this.e = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:structInit
|
||||||
|
final class SwitchCase {
|
||||||
|
public var values : Array<Expr>;
|
||||||
|
public var expr : Expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
|
||||||
|
class Argument {
|
||||||
|
public var name : VarN;
|
||||||
|
public var t : Null<CType>;
|
||||||
|
public var opt : Bool;
|
||||||
|
public var value : Null<Expr>;
|
||||||
|
public function new(name, ?t, ?opt, ?value) {
|
||||||
|
this.name = name;
|
||||||
|
this.t = t;
|
||||||
|
this.opt = opt;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString() {
|
||||||
|
return (opt ? "?" : "") + name + (t != null ? ":" + Printer.convertTypeToString(t) : "") + (value != null ? "=" + Printer.convertExprToString(value) : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
|
||||||
|
|
||||||
|
enum CType {
|
||||||
|
CTPath( path : Array<String>, ?params : Array<CType> );
|
||||||
|
CTFun( args : Array<CType>, ret : CType );
|
||||||
|
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
|
||||||
|
CTParent( t : CType );
|
||||||
|
CTOpt( t : CType );
|
||||||
|
CTNamed( n : String, t : CType );
|
||||||
|
}
|
||||||
|
|
||||||
|
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 abstract FieldAccess(UInt8) {
|
||||||
|
var APublic;
|
||||||
|
var APrivate;
|
||||||
|
var AInline;
|
||||||
|
var AOverride;
|
||||||
|
var AStatic;
|
||||||
|
var 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>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
abstract VarN(Dynamic) from Int from String to Int to String {}
|
||||||
|
#else
|
||||||
|
typedef VarN = String;
|
||||||
|
#end
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
interface IHScriptCustomBehaviour {
|
||||||
|
public function hset(name:String, val:Dynamic):Dynamic;
|
||||||
|
public function hget(name:String):Dynamic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
import hscript.Interp;
|
||||||
|
|
||||||
|
interface IHScriptCustomClassBehaviour {
|
||||||
|
public var __interp:Interp; // TODO: Remove
|
||||||
|
public var __custom__variables:Map<String, Dynamic>;
|
||||||
|
public var __allowSetGet:Bool;
|
||||||
|
|
||||||
|
public function hset(name:String, val:Dynamic):Dynamic;
|
||||||
|
public function hget(name:String):Dynamic;
|
||||||
|
|
||||||
|
public function __callGetter(name:String):Dynamic;
|
||||||
|
|
||||||
|
public function __callSetter(name:String, val:Dynamic):Dynamic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
interface IHScriptCustomConstructor {
|
||||||
|
public function hnew(args:Array<Dynamic>):Dynamic;
|
||||||
|
}
|
||||||
+1456
-501
File diff suppressed because it is too large
Load Diff
+69
-30
@@ -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,11 @@ class Macro {
|
|||||||
#if haxe3
|
#if haxe3
|
||||||
case OpArrow: "=>";
|
case OpArrow: "=>";
|
||||||
#end
|
#end
|
||||||
|
#if (haxe_ver >= 4)
|
||||||
|
case OpIn: "in";
|
||||||
|
#end
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
};
|
};
|
||||||
binops.set(str, op);
|
binops.set(str, op);
|
||||||
if( assign )
|
if( assign )
|
||||||
@@ -92,6 +94,9 @@ class Macro {
|
|||||||
case OpNegBits: "~";
|
case OpNegBits: "~";
|
||||||
case OpIncrement: "++";
|
case OpIncrement: "++";
|
||||||
case OpDecrement: "--";
|
case OpDecrement: "--";
|
||||||
|
#if (haxe_ver >= 4.2)
|
||||||
|
case OpSpread: continue;
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
unops.set(str, op);
|
unops.set(str, op);
|
||||||
}
|
}
|
||||||
@@ -113,6 +118,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 +133,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 +196,26 @@ 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:Expr = #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:Expr = #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 EForKeyValue(v, it, efor, ithv):
|
||||||
|
#if (haxe_ver >= 4)
|
||||||
|
var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||||
|
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor), ithv);
|
||||||
|
#elseif (haxe_211 || haxe3)
|
||||||
|
var p:Expr = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
|
||||||
|
EFor({ expr : EIn({ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor), ithv);
|
||||||
|
#else
|
||||||
|
EFor(v, convert(it), convert(efor), ithv);
|
||||||
|
#end
|
||||||
case EBreak:
|
case EBreak:
|
||||||
EBreak;
|
EBreak;
|
||||||
case EContinue:
|
case EContinue:
|
||||||
@@ -203,7 +229,7 @@ class Macro {
|
|||||||
opt : false,
|
opt : false,
|
||||||
value : null,
|
value : null,
|
||||||
});
|
});
|
||||||
EFunction(name, {
|
EFunction(#if haxe4 FNamed(name,false) #else name #end, {
|
||||||
params : [],
|
params : [],
|
||||||
args : targs,
|
args : targs,
|
||||||
expr : convert(e),
|
expr : convert(e),
|
||||||
@@ -213,6 +239,12 @@ class Macro {
|
|||||||
EReturn(e == null ? null : convert(e));
|
EReturn(e == null ? null : convert(e));
|
||||||
case EArray(e, index):
|
case EArray(e, index):
|
||||||
EArray(convert(e), convert(index));
|
EArray(convert(e), convert(index));
|
||||||
|
case EMapDecl(type, keys, values): // port to array decl, haxe still uses EArrayDecl but with => as a binop
|
||||||
|
var el = [];
|
||||||
|
for( i in 0...keys.length ) {
|
||||||
|
el.push( hscript.Expr.EBinop("=>", keys[i], values[i]) );
|
||||||
|
}
|
||||||
|
EArrayDecl(map(el,convert));
|
||||||
case EArrayDecl(el):
|
case EArrayDecl(el):
|
||||||
EArrayDecl(map(el,convert));
|
EArrayDecl(map(el,convert));
|
||||||
case ENew(cl, params):
|
case ENew(cl, params):
|
||||||
@@ -231,6 +263,13 @@ class Macro {
|
|||||||
ETernary(convert(cond), convert(e1), convert(e2));
|
ETernary(convert(cond), convert(e1), convert(e2));
|
||||||
case ESwitch(e, cases, edef):
|
case ESwitch(e, cases, edef):
|
||||||
ESwitch(convert(e), [for( c in cases ) { values : [for( v in c.values ) convert(v)], expr : convert(c.expr) } ], edef == null ? null : convert(edef));
|
ESwitch(convert(e), [for( c in cases ) { values : [for( v in c.values ) convert(v)], expr : convert(c.expr) } ], edef == null ? null : convert(edef));
|
||||||
|
case EMeta(m, params, esub):
|
||||||
|
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));
|
||||||
|
case ECheckType(e, t):
|
||||||
|
ECheckType(convert(e), convertType(t));
|
||||||
|
default:
|
||||||
|
null;
|
||||||
}, 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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,643 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
import hscript.Expr;
|
||||||
|
import hscript.Tools;
|
||||||
|
import hscript.Parser;
|
||||||
|
|
||||||
|
@:access(hscript.Parser)
|
||||||
|
class Optimizer {
|
||||||
|
static inline function expr(e:Expr) return Tools.expr(e);
|
||||||
|
|
||||||
|
public static function optimize(s:Expr):Expr {
|
||||||
|
if(s == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var e = Tools.expr(s);
|
||||||
|
|
||||||
|
// TODO: Convert EBinOp(+, a, EUnop(-, b)) to EBinOp(-, a, b) aka (a - b)
|
||||||
|
// TODO: Make it so if EFor & EForKeyValue iterator variable isnt used its replaced with a $, which the code handles as doesnt exist
|
||||||
|
// TODO: Make it so <PURE_VAR|CONST> == ?; gets removed, IF it isnt stored
|
||||||
|
// TODO: Optimize ?. and ??
|
||||||
|
// TODO: Optimize Std.string(CONSTANT) to "CONSTANT"
|
||||||
|
// TODO: Remove Std.string if its the right side of a binop of a string
|
||||||
|
// TODO: Add EForComprehension, EWhileComprehension, EDoWhileComprehension for better optimization
|
||||||
|
// TODO: Optimize EFor(v, it, EBlock([e1])) to EFor(v, it, e1)
|
||||||
|
// TODO: Optimize EIf(cond, e1, e2) to EBlock([cond, e1]) if e1 == e2, if cond is constant or side effect free then convert to EParent(e1)
|
||||||
|
|
||||||
|
switch(e) {
|
||||||
|
// Parse all expressions and recreate the AST
|
||||||
|
case EBlock(exprs):
|
||||||
|
var exprs = exprs.map((v) -> optimize(v)).filter((v) -> v != null).filter((v) -> {
|
||||||
|
if(Tools.expr(v).match(EBlock([]))) return false; // Remove empty blocks
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var newExprs = [];
|
||||||
|
for(e in exprs) {
|
||||||
|
switch(Tools.expr(e)) {
|
||||||
|
case EBlock(iex):
|
||||||
|
var declares = Lambda.exists(iex, ie -> hasDecl(ie));
|
||||||
|
if(!declares) {
|
||||||
|
for(ex in iex)
|
||||||
|
newExprs.push(ex);
|
||||||
|
} else {
|
||||||
|
newExprs.push(e);
|
||||||
|
}
|
||||||
|
case EReturn(_): // remove stuff that are after a return
|
||||||
|
newExprs.push(e);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newExprs.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mk(EBlock(newExprs), s);
|
||||||
|
|
||||||
|
case EIf(econd, e1, e2):
|
||||||
|
var econd = optimize(econd);
|
||||||
|
var e1 = optimize(e1);
|
||||||
|
var e2 = optimize(e2);
|
||||||
|
if(isBool(econd)) {
|
||||||
|
var econd = getBool(econd);
|
||||||
|
if(econd == true) {
|
||||||
|
if(e1 == null)
|
||||||
|
return mk(EBlock([]), s);
|
||||||
|
return mk(EBlock([e1]), s);
|
||||||
|
} else {
|
||||||
|
if(e2 == null)
|
||||||
|
return mk(EBlock([]), s);
|
||||||
|
return mk(EBlock([e2]), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(e2 != null && isBool(e1) && isBool(e2)) {
|
||||||
|
var c1 = getBool(e1);
|
||||||
|
var c2 = getBool(e2);
|
||||||
|
|
||||||
|
if(c1 == false && c2 == true) { // (VAR ? false : true)
|
||||||
|
return optimize(mk(EUnop(OpNot, true, econd), s));
|
||||||
|
}
|
||||||
|
if(c1 == true && c2 == false) { // (VAR ? true : false)
|
||||||
|
return optimize(mk(Tools.expr(econd), s));
|
||||||
|
}
|
||||||
|
if(isConstant(econd)) { // Side effect free
|
||||||
|
if(c1 == true && c2 == true) { // (CONST ? true : true)
|
||||||
|
return mk(convertConstant(true), s);
|
||||||
|
}
|
||||||
|
if(c1 == false && c2 == false) { // (CONST ? false : false)
|
||||||
|
return mk(convertConstant(false), s);
|
||||||
|
}
|
||||||
|
// TODO: Check if local variables are used in the condition
|
||||||
|
// Since they cant have side effects, they can be optimized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mk(EIf(econd, e1, e2), s);
|
||||||
|
|
||||||
|
case ETernary(econd, e1, e2):
|
||||||
|
var econd = optimize(econd);
|
||||||
|
var e1 = optimize(e1);
|
||||||
|
var e2 = optimize(e2);
|
||||||
|
if(isConstant(econd)) {
|
||||||
|
var econd = getConstant(econd);
|
||||||
|
if(econd == true) {
|
||||||
|
if(e1 == null)
|
||||||
|
return mk(EBlock([]), s);
|
||||||
|
return mk(EParent(e1), s);
|
||||||
|
} else {
|
||||||
|
if(e2 == null)
|
||||||
|
return mk(EBlock([]), s);
|
||||||
|
return mk(EParent(e2), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isBool(e1) && isBool(e2)) {
|
||||||
|
var c1 = getBool(e1);
|
||||||
|
var c2 = getBool(e2);
|
||||||
|
|
||||||
|
if(c1 == false && c2 == true) { // (VAR ? false : true)
|
||||||
|
return optimize(mk(EUnop(OpNot, true, econd), s));
|
||||||
|
}
|
||||||
|
if(c1 == true && c2 == false) { // (VAR ? true : false)
|
||||||
|
return optimize(mk(Tools.expr(econd), s));
|
||||||
|
}
|
||||||
|
if(isConstant(econd)) { // Side effect free
|
||||||
|
if(c1 == true && c2 == true) { // (CONST ? true : true)
|
||||||
|
return mk(convertConstant(true), s);
|
||||||
|
}
|
||||||
|
if(c1 == false && c2 == false) { // (CONST ? false : false)
|
||||||
|
return mk(convertConstant(false), s);
|
||||||
|
}
|
||||||
|
// TODO: Check if local variables are used in the condition
|
||||||
|
// Since they cant have side effects, they can be optimized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mk(ETernary(econd, e1, e2), s);
|
||||||
|
case EWhile(econd, e):
|
||||||
|
var econd = optimize(econd);
|
||||||
|
var e = optimize(e);
|
||||||
|
return mk(EWhile(econd, e), s);
|
||||||
|
case EDoWhile(econd, e):
|
||||||
|
var econd = optimize(econd);
|
||||||
|
var e = optimize(e);
|
||||||
|
return mk(EDoWhile(econd, e), s);
|
||||||
|
case EFor(v, it, e):
|
||||||
|
var it = optimize(it);
|
||||||
|
var e = optimize(e);
|
||||||
|
return mk(EFor(v, it, e), s);
|
||||||
|
case EForKeyValue(v, it, e, ithv):
|
||||||
|
var it = optimize(it);
|
||||||
|
var e = optimize(e);
|
||||||
|
return mk(EForKeyValue(v, it, e, ithv), s);
|
||||||
|
case EBreak:
|
||||||
|
return mk(EBreak, s);
|
||||||
|
case EContinue:
|
||||||
|
return mk(EContinue, s);
|
||||||
|
case EReturn(e):
|
||||||
|
var e = optimize(e);
|
||||||
|
return mk(EReturn(e), s);
|
||||||
|
|
||||||
|
case ETry(e, v, t, ecatch):
|
||||||
|
var e = optimize(e);
|
||||||
|
var ecatch = optimize(ecatch);
|
||||||
|
return mk(ETry(e, v, t, ecatch), s);
|
||||||
|
|
||||||
|
case EThrow(e):
|
||||||
|
var e = optimize(e);
|
||||||
|
return mk(EThrow(e), s);
|
||||||
|
|
||||||
|
case EVar(n, t, e, isPublic, isStatic):
|
||||||
|
e = optimize(e);
|
||||||
|
return mk(EVar(n, t, e, isPublic, isStatic), s);
|
||||||
|
|
||||||
|
case ECall(e, params):
|
||||||
|
e = optimize(e);
|
||||||
|
params = params.map((v) -> optimize(v));
|
||||||
|
|
||||||
|
function p(i:Int, t:ConstType, opt:Bool = false):Dynamic {
|
||||||
|
var p = params[i];
|
||||||
|
if(t != getConstType(p))
|
||||||
|
if(opt)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
throw Parser.getBaseError(EInvalidType(getTypeName(p)));
|
||||||
|
return switch(t) {
|
||||||
|
case CTInt: getInt(p);
|
||||||
|
case CTFloat: getFloat(p);
|
||||||
|
case CTBool: getBool(p);
|
||||||
|
case CTString: getStringConstant(p);
|
||||||
|
case CTNull: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(Tools.expr(e)) {
|
||||||
|
case EField(expr(_) => EConst(CString(str)), field):
|
||||||
|
switch(field) {
|
||||||
|
case "toString":
|
||||||
|
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toString() takes no arguments"));
|
||||||
|
return mk(convertConstant(str.toString()), s);
|
||||||
|
|
||||||
|
case "toUpperCase":
|
||||||
|
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toUpperCase() takes no arguments"));
|
||||||
|
return mk(convertConstant(str.toUpperCase()), s);
|
||||||
|
|
||||||
|
case "toLowerCase":
|
||||||
|
if(params.length != 0) throw Parser.getBaseError(ECustom("String.toLowerCase() takes no arguments"));
|
||||||
|
return mk(convertConstant(str.toLowerCase()), s);
|
||||||
|
|
||||||
|
case "charAt":
|
||||||
|
var index = p(0, CTInt);
|
||||||
|
return mk(convertConstant(str.charAt(index)), s);
|
||||||
|
|
||||||
|
case "charCodeAt":
|
||||||
|
var index = p(0, CTInt);
|
||||||
|
return mk(convertConstant(str.charCodeAt(index)), s);
|
||||||
|
|
||||||
|
case "indexOf":
|
||||||
|
var value = p(0, CTString);
|
||||||
|
var startIndex = p(1, CTInt, true);
|
||||||
|
return mk(convertConstant(str.indexOf(value, startIndex)), s);
|
||||||
|
|
||||||
|
case "lastIndexOf":
|
||||||
|
var value = p(0, CTString);
|
||||||
|
var startIndex = p(1, CTInt, true);
|
||||||
|
return mk(convertConstant(str.lastIndexOf(value, startIndex)), s);
|
||||||
|
|
||||||
|
case "split":
|
||||||
|
var delimiter = p(0, CTString);
|
||||||
|
var strArr = str == "" ? [] : str.split(delimiter); // fix platform dependent behavior
|
||||||
|
return mk(EArrayDecl([for(st in strArr) mk(EConst(CString(st)), s)]), s);
|
||||||
|
|
||||||
|
case "substr":
|
||||||
|
var pos = p(0, CTInt);
|
||||||
|
var len = p(1, CTInt, true);
|
||||||
|
return mk(convertConstant(str.substr(pos, len)), s);
|
||||||
|
|
||||||
|
case "substring":
|
||||||
|
var startIndex = p(0, CTInt);
|
||||||
|
var endIndex = p(1, CTInt, true);
|
||||||
|
return mk(convertConstant(str.substring(startIndex, endIndex)), s);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
switch(Tools.expr(e)) {
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return mk(ECall(e, params), s);
|
||||||
|
|
||||||
|
case EField(e, f, safe):
|
||||||
|
e = optimize(e);
|
||||||
|
switch(Tools.expr(e)) {
|
||||||
|
case EConst(CString(str)) if(f == "length"):
|
||||||
|
return mk(convertConstant(str.length), s);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return mk(EField(e, f, safe), s);
|
||||||
|
|
||||||
|
case EIdent(_) | EConst(_):
|
||||||
|
return s;
|
||||||
|
|
||||||
|
case EParent(e, noOptimize):
|
||||||
|
e = optimize(e);
|
||||||
|
return mk(noOptimize ? EParent(e) : Tools.expr(e), s);
|
||||||
|
|
||||||
|
case ECheckType(e, t):
|
||||||
|
e = optimize(e);
|
||||||
|
return mk(ECheckType(e, t), s);
|
||||||
|
|
||||||
|
case EMeta(name, args, e):
|
||||||
|
e = optimize(e);
|
||||||
|
return mk(EMeta(name, args, e), s);
|
||||||
|
|
||||||
|
case ENew(cl, args):
|
||||||
|
args = args.map((v) -> optimize(v));
|
||||||
|
return mk(ENew(cl, args), s);
|
||||||
|
|
||||||
|
case EObject(fl):
|
||||||
|
fl.map((v) -> {
|
||||||
|
v.e = optimize(v.e);
|
||||||
|
});
|
||||||
|
return mk(EObject(fl), s);
|
||||||
|
|
||||||
|
case EImport(c, n):
|
||||||
|
return mk(EImport(c, n), s);
|
||||||
|
|
||||||
|
case EClass(name, fields, extend, interfaces): // Possible code not working
|
||||||
|
fields = fields.map((v) -> optimize(v));
|
||||||
|
return mk(EClass(name, fields, extend, interfaces), s);
|
||||||
|
|
||||||
|
case EFunction(args, e, name, ret, isPublic, isStatic, isOverride):
|
||||||
|
args.map((v) -> {
|
||||||
|
v.name = v.name;
|
||||||
|
v.value = optimize(v.value);
|
||||||
|
});
|
||||||
|
e = optimize(e);
|
||||||
|
return mk(EFunction(args, e, name, ret, isPublic, isStatic, isOverride), s);
|
||||||
|
|
||||||
|
case ESwitch(e, cases, def):
|
||||||
|
e = optimize(e);
|
||||||
|
cases.map((v) -> {
|
||||||
|
var values = v.values.map((v) -> optimize(v));
|
||||||
|
v.expr = optimize(v.expr);
|
||||||
|
});
|
||||||
|
def = optimize(def);
|
||||||
|
|
||||||
|
if(isConstant(e)) {
|
||||||
|
var econd = getConstant(e);
|
||||||
|
|
||||||
|
for(c in cases) {
|
||||||
|
for(v in c.values) {
|
||||||
|
if(isConstant(v)) {
|
||||||
|
var value = getConstant(v);
|
||||||
|
if(value == econd)
|
||||||
|
return mk(Tools.expr(c.expr), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe convert this to a Lambda.foreach?
|
||||||
|
var isAllCasesConstant = true;
|
||||||
|
for(c in cases) {
|
||||||
|
for(v in c.values) {
|
||||||
|
if(!isConstant(v)) {
|
||||||
|
isAllCasesConstant = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isAllCasesConstant) {
|
||||||
|
#if debug
|
||||||
|
//trace("Didnt find any cases that match");
|
||||||
|
#end
|
||||||
|
if(def != null)
|
||||||
|
return mk(Tools.expr(def), s);
|
||||||
|
else
|
||||||
|
return mk(EBlock([]), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mk(ESwitch(e, cases, def), s);
|
||||||
|
|
||||||
|
case EMapDecl(type, keys, values):
|
||||||
|
keys = keys.map((v) -> optimize(v));
|
||||||
|
values = values.map((v) -> optimize(v));
|
||||||
|
return mk(EMapDecl(type, keys, values), s);
|
||||||
|
|
||||||
|
case EArrayDecl(arr):
|
||||||
|
arr = arr.map((v) -> optimize(v));
|
||||||
|
return mk(EArrayDecl(arr), s);
|
||||||
|
|
||||||
|
case EArray(e, index):
|
||||||
|
e = optimize(e);
|
||||||
|
index = optimize(index);
|
||||||
|
if(isConstant(index)) {
|
||||||
|
if(Tools.expr(e).match(EArrayDecl(_))) {
|
||||||
|
var arr = switch(Tools.expr(e)) {
|
||||||
|
case EArrayDecl(arr): arr;
|
||||||
|
default: null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var constant = Lambda.exists(arr, isConstant);
|
||||||
|
var index = getInt(index);
|
||||||
|
if(constant && index != null) {
|
||||||
|
return mk(Tools.expr(arr[index]), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(Tools.expr(e).match(EMapDecl(_))) {
|
||||||
|
var map = switch(Tools.expr(e)) {
|
||||||
|
case EMapDecl(type, keys, vals): [for(i in 0...keys.length) [keys[i], vals[i]]];
|
||||||
|
default: null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var constant = Lambda.exists(map, (v) -> isConstant(v[0]) && isConstant(v[1]));
|
||||||
|
if(constant) {
|
||||||
|
var idx = Lambda.findIndex(map, (v) -> Type.enumEq(Tools.expr(v[0]), Tools.expr(index)));
|
||||||
|
if(idx == -1)
|
||||||
|
return mk(EIdent("null"), s);
|
||||||
|
return mk(Tools.expr(map[idx][1]), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mk(EArray(e, index), s);
|
||||||
|
|
||||||
|
case EBinop(op, e1, e2):
|
||||||
|
e1 = optimize(e1);
|
||||||
|
e2 = optimize(e2);
|
||||||
|
|
||||||
|
if(isConstant(e1) && isConstant(e2)) {
|
||||||
|
var optimized:Dynamic = optimizeOp(op, getConstant(e1), getConstant(e2));
|
||||||
|
if(optimized != null)
|
||||||
|
return mk(convertConstant(optimized), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possible bugs here
|
||||||
|
if(isNumber(e1) && !isConstant(e2)) {
|
||||||
|
var c1 = getNumber(e1);
|
||||||
|
if(compareNumber(c1, 0) && op == OpAdd)
|
||||||
|
return mk(Tools.expr(e2), s);
|
||||||
|
if(compareNumber(c1, 1) && op == OpMult)
|
||||||
|
return mk(Tools.expr(e2), s);
|
||||||
|
//if(compareNumber(c1, 0) && op == "*")
|
||||||
|
// return mk(convertConstant(0), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isConstant(e1) && isNumber(e2)) {
|
||||||
|
var c2 = getNumber(e2);
|
||||||
|
if(op == OpAdd && compareNumber(c2, 0))
|
||||||
|
return mk(Tools.expr(e1), s);
|
||||||
|
if(op == OpDiv && compareNumber(c2, 1))
|
||||||
|
return mk(Tools.expr(e1), s);
|
||||||
|
if(op == OpMult && compareNumber(c2, 1))
|
||||||
|
return mk(Tools.expr(e1), s);
|
||||||
|
//if(op == "*" && compareNumber(c2, 0))
|
||||||
|
// return mk(convertConstant(0), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mk(EBinop(op, e1, e2), s);
|
||||||
|
|
||||||
|
case EUnop(op, prefix, e):
|
||||||
|
e = optimize(e);
|
||||||
|
|
||||||
|
if(isConstant(e) && prefix) {
|
||||||
|
var constant:Dynamic = getConstant(e);
|
||||||
|
switch(op) {
|
||||||
|
case OpNeg: return mk(convertConstant(-constant), s);
|
||||||
|
case OpNot: return mk(convertConstant(!constant), s);
|
||||||
|
case OpNegBits:
|
||||||
|
var complement = #if (neko && !haxe3) haxe.Int32.complement(constant) #else ~constant #end;
|
||||||
|
return mk(convertConstant(complement), s);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mk(EUnop(op, prefix, e), s);
|
||||||
|
default:
|
||||||
|
Sys.println("Unknown expr: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function compareNumber(a:Dynamic, b:Dynamic):Bool {
|
||||||
|
return switch(Type.typeof(a)) {
|
||||||
|
case TInt:
|
||||||
|
var a:Int = cast a;
|
||||||
|
switch(Type.typeof(b)) {
|
||||||
|
case TInt: a == cast(b, Int);
|
||||||
|
case TFloat: a == cast(b, Float);
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
case TFloat:
|
||||||
|
var a:Float = cast a;
|
||||||
|
switch(Type.typeof(b)) {
|
||||||
|
case TInt: a == cast(b, Int);
|
||||||
|
case TFloat: a == cast(b, Float);
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function hasDecl(e:Expr):Bool {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EVar(_): true;// has a declaration
|
||||||
|
case EFunction(_): true;// has a declaration
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getTypeName(e:Expr):String {
|
||||||
|
if(e == null)
|
||||||
|
return null;
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CInt(_)): "Int";
|
||||||
|
case EConst(CFloat(_)): "Float";
|
||||||
|
case EConst(CString(_)): "String";
|
||||||
|
case EIdent("true") | EIdent("false"): "Bool";
|
||||||
|
case EIdent("null"): "Null";
|
||||||
|
case EIdent(_): "Dynamic";
|
||||||
|
case EParent(e): getTypeName(e);
|
||||||
|
default: Std.string(Tools.expr(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getConstType(e:Expr):ConstType {
|
||||||
|
if(e == null)
|
||||||
|
return null;
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CInt(_)): CTInt;
|
||||||
|
case EConst(CFloat(_)): CTFloat;
|
||||||
|
case EConst(CString(_)): CTString;
|
||||||
|
case EIdent("true") | EIdent("false"): CTBool;
|
||||||
|
case EIdent("null"): CTNull;
|
||||||
|
case EParent(e): getConstType(e);
|
||||||
|
default: throw "Unknown type " + Tools.expr(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isConstant(e:Expr):Bool {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EIdent("true") | EIdent("false") | EIdent("null"): true;
|
||||||
|
case EConst(_): true;
|
||||||
|
case EParent(e): isConstant(e);
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isBool(e:Expr):Bool {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EIdent("true") | EIdent("false"): true;
|
||||||
|
case EParent(e): isBool(e);
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isNumber(e:Expr):Bool {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CInt(_)): true;
|
||||||
|
case EConst(CFloat(_)): true;
|
||||||
|
case EParent(e): isNumber(e);
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getNumber(e:Expr):Dynamic {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CInt(value)): value;
|
||||||
|
case EConst(CFloat(value)): value;
|
||||||
|
case EParent(e): getNumber(e);
|
||||||
|
default: throw "Unknown type " + Tools.expr(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getBool(e:Expr):Bool {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EIdent("true"): true;
|
||||||
|
case EIdent("false"): false;
|
||||||
|
case EParent(e): getBool(e);
|
||||||
|
default: throw "Unknown type " + Tools.expr(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getInt(e:Expr):Null<Int> {
|
||||||
|
if(e == null)
|
||||||
|
return null;
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CInt(value)): value;
|
||||||
|
case EParent(e): getInt(e);
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getFloat(e:Expr):Null<Float> {
|
||||||
|
if(e == null)
|
||||||
|
return null;
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CFloat(value)): value;
|
||||||
|
case EParent(e): getFloat(e);
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function isString(e:Expr):Bool {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CString(_)): true;
|
||||||
|
case EParent(e): isString(e);
|
||||||
|
default: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getStringConstant(e:Expr):String {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EConst(CString(value)): value;
|
||||||
|
case EParent(e): getStringConstant(e);
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getConstant(e:Expr):Dynamic {
|
||||||
|
return switch(Tools.expr(e)) {
|
||||||
|
case EParent(e): getConstant(e);
|
||||||
|
case EIdent("true"): true;
|
||||||
|
case EIdent("false"): false;
|
||||||
|
case EIdent("null"): null;
|
||||||
|
case EConst(CInt(value)): value;
|
||||||
|
case EConst(CFloat(value)): value;
|
||||||
|
case EConst(CString(value)): value;
|
||||||
|
default: throw "Unknown constant " + Tools.expr(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function convertConstant(value:Dynamic):ExprDef {
|
||||||
|
return switch(Type.typeof(value)) {
|
||||||
|
case TInt: EConst(CInt(value));
|
||||||
|
case TFloat: EConst(CFloat(value));
|
||||||
|
case TBool: EIdent(value == true ? "true" : "false");
|
||||||
|
//case TString: EConst(CString(value));
|
||||||
|
case TNull: EIdent("null");
|
||||||
|
case TClass(String): EConst(CString(value));
|
||||||
|
default: throw "Unknown type " + Type.typeof(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function mk(e:ExprDef, s:Expr):Expr {
|
||||||
|
#if hscriptPos
|
||||||
|
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
static function optimizeOp(op:Binop, f1:Dynamic, f2:Dynamic):Dynamic {
|
||||||
|
//trace("Optimizing " + f1 + " " + op + " " + f2);
|
||||||
|
return switch(op) {
|
||||||
|
case OpAdd: f1 + f2;
|
||||||
|
case OpSub: f1 - f2;
|
||||||
|
case OpMult: f1 * f2;
|
||||||
|
case OpDiv: f1 / f2;
|
||||||
|
case OpMod: f1 % f2;
|
||||||
|
case OpAnd: f1 & f2;
|
||||||
|
case OpOr: f1 | f2;
|
||||||
|
case OpXor: f1 ^ f2;
|
||||||
|
case OpShl: f1 << f2;
|
||||||
|
case OpShr: f1 >> f2;
|
||||||
|
case OpUShr: f1 >>> f2;
|
||||||
|
case OpEq: f1 == f2;
|
||||||
|
case OpNotEq: f1 != f2;
|
||||||
|
case OpGte: f1 >= f2;
|
||||||
|
case OpLte: f1 <= f2;
|
||||||
|
case OpGt: f1 > f2;
|
||||||
|
case OpLt: f1 < f2;
|
||||||
|
case OpBoolOr: f1 == true || f2 == true;
|
||||||
|
case OpBoolAnd: f1 == true && f2 == true;
|
||||||
|
case OpNullCoal: f1 == null ? f2 : f1;
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConstType {
|
||||||
|
CTInt;
|
||||||
|
CTFloat;
|
||||||
|
CTBool;
|
||||||
|
CTString;
|
||||||
|
CTNull;
|
||||||
|
}
|
||||||
+2023
-566
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
import hscript.Expr;
|
||||||
|
import hscript.Error;
|
||||||
|
import hscript.Tools;
|
||||||
|
import hscript.Parser;
|
||||||
|
|
||||||
|
@:access(hscript.Parser)
|
||||||
|
class Postprocessor {
|
||||||
|
static inline function expr(e:Expr) return Tools.expr(e);
|
||||||
|
|
||||||
|
static function mk(e:ExprDef, s:Expr):Expr {
|
||||||
|
#if hscriptPos
|
||||||
|
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
public static function processvars(e:Expr, ?vars:Array<String>) {
|
||||||
|
if(vars == null) vars = [];
|
||||||
|
for(v in getvars(e)) vars.push(v);
|
||||||
|
e = _processvars(e, vars);
|
||||||
|
|
||||||
|
return mk(EInfo(new InfoClass(vars), e), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function _processvars(e:Expr, vars:Array<String>):Expr {
|
||||||
|
if(e == null) return null;
|
||||||
|
var ge = e;
|
||||||
|
var doExtra = true;
|
||||||
|
var e = switch (expr(e)) {
|
||||||
|
case EIdent(v) if (v is String):
|
||||||
|
mk(EIdent(vars.indexOf(v)), e);
|
||||||
|
case EVar(n, t, _e, p, s) if (n is String):
|
||||||
|
mk(EVar(vars.indexOf(n), t, _e, p, s), e);
|
||||||
|
case EImport(c, IAs(n)) if (n is String):
|
||||||
|
mk(EVar(vars.indexOf(n), null, mk(EImport(c, IAsReturn), e), false, false), e);
|
||||||
|
case EFunction(args, _e, n, r, p, s, o) if (n is String):
|
||||||
|
var _args:Array<Argument> = [
|
||||||
|
for (arg in args)
|
||||||
|
new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value)
|
||||||
|
];
|
||||||
|
mk(EFunction(_args, _e, vars.indexOf(n), r, p, s, o), e);
|
||||||
|
case EFunction(args, _e, null, r, p, s, o):
|
||||||
|
var _args:Array<Argument> = [
|
||||||
|
for (arg in args)
|
||||||
|
new Argument(vars.indexOf(arg.name), arg.t, arg.opt, arg.value)
|
||||||
|
];
|
||||||
|
mk(EFunction(_args, _e, null, r, p, s, o), e);
|
||||||
|
case ENew(cl, p) if (cl is String):
|
||||||
|
var p = [for(arg in p) arg];
|
||||||
|
mk(ENew(vars.indexOf(cl), p), e);
|
||||||
|
case EClass(n, fls, extnd, i) if (n is String):
|
||||||
|
var fls:Array<Expr> = [for(f in fls) f];
|
||||||
|
mk(EClass(vars.indexOf(n), fls, extnd, i), e);
|
||||||
|
case ETry(_e, v, t, ec) if (v is String):
|
||||||
|
mk(ETry(_e, vars.indexOf(v), t, ec), e);
|
||||||
|
case EFor(v, it, _e) if (v is String):
|
||||||
|
mk(EFor(vars.indexOf(v), it, _e), e);
|
||||||
|
case EForKeyValue(v, it, _e, ithv) if (v is String && ithv is String):
|
||||||
|
mk(EForKeyValue(vars.indexOf(v), it, _e, vars.indexOf(ithv)), e);
|
||||||
|
default: {
|
||||||
|
doExtra = false;
|
||||||
|
e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
e = Tools.map(e, function(e) {
|
||||||
|
return _processvars(e, vars);
|
||||||
|
});
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getvars(se:Expr, ?vars:Array<String>):Array<String> {
|
||||||
|
inline function pushVar(v:VarN) {
|
||||||
|
if(v is String) {
|
||||||
|
var v:String = cast v;
|
||||||
|
if (!vars.contains(v))
|
||||||
|
vars.push(v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(vars == null) vars = [];
|
||||||
|
Tools.iterExprRecursive(se, (e) -> {
|
||||||
|
switch (expr(e)) {
|
||||||
|
case EVar(n, t, e, _,_): pushVar(n);
|
||||||
|
case EIdent(v): pushVar(v);
|
||||||
|
case EFunction(args,e,name,_,_,_,_):
|
||||||
|
pushVar(name);
|
||||||
|
for (a in args) {
|
||||||
|
pushVar(a.name);
|
||||||
|
}
|
||||||
|
case EFor(v,it,e): pushVar(v);
|
||||||
|
case EForKeyValue(v,it,e,ithv): pushVar(ithv); pushVar(v);
|
||||||
|
case ETry(e, v, _, ec): pushVar(v);
|
||||||
|
case ENew(cl, params): pushVar(cl);
|
||||||
|
case EClass(name, fls,_,_): pushVar(name);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return vars;
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package hscript;
|
||||||
|
|
||||||
|
import hscript.Expr;
|
||||||
|
import hscript.Error;
|
||||||
|
import hscript.Tools;
|
||||||
|
import hscript.Parser;
|
||||||
|
|
||||||
|
@:access(hscript.Parser)
|
||||||
|
class Preprocessor {
|
||||||
|
static inline function expr(e:Expr) return Tools.expr(e);
|
||||||
|
|
||||||
|
private static var importStackName:Array<String> = [];
|
||||||
|
private static var importStackMode:Array<KImportMode> = [];
|
||||||
|
private static function addImport(e:String, mode:KImportMode = INormal) {
|
||||||
|
for(i in importStackName) if(i == e) return;
|
||||||
|
importStackName.push(e);
|
||||||
|
importStackMode.push(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function popImport(e:Expr) {
|
||||||
|
return mk(EImport(importStackName.pop(), importStackMode.pop()), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preprocesses the expression, like 'a'.code => 97
|
||||||
|
* Also for transforming any abstracts into their implementations (TODO)
|
||||||
|
* Also for transforming any static extensions into their real form (TODO)
|
||||||
|
* Also to automatically add imports for stuff that is not imported
|
||||||
|
**/
|
||||||
|
public static function process(e:Expr, top:Bool = true):Expr {
|
||||||
|
importStackName = [];
|
||||||
|
importStackMode = [];
|
||||||
|
var e = _process(e, top);
|
||||||
|
|
||||||
|
// Automatically add imports for stuff
|
||||||
|
switch(expr(e)) {
|
||||||
|
case EBlock(exprs):
|
||||||
|
while(importStackName.length > 0) {
|
||||||
|
exprs.unshift(popImport(e));
|
||||||
|
}
|
||||||
|
return mk(EBlock(exprs), e);
|
||||||
|
default:
|
||||||
|
if(importStackName.length > 0) {
|
||||||
|
var exprs = [];
|
||||||
|
while(importStackName.length > 0) {
|
||||||
|
exprs.unshift(popImport(e));
|
||||||
|
}
|
||||||
|
exprs.push(e);
|
||||||
|
return mk(EBlock(exprs), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function _process(e:Expr, top:Bool = true):Expr {
|
||||||
|
if(e == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// If stuff looks wrong, add this back
|
||||||
|
//e = Tools.map(e, function(e) {
|
||||||
|
// return _process(e, false);
|
||||||
|
//});
|
||||||
|
|
||||||
|
//trace(expr(e));
|
||||||
|
|
||||||
|
switch(expr(e)) {
|
||||||
|
case EField(expr(_) => EConst(CString(s)), "code", _): // Transform string.code into charCode
|
||||||
|
if(s.length != 1) {
|
||||||
|
throw Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI));
|
||||||
|
}
|
||||||
|
return mk(EConst(CInt(s.charCodeAt(0))), e);
|
||||||
|
case ECall(expr(_) => EField(expr(_) => EIdent("String"), "fromCharCode", _), [e]): // Seperate this later?
|
||||||
|
switch(expr(e)) { // should this be Optimizer?
|
||||||
|
case EConst(CInt(i)):
|
||||||
|
return mk(EConst(CString(String.fromCharCode(i))), e);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if(!Preprocessor.isStringFromCharCodeFixed) {
|
||||||
|
// __StringWorkaround__fromCharCode(i);
|
||||||
|
#if !NO_FROM_CHAR_CODE_FIX
|
||||||
|
return mk(ECall(mk(EIdent("__StringWorkaround__fromCharCode"), e), [e]), e);
|
||||||
|
#else
|
||||||
|
throw Parser.getBaseError(EPreset(FROM_CHAR_CODE_NON_INT));
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically add imports for stuff
|
||||||
|
case ENew("String", _): addImport("String");
|
||||||
|
case EIdent("String"): addImport("String");
|
||||||
|
case ENew("StringBuf", _): addImport("StringBuf");
|
||||||
|
case EIdent("StringBuf"): addImport("StringBuf");
|
||||||
|
case EIdent("Bool"): addImport("Bool");
|
||||||
|
case EIdent("Float"): addImport("Float");
|
||||||
|
case EIdent("Int"): addImport("Int");
|
||||||
|
case ENew("IntIterator", _): addImport("IntIterator");
|
||||||
|
case EIdent("IntIterator"): addImport("IntIterator");
|
||||||
|
case EIdent("Array"): addImport("Array");
|
||||||
|
|
||||||
|
case EIdent("Sys"): addImport("Sys");
|
||||||
|
case EIdent("Std"): addImport("Std");
|
||||||
|
case EIdent("Type"): addImport("Type");
|
||||||
|
case EIdent("Reflect"): addImport("Reflect");
|
||||||
|
case EIdent("StringTools"): addImport("StringTools");
|
||||||
|
case EIdent("Math"): addImport("Math");
|
||||||
|
case ENew("Date", _): addImport("Date");
|
||||||
|
case EIdent("Date"): addImport("Date");
|
||||||
|
case EIdent("DateTools"): addImport("DateTools");
|
||||||
|
case EIdent("Lambda"): addImport("Lambda");
|
||||||
|
case ENew("Xml", _): addImport("Xml");
|
||||||
|
case EIdent("Xml"): addImport("Xml");
|
||||||
|
//case EIdent("List"): addImport("haxe.ds.List");
|
||||||
|
|
||||||
|
case ENew("EReg", _): addImport("EReg");
|
||||||
|
case EIdent("EReg"): addImport("EReg");
|
||||||
|
//case EField(expr(_) => EIdent("EReg"), "escape", _):
|
||||||
|
// addImport("EReg");
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
e = Tools.map(e, function(e) {
|
||||||
|
return _process(e, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function mk(e:ExprDef, s:Expr):Expr {
|
||||||
|
#if hscriptPos
|
||||||
|
return new Expr(e, s.pmin, s.pmax, s.origin, s.line);
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var isStringFromCharCodeFixed(get, null):Null<Bool> = null;
|
||||||
|
static function get_isStringFromCharCodeFixed():Null<Bool> {
|
||||||
|
if(isStringFromCharCodeFixed == null) {
|
||||||
|
try {
|
||||||
|
Reflect.callMethod(null, Reflect.field(String, "fromCharCode"), [65]);
|
||||||
|
isStringFromCharCodeFixed = true;
|
||||||
|
} catch(e:Dynamic) {
|
||||||
|
isStringFromCharCodeFixed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isStringFromCharCodeFixed;
|
||||||
|
}
|
||||||
|
}
|
||||||
+544
-180
@@ -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,30 @@ class Printer {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function convertExprToString( e : Expr ) {
|
||||||
|
var printer = new Printer();
|
||||||
|
return printer.exprToString(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function typeToString( t : CType ) {
|
||||||
|
buf = new StringBuf();
|
||||||
|
tabs = "";
|
||||||
|
type(t);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function convertTypeToString( t : CType ) {
|
||||||
|
var printer = new Printer();
|
||||||
|
return printer.typeToString(t);
|
||||||
|
}
|
||||||
|
|
||||||
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 +72,22 @@ 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('(');
|
||||||
|
var first = true;
|
||||||
|
for (i=>a in args) {
|
||||||
|
switch a {
|
||||||
|
case CTNamed(_, _): type(a);
|
||||||
|
default: type(CTNamed('_', a));
|
||||||
|
}
|
||||||
|
if( i != args.length - 1 ) add(", ");
|
||||||
|
}
|
||||||
|
add(')->');
|
||||||
|
type(ret);
|
||||||
case CTFun(args, ret):
|
case CTFun(args, ret):
|
||||||
if( args.length == 0 )
|
if( args.length == 0 )
|
||||||
add("Void -> ");
|
add("Void -> ");
|
||||||
@@ -64,205 +121,512 @@ class Printer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function block(e : Expr, addSpaceIfBlock : Bool = true) {
|
||||||
|
var isBlock = Tools.expr(e).match(EBlock(_));
|
||||||
|
if(isBlock) {
|
||||||
|
if(addSpaceIfBlock) add(" ");
|
||||||
|
} else {
|
||||||
|
add("\n");
|
||||||
|
tabs += "\t";
|
||||||
|
add(tabs);
|
||||||
|
}
|
||||||
|
expr(e);
|
||||||
|
|
||||||
|
if(!isBlock) {
|
||||||
|
//add("\n");
|
||||||
|
tabs = tabs.substr(1);
|
||||||
|
//add(tabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJson(s:String) {
|
||||||
|
var len = s.length;
|
||||||
|
var i = 0;
|
||||||
|
while (i < len) {
|
||||||
|
switch (StringTools.fastCodeAt(s, i++)) {
|
||||||
|
// [a-zA-Z0-9_]+
|
||||||
|
case "a".code | "b".code | "c".code | "d".code | "e".code | "f".code | "g".code | "h".code | "i".code | "j".code | "k".code | "l".code | "m".code
|
||||||
|
| "n".code | "o".code | "p".code | "q".code | "r".code | "s".code | "t".code | "u".code | "v".code | "w".code | "x".code | "y".code | "z".code
|
||||||
|
| "A".code | "B".code | "C".code | "D".code | "E".code | "F".code | "G".code | "H".code | "I".code | "J".code | "K".code | "L".code | "M".code
|
||||||
|
| "N".code | "O".code | "P".code | "Q".code | "R".code | "S".code | "T".code | "U".code | "V".code | "W".code | "X".code | "Y".code | "Z".code
|
||||||
|
| "0".code | "1".code | "2".code | "3".code | "4".code | "5".code | "6".code | "7".code | "8".code | "9".code | "_".code:
|
||||||
|
case _:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline function isPrintable( c : Int ) {
|
||||||
|
return c >= 32 && c <= 126;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline function hex( c : Int, ?len : Int = 2 ) {
|
||||||
|
return StringTools.hex(c, len).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEscapedString( s : String ) {
|
||||||
|
var buf = new StringBuf();
|
||||||
|
#if target.unicode
|
||||||
|
var s = new UnicodeString(s);
|
||||||
|
#end
|
||||||
|
for( i in 0...s.length ) {
|
||||||
|
#if target.unicode
|
||||||
|
var c:Null<Int> = s.charCodeAt(i);
|
||||||
|
#else
|
||||||
|
var c:Null<Int> = StringTools.unsafeCodeAt(s, i);
|
||||||
|
#end
|
||||||
|
switch( c ) {
|
||||||
|
case '"'.code: buf.add('\\"');
|
||||||
|
case '\\'.code: buf.add('\\\\');
|
||||||
|
case '\n'.code: buf.add('\\n');
|
||||||
|
case '\r'.code: buf.add('\\r');
|
||||||
|
case '\t'.code: buf.add('\\t');
|
||||||
|
default:
|
||||||
|
if(c == null) continue;
|
||||||
|
if(isPrintable(c))
|
||||||
|
buf.addChar(c);
|
||||||
|
else {
|
||||||
|
if(c > 0xFF) {
|
||||||
|
buf.add("\\u{");
|
||||||
|
buf.add(hex(c, null));
|
||||||
|
buf.add("}");
|
||||||
|
} else {
|
||||||
|
buf.add("\\x");
|
||||||
|
buf.add(hex((c & 0xFF)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVar(v:VarN) {
|
||||||
|
if( v == null ) {
|
||||||
|
return ("??NULL??");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(v is Int) {
|
||||||
|
return ("$" + v);
|
||||||
|
} else {
|
||||||
|
return (v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getBinaryOp(op:Binop) {
|
||||||
|
return switch(op) {
|
||||||
|
case OpAdd: "+";
|
||||||
|
case OpSub: "-";
|
||||||
|
case OpMult: "*";
|
||||||
|
case OpDiv: "/";
|
||||||
|
case OpMod: "%";
|
||||||
|
case OpAnd: "&";
|
||||||
|
case OpOr: "|";
|
||||||
|
case OpXor: "^";
|
||||||
|
case OpShl: "<<";
|
||||||
|
case OpShr: ">>";
|
||||||
|
case OpUShr: ">>>";
|
||||||
|
case OpEq: "==";
|
||||||
|
case OpNotEq: "!=";
|
||||||
|
case OpGt: ">";
|
||||||
|
case OpGte: ">=";
|
||||||
|
case OpLt: "<";
|
||||||
|
case OpLte: "<=";
|
||||||
|
case OpBoolAnd: "&&";
|
||||||
|
case OpBoolOr: "||";
|
||||||
|
case OpIs: "is";
|
||||||
|
case OpNullCoal: "??";
|
||||||
|
case OpAssign: "=";
|
||||||
|
case OpArrow: "=>";
|
||||||
|
case OpInterval: "...";
|
||||||
|
case OpAssignOp(op): getBinaryOp(op) + "=";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getUnaryOp(op:Unop) {
|
||||||
|
return switch(op) {
|
||||||
|
case OpIncrement: "++";
|
||||||
|
case OpDecrement: "--";
|
||||||
|
case OpNot: "!";
|
||||||
|
case OpNeg: "-";
|
||||||
|
case OpNegBits: "~";
|
||||||
|
case OpSpread: "...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function expr( e : Expr ) {
|
function expr( e : Expr ) {
|
||||||
if( e == null ) {
|
if( e == null ) {
|
||||||
add("??NULL??");
|
add("??NULL??");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch( #if hscriptPos e.e #else e #end ) {
|
|
||||||
case EConst(c):
|
// TODO: make else if print correctly
|
||||||
switch( c ) {
|
|
||||||
case CInt(i): add(i);
|
switch( Tools.expr(e) ) {
|
||||||
case CFloat(f): add(f);
|
case EImport(c, mode):
|
||||||
case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"');
|
add("import " + c);
|
||||||
}
|
switch(mode) {
|
||||||
case EIdent(v):
|
case IAs(name): add(' as $name');
|
||||||
add(v);
|
case IAll: add('.*');
|
||||||
case EVar(n, t, e):
|
default:
|
||||||
add("var " + n);
|
}
|
||||||
addType(t);
|
case EClass(name, fields, extend, interfaces):
|
||||||
if( e != null ) {
|
add('class ${getVar(name)}');
|
||||||
add(" = ");
|
if (extend != null)
|
||||||
expr(e);
|
add(' extends $extend');
|
||||||
}
|
for(_interface in interfaces) {
|
||||||
case EParent(e):
|
add(' implements $_interface');
|
||||||
add("("); expr(e); add(")");
|
}
|
||||||
case EBlock(el):
|
add(' {\n');
|
||||||
if( el.length == 0 ) {
|
|
||||||
add("{}");
|
|
||||||
} else {
|
|
||||||
tabs += "\t";
|
tabs += "\t";
|
||||||
add("{\n");
|
// TODO: Print fields
|
||||||
for( e in el ) {
|
//for(field in fields) {
|
||||||
add(tabs);
|
// expr(field);
|
||||||
|
//}
|
||||||
|
|
||||||
|
tabs = tabs.substr(1);
|
||||||
|
add("}");
|
||||||
|
case EConst(c):
|
||||||
|
switch( c ) {
|
||||||
|
case CInt(i): add(i);
|
||||||
|
case CFloat(f): add(f);
|
||||||
|
case CString(s):
|
||||||
|
add('"');
|
||||||
|
add(getEscapedString(s));
|
||||||
|
add('"');
|
||||||
|
}
|
||||||
|
case EIdent(v):
|
||||||
|
add(getVar(v));
|
||||||
|
case EVar(n, t, e): // TODO: static, public, override
|
||||||
|
add("var " + getVar(n));
|
||||||
|
if( t != null )
|
||||||
|
addType(t);
|
||||||
|
else
|
||||||
|
switch( Tools.expr(e) ) {
|
||||||
|
case EMapDecl(type, _, _):
|
||||||
|
add(" : Map<");
|
||||||
|
switch( type ) {
|
||||||
|
case ObjectMap: add("Dynamic");
|
||||||
|
case StringMap: add("String");
|
||||||
|
case EnumMap: add("EnumValue");
|
||||||
|
case IntMap: add("Int");
|
||||||
|
case UnknownMap: add("?");
|
||||||
|
case Null: add("Null");
|
||||||
|
}
|
||||||
|
add(", Dynamic>");
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if( e != null ) {
|
||||||
|
add(" = ");
|
||||||
expr(e);
|
expr(e);
|
||||||
|
}
|
||||||
|
case EParent(e):
|
||||||
|
add("("); expr(e); add(")");
|
||||||
|
case EBlock(el):
|
||||||
|
if( el.length == 0 ) {
|
||||||
|
add("{}");
|
||||||
|
} else {
|
||||||
|
tabs += "\t";
|
||||||
|
add("{\n");
|
||||||
|
for( e in el ) {
|
||||||
|
add(tabs);
|
||||||
|
expr(e);
|
||||||
|
add(";\n");
|
||||||
|
}
|
||||||
|
tabs = tabs.substr(1);
|
||||||
|
add(tabs);
|
||||||
|
add("}");
|
||||||
|
}
|
||||||
|
case EField(e, f, s):
|
||||||
|
expr(e);
|
||||||
|
add((s == true ? "?." : ".") + f);
|
||||||
|
case EBinop(op, e1, e2):
|
||||||
|
/*var op1 = switch(Tools.expr(e1)) {
|
||||||
|
case EBinop(op, _, _): op;
|
||||||
|
case EConst(_): "_";
|
||||||
|
case EIdent(_): "_";
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
var op2 = switch(Tools.expr(e2)) {
|
||||||
|
case EBinop(op, _, _): op;
|
||||||
|
case EConst(_): "_";
|
||||||
|
case EIdent(_): "_";
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
var paran = Tools.checkOpPrecedence(op, op1, op2);*/
|
||||||
|
var paran = 2;
|
||||||
|
|
||||||
|
var op = getBinaryOp(op);
|
||||||
|
|
||||||
|
if(paran == 0 || paran == 2) {
|
||||||
|
add("(");
|
||||||
|
expr(e1);
|
||||||
|
add(")");
|
||||||
|
} else {
|
||||||
|
expr(e1);
|
||||||
|
}
|
||||||
|
if(op == "...")
|
||||||
|
add(op);
|
||||||
|
else
|
||||||
|
add(" " + op + " ");
|
||||||
|
if(paran == 1 || paran == 2) {
|
||||||
|
add("(");
|
||||||
|
expr(e2);
|
||||||
|
add(")");
|
||||||
|
} else {
|
||||||
|
expr(e2);
|
||||||
|
}
|
||||||
|
case EUnop(op, pre, e):
|
||||||
|
var op = getUnaryOp(op);
|
||||||
|
|
||||||
|
if( pre ) {
|
||||||
|
add(op);
|
||||||
|
expr(e);
|
||||||
|
} else {
|
||||||
|
expr(e);
|
||||||
|
add(op);
|
||||||
|
}
|
||||||
|
case ECall(e, args):
|
||||||
|
if( e == null )
|
||||||
|
expr(e);
|
||||||
|
else switch( Tools.expr(e) ) {
|
||||||
|
case EField(_), EIdent(_), EConst(_):
|
||||||
|
expr(e);
|
||||||
|
default:
|
||||||
|
add("(");
|
||||||
|
expr(e);
|
||||||
|
add(")");
|
||||||
|
}
|
||||||
|
add("(");
|
||||||
|
var first = true;
|
||||||
|
for( a in args ) {
|
||||||
|
if( first ) first = false else add(", ");
|
||||||
|
expr(a);
|
||||||
|
}
|
||||||
|
add(")");
|
||||||
|
case EIf(cond,e1,e2):
|
||||||
|
add("if( ");
|
||||||
|
expr(cond);
|
||||||
|
add(" )");
|
||||||
|
var wasBlock = block(e1, true);
|
||||||
|
|
||||||
|
if( e2 != null ) {
|
||||||
|
if(!wasBlock) add("\n" + tabs);
|
||||||
|
add(" else ");
|
||||||
|
block(e2);
|
||||||
|
}
|
||||||
|
case EWhile(cond,e):
|
||||||
|
add("while( ");
|
||||||
|
expr(cond);
|
||||||
|
add(" )");
|
||||||
|
block(e, true);
|
||||||
|
case EDoWhile(cond,e):
|
||||||
|
add("do");
|
||||||
|
block(e, true);
|
||||||
|
add(" while ( ");
|
||||||
|
expr(cond);
|
||||||
|
add(" )");
|
||||||
|
case EFor(v, it, e):
|
||||||
|
add("for( "+getVar(v)+" in ");
|
||||||
|
expr(it);
|
||||||
|
add(" )");
|
||||||
|
block(e, true);
|
||||||
|
case EForKeyValue(v, it, e, ithv):
|
||||||
|
add("for( "+getVar(ithv)+" => "+getVar(v)+" in ");
|
||||||
|
expr(it);
|
||||||
|
add(" )");
|
||||||
|
block(e, true);
|
||||||
|
case EBreak:
|
||||||
|
add("break");
|
||||||
|
case EContinue:
|
||||||
|
add("continue");
|
||||||
|
case EFunction(params, e, name, ret): // TODO: static, public, override
|
||||||
|
add("function");
|
||||||
|
if( name != null )
|
||||||
|
add(" " + getVar(name));
|
||||||
|
add("(");
|
||||||
|
var first = true;
|
||||||
|
for( a in params ) {
|
||||||
|
if( first ) first = false else add(", ");
|
||||||
|
if( a.opt ) add("?");
|
||||||
|
add(getVar(a.name));
|
||||||
|
addType(a.t);
|
||||||
|
}
|
||||||
|
add(")");
|
||||||
|
addType(ret);
|
||||||
|
add(" ");
|
||||||
|
expr(e);
|
||||||
|
case EReturn(e):
|
||||||
|
add("return");
|
||||||
|
if( e != null ) {
|
||||||
|
add(" ");
|
||||||
|
expr(e);
|
||||||
|
}
|
||||||
|
case EArray(e,index):
|
||||||
|
expr(e);
|
||||||
|
add("[");
|
||||||
|
expr(index);
|
||||||
|
add("]");
|
||||||
|
case EMapDecl(type, keys, values):
|
||||||
|
add("[");
|
||||||
|
var first = true;
|
||||||
|
for( i in 0...keys.length ) {
|
||||||
|
if( first ) first = false else add(", ");
|
||||||
|
expr(keys[i]);
|
||||||
|
add(" => ");
|
||||||
|
expr(values[i]);
|
||||||
|
}
|
||||||
|
add("]");
|
||||||
|
case EArrayDecl(el):
|
||||||
|
add("[");
|
||||||
|
var first = true;
|
||||||
|
for( e in el ) {
|
||||||
|
if( first ) first = false else add(", ");
|
||||||
|
expr(e);
|
||||||
|
}
|
||||||
|
add("]");
|
||||||
|
case ENew(cl, args):
|
||||||
|
add("new " + getVar(cl) + "(");
|
||||||
|
var first = true;
|
||||||
|
for( e in args ) {
|
||||||
|
if( first ) first = false else add(", ");
|
||||||
|
expr(e);
|
||||||
|
}
|
||||||
|
add(")");
|
||||||
|
case EThrow(e):
|
||||||
|
add("throw ");
|
||||||
|
expr(e);
|
||||||
|
case ETry(e, v, t, ecatch):
|
||||||
|
add("try");
|
||||||
|
var wasBlock = block(e);
|
||||||
|
if( !wasBlock ) {
|
||||||
|
add("\n" + tabs);
|
||||||
|
} else add(" ");
|
||||||
|
add("catch( " + v);
|
||||||
|
addType(t);
|
||||||
|
add(" )");
|
||||||
|
block(ecatch);
|
||||||
|
case EObject(fl):
|
||||||
|
if( fl.length == 0 ) {
|
||||||
|
add("{}");
|
||||||
|
} else {
|
||||||
|
tabs += "\t";
|
||||||
|
add("{\n");
|
||||||
|
for( i=>f in fl ) {
|
||||||
|
add(tabs);
|
||||||
|
var name = isJson(f.name) ? "\"" + f.name + "\"" : f.name;
|
||||||
|
add(name+" : ");
|
||||||
|
expr(f.e);
|
||||||
|
if( i != fl.length - 1 ) add(",");
|
||||||
|
add("\n");
|
||||||
|
}
|
||||||
|
tabs = tabs.substr(1);
|
||||||
|
add(tabs);
|
||||||
|
add("}");
|
||||||
|
}
|
||||||
|
case ETernary(c,e1,e2):
|
||||||
|
expr(c);
|
||||||
|
add(" ? ");
|
||||||
|
expr(e1);
|
||||||
|
add(" : ");
|
||||||
|
expr(e2);
|
||||||
|
case ESwitch(e, cases, def):
|
||||||
|
add("switch( ");
|
||||||
|
expr(e);
|
||||||
|
add(" ) {\n");
|
||||||
|
tabs += "\t";
|
||||||
|
for( c in cases ) {
|
||||||
|
add(tabs);
|
||||||
|
add("case ");
|
||||||
|
var first = true;
|
||||||
|
for( v in c.values ) {
|
||||||
|
if( first ) first = false else add(", ");
|
||||||
|
expr(v);
|
||||||
|
}
|
||||||
|
add(":");
|
||||||
|
block(c.expr, true);
|
||||||
|
add(";\n");
|
||||||
|
}
|
||||||
|
if( def != null ) {
|
||||||
|
add(tabs);
|
||||||
|
add("default: ");
|
||||||
|
block(def, true);
|
||||||
add(";\n");
|
add(";\n");
|
||||||
}
|
}
|
||||||
tabs = tabs.substr(1);
|
tabs = tabs.substr(1);
|
||||||
|
add(tabs);
|
||||||
add("}");
|
add("}");
|
||||||
}
|
case EMeta(name, args, e):
|
||||||
case EField(e, f):
|
add("@");
|
||||||
expr(e);
|
add(name);
|
||||||
add("." + f);
|
if( args != null && args.length > 0 ) {
|
||||||
case EBinop(op, e1, e2):
|
add("(");
|
||||||
expr(e1);
|
var first = true;
|
||||||
add(" " + op + " ");
|
for( a in args ) {
|
||||||
expr(e2);
|
if( first ) first = false else add(", ");
|
||||||
case EUnop(op, pre, e):
|
expr(e);
|
||||||
if( pre ) {
|
}
|
||||||
add(op);
|
add(")");
|
||||||
expr(e);
|
}
|
||||||
} else {
|
|
||||||
expr(e);
|
|
||||||
add(op);
|
|
||||||
}
|
|
||||||
case ECall(e, args):
|
|
||||||
if( e == null )
|
|
||||||
expr(e);
|
|
||||||
else switch( #if hscriptPos e.e #else e #end ) {
|
|
||||||
case EField(_), EIdent(_), EConst(_):
|
|
||||||
expr(e);
|
|
||||||
default:
|
|
||||||
add("(");
|
|
||||||
expr(e);
|
|
||||||
add(")");
|
|
||||||
}
|
|
||||||
add("(");
|
|
||||||
var first = true;
|
|
||||||
for( a in args ) {
|
|
||||||
if( first ) first = false else add(", ");
|
|
||||||
expr(a);
|
|
||||||
}
|
|
||||||
add(")");
|
|
||||||
case EIf(cond,e1,e2):
|
|
||||||
add("if( ");
|
|
||||||
expr(cond);
|
|
||||||
add(" ) ");
|
|
||||||
expr(e1);
|
|
||||||
if( e2 != null ) {
|
|
||||||
add(" else ");
|
|
||||||
expr(e2);
|
|
||||||
}
|
|
||||||
case EWhile(cond,e):
|
|
||||||
add("while( ");
|
|
||||||
expr(cond);
|
|
||||||
add(" ) ");
|
|
||||||
expr(e);
|
|
||||||
case EDoWhile(cond,e):
|
|
||||||
add("do ");
|
|
||||||
expr(e);
|
|
||||||
add(" while ( ");
|
|
||||||
expr(cond);
|
|
||||||
add(" )");
|
|
||||||
case EFor(v, it, e):
|
|
||||||
add("for( "+v+" in ");
|
|
||||||
expr(it);
|
|
||||||
add(" ) ");
|
|
||||||
expr(e);
|
|
||||||
case EBreak:
|
|
||||||
add("break");
|
|
||||||
case EContinue:
|
|
||||||
add("continue");
|
|
||||||
case EFunction(params, e, name, ret):
|
|
||||||
add("function");
|
|
||||||
if( name != null )
|
|
||||||
add(" " + name);
|
|
||||||
add("(");
|
|
||||||
var first = true;
|
|
||||||
for( a in params ) {
|
|
||||||
if( first ) first = false else add(", ");
|
|
||||||
if( a.opt ) add("?");
|
|
||||||
add(a.name);
|
|
||||||
addType(a.t);
|
|
||||||
}
|
|
||||||
add(")");
|
|
||||||
addType(ret);
|
|
||||||
add(" ");
|
|
||||||
expr(e);
|
|
||||||
case EReturn(e):
|
|
||||||
add("return");
|
|
||||||
if( e != null ) {
|
|
||||||
add(" ");
|
add(" ");
|
||||||
expr(e);
|
expr(e);
|
||||||
}
|
case ECheckType(e, t):
|
||||||
case EArray(e,index):
|
add("(");
|
||||||
expr(e);
|
|
||||||
add("[");
|
|
||||||
expr(index);
|
|
||||||
add("]");
|
|
||||||
case EArrayDecl(el):
|
|
||||||
add("[");
|
|
||||||
var first = true;
|
|
||||||
for( e in el ) {
|
|
||||||
if( first ) first = false else add(", ");
|
|
||||||
expr(e);
|
expr(e);
|
||||||
}
|
add(" : ");
|
||||||
add("]");
|
addType(t);
|
||||||
case ENew(cl, args):
|
add(")");
|
||||||
add("new " + cl + "(");
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
var first = true;
|
case EInfo(_, e):
|
||||||
for( e in args ) {
|
|
||||||
if( first ) first = false else add(", ");
|
|
||||||
expr(e);
|
expr(e);
|
||||||
}
|
#end
|
||||||
add(")");
|
|
||||||
case EThrow(e):
|
|
||||||
add("throw ");
|
|
||||||
expr(e);
|
|
||||||
case ETry(e, v, t, ecatch):
|
|
||||||
add("try ");
|
|
||||||
expr(e);
|
|
||||||
add(" catch( " + v);
|
|
||||||
addType(t);
|
|
||||||
add(") ");
|
|
||||||
expr(ecatch);
|
|
||||||
case EObject(fl):
|
|
||||||
if( fl.length == 0 ) {
|
|
||||||
add("{}");
|
|
||||||
} else {
|
|
||||||
tabs += "\t";
|
|
||||||
add("{\n");
|
|
||||||
for( f in fl ) {
|
|
||||||
add(tabs);
|
|
||||||
add(f.name+" : ");
|
|
||||||
expr(f.e);
|
|
||||||
add(",\n");
|
|
||||||
}
|
|
||||||
tabs = tabs.substr(1);
|
|
||||||
add("}");
|
|
||||||
}
|
|
||||||
case ETernary(c,e1,e2):
|
|
||||||
expr(c);
|
|
||||||
add(" ? ");
|
|
||||||
expr(e1);
|
|
||||||
add(" : ");
|
|
||||||
expr(e2);
|
|
||||||
case ESwitch(e, cases, def):
|
|
||||||
add("switch( ");
|
|
||||||
expr(e);
|
|
||||||
add(") {");
|
|
||||||
for( c in cases ) {
|
|
||||||
add("case ");
|
|
||||||
var first = true;
|
|
||||||
for( v in c.values ) {
|
|
||||||
if( first ) first = false else add(", ");
|
|
||||||
expr(v);
|
|
||||||
}
|
|
||||||
add(": ");
|
|
||||||
expr(c.expr);
|
|
||||||
add(";\n");
|
|
||||||
}
|
|
||||||
if( def != null ) {
|
|
||||||
add("default: ");
|
|
||||||
expr(def);
|
|
||||||
add(";\n");
|
|
||||||
}
|
|
||||||
add("}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function toString( e : Expr ) {
|
public inline static function toString( e : Expr ) {
|
||||||
return new Printer().exprToString(e);
|
return new Printer().exprToString(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public inline static function compareErrors( e1 : Error, e2 : Error ) {
|
||||||
|
return Type.enumEq(Tools.cleanError(e1), Tools.cleanError(e2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline static function getPrintableError( e : Error ) {
|
||||||
|
return Tools.cleanError(switch( Tools.cleanError(e) ) {
|
||||||
|
case EPreset(msg): Parser.getBaseError(ECustom(msg.toString()));
|
||||||
|
default: e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function errorToString( e : Error ) {
|
||||||
|
var message = switch( Tools.cleanError(e) ) {
|
||||||
|
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF (End Of File)" : String.fromCharCode(c))+"' ("+c+")";
|
||||||
|
case EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||||
|
case EUnterminatedString: "Unterminated string";
|
||||||
|
case EUnterminatedComment: "Unterminated comment";
|
||||||
|
case EUnterminatedRegex: "Unterminated regular expression";
|
||||||
|
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
|
||||||
|
case EUnknownVariable(v): "Unknown variable: "+v;
|
||||||
|
case EInvalidIterator(v): "Invalid iterator: "+v;
|
||||||
|
case EInvalidType(t): "Invalid type: "+t;
|
||||||
|
case EInvalidOp(op): "Invalid operator: "+op;
|
||||||
|
case EInvalidAccess(f, on) if (on != null): "Invalid access to field " + f + " on " + on;
|
||||||
|
case EInvalidAccess(f): "Invalid access to field " + f;
|
||||||
|
case ECustom(msg): msg;
|
||||||
|
case EPreset(msg): msg.toString();
|
||||||
|
case EInvalidClass(cla): "Invalid class: " + cla + " was not found.";
|
||||||
|
case EAlreadyExistingClass(cla): 'Custom Class named $cla already exists.';
|
||||||
|
case EInvalidEscape(s): "Invalid escape sequence: " + s;
|
||||||
|
};
|
||||||
|
#if hscriptPos
|
||||||
|
return e.origin + ":" + e.line + ": " + message;
|
||||||
|
#else
|
||||||
|
return message;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+308
-54
@@ -1,68 +1,322 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
import hscript.utils.UnsafeReflect;
|
||||||
|
|
||||||
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(_): f(e);
|
||||||
case EVar(_, _, e): if( e != null ) f(e);
|
case EImport(c, _): f(e);
|
||||||
case EParent(e): f(e);
|
case EClass(_, e, _, _): for( a in e ) f(a);
|
||||||
case EBlock(el): for( e in el ) f(e);
|
case EVar(_, _, e): if( e != null ) f(e);
|
||||||
case EField(e, _): f(e);
|
case EParent(e, _): f(e);
|
||||||
case EBinop(_, e1, e2): f(e1); f(e2);
|
case EBlock(el): for( e in el ) f(e);
|
||||||
case EUnop(_, _, e): f(e);
|
case EField(e, _): f(e);
|
||||||
case ECall(e, args): f(e); for( a in args ) f(a);
|
case EBinop(_, e1, e2): f(e1); f(e2);
|
||||||
case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2);
|
case EUnop(_, _, e): f(e);
|
||||||
case EWhile(c, e): f(c); f(e);
|
case ECall(e, args): f(e); for( a in args ) f(a);
|
||||||
case EDoWhile(c, e): f(c); f(e);
|
case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2);
|
||||||
case EFor(_, it, e): f(it); f(e);
|
case EWhile(c, e): f(c); f(e);
|
||||||
case EBreak,EContinue:
|
case EDoWhile(c, e): f(c); f(e);
|
||||||
case EFunction(_, e, _, _): f(e);
|
case EFor(_, it, e): f(it); f(e);
|
||||||
case EReturn(e): if( e != null ) f(e);
|
case EForKeyValue(_, it, e, _): f(it); f(e);
|
||||||
case EArray(e, i): f(e); f(i);
|
case EBreak,EContinue:
|
||||||
case EArrayDecl(el): for( e in el ) f(e);
|
case EFunction(_, e, _, _): f(e);
|
||||||
case ENew(_,el): for( e in el ) f(e);
|
case EReturn(e): if( e != null ) f(e);
|
||||||
case EThrow(e): f(e);
|
case EArray(e, i): f(e); f(i);
|
||||||
case ETry(e, _, _, c): f(e); f(c);
|
case EMapDecl(type, keys, values): for( e in keys ) f(e); for( e in values ) f(e);
|
||||||
case EObject(fl): for( fi in fl ) f(fi.e);
|
case EArrayDecl(el): for( e in el ) f(e);
|
||||||
case ETernary(c, e1, e2): f(c); f(e1); f(e2);
|
case ENew(_,el): for( e in el ) f(e);
|
||||||
case ESwitch(e, cases, def):
|
case EThrow(e): f(e);
|
||||||
f(e);
|
case ETry(e, _, _, c): f(e); f(c);
|
||||||
for( c in cases ) {
|
case EObject(fl): for( fi in fl ) f(fi.e);
|
||||||
for( v in c.values ) f(v);
|
case ETernary(c, e1, e2): f(c); f(e1); f(e2);
|
||||||
f(c.expr);
|
case ESwitch(e, cases, def):
|
||||||
}
|
f(e);
|
||||||
if( def != null ) f(def);
|
for( c in cases ) {
|
||||||
|
for( v in c.values ) f(v);
|
||||||
|
f(c.expr);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
case EInfo(info, e): f(e);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function iterExprRecursive( ve : Expr, f : Expr -> Void ) {
|
||||||
|
var rec = (e:Expr) -> iterExprRecursive(e, f);
|
||||||
|
f(ve);
|
||||||
|
switch( expr(ve) ) {
|
||||||
|
case EConst(_), EIdent(_): {}
|
||||||
|
case EImport(c, _): {}
|
||||||
|
case EClass(_, e, _, _): for( a in e ) rec(a);
|
||||||
|
case EVar(_, _, e): if( e != null ) rec(e);
|
||||||
|
case EParent(e, _): rec(e);
|
||||||
|
case EBlock(el): for( e in el ) rec(e);
|
||||||
|
case EField(e, _): rec(e);
|
||||||
|
case EBinop(_, e1, e2): rec(e1); rec(e2);
|
||||||
|
case EUnop(_, _, e): rec(e);
|
||||||
|
case ECall(e, args): rec(e); for( a in args ) rec(a);
|
||||||
|
case EIf(c, e1, e2): rec(c); rec(e1); if( e2 != null ) rec(e2);
|
||||||
|
case EWhile(c, e): rec(c); rec(e);
|
||||||
|
case EDoWhile(c, e): rec(c); rec(e);
|
||||||
|
case EFor(_, it, e): rec(it); rec(e);
|
||||||
|
case EForKeyValue(_, it, e, _): rec(it); rec(e);
|
||||||
|
case EBreak,EContinue: {}
|
||||||
|
case EFunction(_, e, _, _): rec(e);
|
||||||
|
case EReturn(e): if( e != null ) rec(e);
|
||||||
|
case EArray(e, i): rec(e); rec(i);
|
||||||
|
case EMapDecl(type, keys, values): for( e in keys ) rec(e); for( e in values ) rec(e);
|
||||||
|
case EArrayDecl(el): for( e in el ) rec(e);
|
||||||
|
case ENew(_,el): for( e in el ) rec(e);
|
||||||
|
case EThrow(e): rec(e);
|
||||||
|
case ETry(e, _, _, c): rec(e); rec(c);
|
||||||
|
case EObject(fl): for( fi in fl ) rec(fi.e);
|
||||||
|
case ETernary(c, e1, e2): rec(c); rec(e1); rec(e2);
|
||||||
|
case ESwitch(e, cases, def):
|
||||||
|
rec(e);
|
||||||
|
for( c in cases ) {
|
||||||
|
for( v in c.values ) rec(v);
|
||||||
|
rec(c.expr);
|
||||||
|
}
|
||||||
|
if( def != null ) rec(def);
|
||||||
|
case EMeta(name, args, e): if( args != null ) for( a in args ) rec(a); rec(e);
|
||||||
|
case ECheckType(e,_): rec(e);
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
case EInfo(info, e): rec(e);
|
||||||
|
#end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, p, s): EVar(n, t, if( e != null ) f(e) else null, p, s);
|
||||||
case EParent(e): EParent(f(e));
|
case EParent(e, no): EParent(f(e), no);
|
||||||
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
case EBlock(el): EBlock([for( e in el ) f(e)]);
|
||||||
case EField(e, fi): EField(f(e),fi);
|
case EField(e, fi, s): EField(f(e),fi,s);
|
||||||
case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
|
case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
|
||||||
case EUnop(op, pre, e): EUnop(op, pre, f(e));
|
case EUnop(op, pre, e): EUnop(op, pre, f(e));
|
||||||
case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]);
|
case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]);
|
||||||
case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null);
|
case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null);
|
||||||
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 EForKeyValue(v, it, e, ithv): EForKeyValue(v, f(it), f(e), ithv);
|
||||||
case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
|
case EFunction(args, e, name, t, p, s, o): EFunction(args, f(e), name, t, p, s, o);
|
||||||
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));
|
||||||
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
|
case EMapDecl(type, keys, values): EMapDecl(type, [for( e in keys ) f(e)], [for( e in values ) f(e)]);
|
||||||
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
|
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
|
||||||
case EThrow(e): EThrow(f(e));
|
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
|
||||||
case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
|
case EThrow(e): EThrow(f(e));
|
||||||
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
|
case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
|
||||||
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
|
case EObject(fl): EObject([for( fi in fl ) new ObjectField(fi.name, f(fi.e))]);
|
||||||
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 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 EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
|
||||||
|
case ECheckType(e,t): ECheckType(f(e), t);
|
||||||
|
case EImport(c, m): EImport(c, m);
|
||||||
|
case EClass(name, el, extend, interfaces): EClass(name, [for( e in el ) f(e)], extend, interfaces);
|
||||||
|
#if !HSCRIPT_NO_INT_VARS
|
||||||
|
case EInfo(info, e): EInfo(info, f(e));
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
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 cleanError( e : Error ) {
|
||||||
|
#if hscriptPos
|
||||||
|
return e.e;
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function mk( e : ExprDef, p : Expr ):Expr {
|
||||||
|
#if hscriptPos
|
||||||
|
return new Expr(e, p.pmin, p.pmax, p.origin, p.line);
|
||||||
|
#else
|
||||||
|
return e;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isValidBinOp(op:String):Bool {
|
||||||
|
if(op == ("??"+"=")) return true;
|
||||||
|
return switch(op) {
|
||||||
|
case "+" | "-" | "*" | "/" | "%" | "&" | "|" | "^" | "<<" | ">>" | ">>>" | "==" | "!=" | ">=" | "<=" | ">" | "<" | "||" | "&&" | "is" | "=" | "??" | "..." | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | ">>>=": true;
|
||||||
|
case "=>": true;
|
||||||
|
default: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getOpEnum(op:String):Binop {
|
||||||
|
return switch(op) {
|
||||||
|
case "+": OpAdd;
|
||||||
|
case "-": OpSub;
|
||||||
|
case "*": OpMult;
|
||||||
|
case "/": OpDiv;
|
||||||
|
case "%": OpMod;
|
||||||
|
case "&": OpAnd;
|
||||||
|
case "|": OpOr;
|
||||||
|
case "^": OpXor;
|
||||||
|
case "<<": OpShl;
|
||||||
|
case ">>": OpShr;
|
||||||
|
case ">>>": OpUShr;
|
||||||
|
case "==": OpEq;
|
||||||
|
case "!=": OpNotEq;
|
||||||
|
case ">=": OpGte;
|
||||||
|
case "<=": OpLte;
|
||||||
|
case ">": OpGt;
|
||||||
|
case "<": OpLt;
|
||||||
|
case "||": OpBoolOr;
|
||||||
|
case "&&": OpBoolAnd;
|
||||||
|
case "is": OpIs;
|
||||||
|
case "=": OpAssign;
|
||||||
|
case "=>": OpArrow;
|
||||||
|
case "??": OpNullCoal;
|
||||||
|
case "...": OpInterval;
|
||||||
|
default: {
|
||||||
|
var op2 = op.substr(0, op.length - 1);
|
||||||
|
if(isValidBinOp(op2)) {
|
||||||
|
return OpAssignOp(getOpEnum(op2));
|
||||||
|
}
|
||||||
|
throw "Unknown binary operator: " + op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getUnopEnum(op:String):Unop {
|
||||||
|
return switch(op) {
|
||||||
|
case "++": OpIncrement;
|
||||||
|
case "--": OpDecrement;
|
||||||
|
case "!": OpNot;
|
||||||
|
case "-": OpNeg;
|
||||||
|
case "~": OpNegBits;
|
||||||
|
case "...": OpSpread;
|
||||||
|
default: throw "Unknown unary operator: " + op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var priorities = [
|
||||||
|
["%"],
|
||||||
|
["*", "/"],
|
||||||
|
["+", "-"],
|
||||||
|
["<<", ">>", ">>>"],
|
||||||
|
["|", "&", "^"],
|
||||||
|
["==", "!=", ">", "<", ">=", "<="],
|
||||||
|
["..."],
|
||||||
|
["&&"],
|
||||||
|
["||"],
|
||||||
|
["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^=","=>","??"+"="],
|
||||||
|
["->", "??"],
|
||||||
|
["is"]
|
||||||
|
];
|
||||||
|
public static function checkOpPrecedence(mainOp:String, leftOp:String, rightOp:String):Int {
|
||||||
|
var mainOpGroup = getOpGroup(mainOp);
|
||||||
|
var leftOpGroup = getOpGroup(leftOp);
|
||||||
|
var rightOpGroup = getOpGroup(rightOp);
|
||||||
|
|
||||||
|
var leftParam = false;
|
||||||
|
var rightParam = false;
|
||||||
|
|
||||||
|
if(leftOpGroup > mainOpGroup && leftOpGroup != -1) leftParam = true;
|
||||||
|
if(rightOpGroup > mainOpGroup && rightOpGroup != -1) rightParam = true;
|
||||||
|
|
||||||
|
if(!leftParam && !rightParam) {
|
||||||
|
var mainOpIndex = getOpIndex(mainOp);
|
||||||
|
var leftOpIndex = getOpIndex(leftOp);
|
||||||
|
var rightOpIndex = getOpIndex(rightOp);
|
||||||
|
|
||||||
|
if(mainOpGroup == rightOpGroup) {
|
||||||
|
if(rightOpIndex < mainOpIndex) rightParam = true;
|
||||||
|
}
|
||||||
|
if(leftOpGroup == mainOpGroup) {
|
||||||
|
if(leftOpIndex < mainOpIndex) leftParam = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to index
|
||||||
|
if(!leftParam && !rightParam) return -1;
|
||||||
|
if(leftParam && rightParam) return 2;
|
||||||
|
if(leftParam) return 0;
|
||||||
|
if(rightParam) return 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getOpIndex(op:String):Int {
|
||||||
|
if(op == "_") return -1;
|
||||||
|
var i = 0;
|
||||||
|
for(p in priorities) {
|
||||||
|
for(pp in p) {
|
||||||
|
if(op == pp)
|
||||||
|
return i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getOpGroup(op:String):Int {
|
||||||
|
if(op == "_") return -1;
|
||||||
|
var i = 0;
|
||||||
|
for(p in priorities) {
|
||||||
|
for(pp in p) {
|
||||||
|
if(op == pp)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function getEnum(cl:Enum<Dynamic>):Dynamic {
|
||||||
|
var enumThingy:Dynamic = {};
|
||||||
|
for (c in cl.getConstructors()) {
|
||||||
|
try {
|
||||||
|
UnsafeReflect.setField(enumThingy, c, cl.createByName(c));
|
||||||
|
} catch(e) {
|
||||||
|
try {
|
||||||
|
UnsafeReflect.setField(enumThingy, c, Reflect.makeVarArgs((args:Array<Dynamic>) -> cl.createByName(c, args)));
|
||||||
|
} catch(ex) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enumThingy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,518 @@
|
|||||||
|
package hscript.macros;
|
||||||
|
|
||||||
|
#if macro
|
||||||
|
import haxe.macro.Type.ClassType;
|
||||||
|
import Type.ValueType;
|
||||||
|
import haxe.macro.Expr.Function;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Type.MetaAccess;
|
||||||
|
import haxe.macro.Type.FieldKind;
|
||||||
|
import haxe.macro.Type.ClassField;
|
||||||
|
import haxe.macro.Type.VarAccess;
|
||||||
|
import haxe.macro.*;
|
||||||
|
import Sys;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
class ClassExtendMacro {
|
||||||
|
public static inline final FUNC_PREFIX = "_HX_SUPER__";
|
||||||
|
public static inline final CLASS_SUFFIX = "_HSX";
|
||||||
|
|
||||||
|
public static var unallowedMetas:Array<String> = [":bitmap", ":noCustomClass", ":generic"];
|
||||||
|
|
||||||
|
public static var modifiedClasses:Array<String> = [];
|
||||||
|
|
||||||
|
public static function init() {
|
||||||
|
#if !display
|
||||||
|
#if CUSTOM_CLASSES
|
||||||
|
if(Context.defined("display")) return;
|
||||||
|
for(apply in Config.ALLOWED_CUSTOM_CLASSES) {
|
||||||
|
Compiler.addGlobalMetadata(apply, "@:build(hscript.macros.ClassExtendMacro.build())");
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function build():Array<Field> {
|
||||||
|
var fields = Context.getBuildFields();
|
||||||
|
var clRef = Context.getLocalClass();
|
||||||
|
if (clRef == null) return fields;
|
||||||
|
var cl = clRef.get();
|
||||||
|
|
||||||
|
if (cl.isAbstract || cl.isExtern || cl.isFinal || cl.isInterface) return fields;
|
||||||
|
if (!cl.name.endsWith("_Impl_") && !cl.name.endsWith(CLASS_SUFFIX) && !cl.name.endsWith("_HSC")) {
|
||||||
|
var metas = cl.meta.get();
|
||||||
|
|
||||||
|
for(m in metas)
|
||||||
|
if (unallowedMetas.contains(m.name))
|
||||||
|
return fields;
|
||||||
|
|
||||||
|
if(cl.params.length > 0)
|
||||||
|
return fields;
|
||||||
|
|
||||||
|
var key = cl.module;
|
||||||
|
var fkey = cl.module + "." + cl.name;
|
||||||
|
if(key == "sys.thread.FixedThreadPool") return fields; // Error: Type name sys.thread.Worker_HSX is redefined from module sys.thread.FixedThreadPool
|
||||||
|
if(key == "StdTypes") return fields; // Error: Cant extend basic class
|
||||||
|
if(key == "Xml") return fields; // Error: Cant extend basic class
|
||||||
|
if(key == "Date") return fields; // Error: Cant extend basic class
|
||||||
|
if(key == "away3d.tools.commands.Mirror") return fields; // Error: Unknown identifier
|
||||||
|
if(key == "away3d.tools.commands.SphereMaker") return fields; // Error: Unknown identifier
|
||||||
|
if(key == "away3d.tools.commands.Weld") return fields; // Error: Unknown identifier
|
||||||
|
if(fkey == "hscript.CustomClassHandler.TemplateClass") return fields; // Error: Redefined
|
||||||
|
if(key == "sys.thread.EventLoop") return fields; // Error: cant override force inlined
|
||||||
|
if(Config.DISALLOW_CUSTOM_CLASSES.contains(cl.module) || Config.DISALLOW_CUSTOM_CLASSES.contains(fkey)) return fields;
|
||||||
|
if(cl.module.contains("_")) return fields; // Weird issue, sorry
|
||||||
|
|
||||||
|
var superFields = [];
|
||||||
|
if(cl.superClass != null) {
|
||||||
|
var _superFields = cl.superClass.t.get().fields.get();
|
||||||
|
_superFields = []; // Comment to enable super support, (broken)
|
||||||
|
for(field in _superFields) {
|
||||||
|
if(!field.kind.match(FMethod(_))) // only catch methods
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var nfield = @:privateAccess TypeTools.toField(field);
|
||||||
|
switch ([field.kind, field.type]) {
|
||||||
|
case [FMethod(kind), TFun(args, ret)]:
|
||||||
|
if(kind == MethInline)
|
||||||
|
nfield.access.push(AInline);
|
||||||
|
if(kind == MethDynamic)
|
||||||
|
nfield.access.push(ADynamic);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(nfield.kind) {
|
||||||
|
case FFun(fun):
|
||||||
|
if (fun.params != null && fun.params.length > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fun.ret = Utils.fixStdTypes(fun.ret);
|
||||||
|
|
||||||
|
var metas = nfield.meta;
|
||||||
|
var defaultValues:Map<String, Dynamic> = [];
|
||||||
|
var defaultEntry = null;
|
||||||
|
var isGeneric = false;
|
||||||
|
for(m in metas) {
|
||||||
|
if(m.name == ":value") {
|
||||||
|
defaultEntry = m;
|
||||||
|
switch(m.params[0].expr) {
|
||||||
|
case EObjectDecl(fields):
|
||||||
|
for(fil in fields)
|
||||||
|
defaultValues[fil.field] = fil.expr;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(m.name == ":generic")
|
||||||
|
isGeneric = true;
|
||||||
|
}
|
||||||
|
if(isGeneric) continue;
|
||||||
|
|
||||||
|
if(defaultEntry != null)
|
||||||
|
metas.remove(defaultEntry);
|
||||||
|
|
||||||
|
for(arg in fun.args) {
|
||||||
|
var opt = false;
|
||||||
|
if(defaultValues.exists(arg.name)) {
|
||||||
|
arg.value = defaultValues[arg.name];
|
||||||
|
arg.opt = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg.type = Utils.fixStdTypes(arg.type);
|
||||||
|
|
||||||
|
if(arg.opt) {
|
||||||
|
if(arg.type.getParameters()[0].name != "Null")
|
||||||
|
arg.type = TPath({name: "Null", params: [TPType(arg.type)], pack: []});//macro {Null<Dynamic>};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
superFields.push(nfield);
|
||||||
|
} catch(e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//superFields = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var shadowClass = macro class {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var definedFields:Array<String> = [];
|
||||||
|
|
||||||
|
//trace(getModuleName(cl));
|
||||||
|
|
||||||
|
var hasNew = false;
|
||||||
|
|
||||||
|
for(_field in [fields.copy(), superFields.copy()])
|
||||||
|
for(f in _field) {
|
||||||
|
if (f == null)
|
||||||
|
continue;
|
||||||
|
if (f.name == "new") {
|
||||||
|
hasNew = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (f.name.startsWith(FUNC_PREFIX))
|
||||||
|
continue;
|
||||||
|
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern) || f.access.contains(AInline))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(f.name == "hget" || f.name == "hset") continue; // sorry, no overwriting the hget and hset in custom classes, yet
|
||||||
|
if(definedFields.contains(f.name)) continue; // no duplicate fields
|
||||||
|
|
||||||
|
for(m in f.meta)
|
||||||
|
if (unallowedMetas.contains(m.name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch(f.kind) {
|
||||||
|
case FFun(fun):
|
||||||
|
if (fun == null)
|
||||||
|
continue;
|
||||||
|
if (fun.params != null && fun.params.length > 0) // TODO: Support for this maybe?
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(fun.params == null)
|
||||||
|
fun.params = [];
|
||||||
|
|
||||||
|
var overrideExpr:Expr;
|
||||||
|
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||||
|
|
||||||
|
var name = f.name;
|
||||||
|
|
||||||
|
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
|
||||||
|
|
||||||
|
if (returns) {
|
||||||
|
overrideExpr = macro {
|
||||||
|
var name:String = $v{name};
|
||||||
|
|
||||||
|
if (__custom__variables != null) {
|
||||||
|
if(__custom__variables.exists(name)) {
|
||||||
|
var v:Dynamic = null;
|
||||||
|
if (Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||||
|
return v($a{arguments});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.$name($a{arguments});
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
overrideExpr = macro {
|
||||||
|
var name:String = $v{name};
|
||||||
|
|
||||||
|
if (__custom__variables != null) {
|
||||||
|
if(__custom__variables.exists(name)) {
|
||||||
|
var v:Dynamic = null;
|
||||||
|
if (Reflect.isFunction(v = __custom__variables.get(name))) {
|
||||||
|
v($a{arguments});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.$name($a{arguments});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var superFuncExpr:Expr = returns ? {
|
||||||
|
macro return super.$name($a{arguments});
|
||||||
|
} : {
|
||||||
|
macro super.$name($a{arguments});
|
||||||
|
};
|
||||||
|
|
||||||
|
var func:Function = {
|
||||||
|
ret: fun.ret,
|
||||||
|
params: fun.params.copy(),
|
||||||
|
expr: overrideExpr,
|
||||||
|
args: fun.args.copy()
|
||||||
|
};
|
||||||
|
|
||||||
|
var overrideField:Field = {
|
||||||
|
name: f.name,
|
||||||
|
access: f.access.copy(),
|
||||||
|
kind: FFun(func),
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
doc: f.doc,
|
||||||
|
meta: f.meta.copy()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!overrideField.access.contains(AOverride))
|
||||||
|
overrideField.access.push(AOverride);
|
||||||
|
|
||||||
|
var superField:Field = {
|
||||||
|
name: '$FUNC_PREFIX${f.name}',
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FFun({
|
||||||
|
ret: fun.ret,
|
||||||
|
params: fun.params.copy(),
|
||||||
|
expr: superFuncExpr,
|
||||||
|
args: fun.args.copy()
|
||||||
|
}),
|
||||||
|
access: f.access.copy()
|
||||||
|
};
|
||||||
|
if (superField.access.contains(AOverride))
|
||||||
|
superField.access.remove(AOverride);
|
||||||
|
shadowClass.fields.push(overrideField);
|
||||||
|
shadowClass.fields.push(superField);
|
||||||
|
definedFields.push(f.name);
|
||||||
|
default:
|
||||||
|
// fuck off >:(
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalFields = definedFields.length;
|
||||||
|
|
||||||
|
if(totalFields == 0 && !hasNew) {
|
||||||
|
//Sys.println(cl.pack.join(".") + "." + cl.name + ", " + totalFields);
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowClass.kind = TDClass({
|
||||||
|
pack: cl.pack.copy(),
|
||||||
|
name: cl.name
|
||||||
|
}, [
|
||||||
|
{name: "IHScriptCustomBehaviour", pack: ["hscript"]},
|
||||||
|
{name: "IHScriptCustomClassBehaviour", pack: ["hscript"]}
|
||||||
|
], false, true, false);
|
||||||
|
shadowClass.name = '${cl.name}$CLASS_SUFFIX';
|
||||||
|
var imports = Context.getLocalImports().copy();
|
||||||
|
Utils.setupMetas(shadowClass, imports);
|
||||||
|
Utils.processImport(imports, "hscript.utils.UnsafeReflect", "UnsafeReflect");
|
||||||
|
|
||||||
|
// Adding hscript getters and setters
|
||||||
|
|
||||||
|
// TODO: Remove
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "__interp",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FVar(TPath({
|
||||||
|
pack: ['hscript'],
|
||||||
|
name: 'Interp'
|
||||||
|
})),
|
||||||
|
access: [APublic]
|
||||||
|
});
|
||||||
|
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "__custom__variables",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FVar(TPath({
|
||||||
|
pack: [],
|
||||||
|
name: 'Map',
|
||||||
|
params: [TPType(TPath({name: "String", pack: []})), TPType(TPath({name: "Dynamic", pack: []}))]
|
||||||
|
})),
|
||||||
|
access: [APublic]
|
||||||
|
});
|
||||||
|
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "__allowSetGet",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FVar(TPath({
|
||||||
|
pack: [],
|
||||||
|
name: 'Bool',
|
||||||
|
}), macro true),
|
||||||
|
access: [APublic]
|
||||||
|
});
|
||||||
|
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "__callGetter",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FFun({
|
||||||
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
|
params: [],
|
||||||
|
expr: macro {
|
||||||
|
__allowSetGet = false;
|
||||||
|
var v = __custom__variables.get("get_" + name)();
|
||||||
|
__allowSetGet = true;
|
||||||
|
return v;
|
||||||
|
},
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
opt: false,
|
||||||
|
meta: [],
|
||||||
|
type: TPath({name: "String", pack: []})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
access: [APublic]
|
||||||
|
});
|
||||||
|
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "__callSetter",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
kind: FFun({
|
||||||
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
|
params: [],
|
||||||
|
expr: macro {
|
||||||
|
__allowSetGet = false;
|
||||||
|
var v = __custom__variables.get("set_" + name)(val);
|
||||||
|
__allowSetGet = true;
|
||||||
|
return v;
|
||||||
|
},
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
opt: false,
|
||||||
|
meta: [],
|
||||||
|
type: TPath({name: "String", pack: []})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "val",
|
||||||
|
opt: false,
|
||||||
|
meta: [],
|
||||||
|
type: TPath({name: "Dynamic", pack: []})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
access: [APublic]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Todo: make it possible to override
|
||||||
|
if(cl.name == "FunkinShader" || cl.name == "CustomShader" || cl.name == "MultiThreadedScript") {
|
||||||
|
Context.defineModule(cl.module, [shadowClass], imports);
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasHgetInSuper = false;
|
||||||
|
var hasHsetInSuper = false;
|
||||||
|
|
||||||
|
if(cl.name == "CustomShader") {
|
||||||
|
hasHgetInSuper = hasHsetInSuper = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: somehow check the super super class
|
||||||
|
for(_field in [fields.copy(), superFields.copy()])
|
||||||
|
for(f in _field) {
|
||||||
|
if (f.name == "new")
|
||||||
|
continue;
|
||||||
|
if (f.name.startsWith(FUNC_PREFIX))
|
||||||
|
continue;
|
||||||
|
if (f.access.contains(ADynamic) || f.access.contains(AStatic) || f.access.contains(AExtern))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch(f.kind) {
|
||||||
|
case FFun(fun):
|
||||||
|
if (fun.params != null && fun.params.length > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!hasHgetInSuper)
|
||||||
|
hasHgetInSuper = f.name == "hget";
|
||||||
|
if(!hasHsetInSuper)
|
||||||
|
hasHsetInSuper = f.name == "hset";
|
||||||
|
|
||||||
|
if(hasHgetInSuper && hasHsetInSuper)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hgetField = if(hasHgetInSuper) {
|
||||||
|
macro {
|
||||||
|
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||||
|
return __callGetter(name);
|
||||||
|
if (__custom__variables.exists(name))
|
||||||
|
return __custom__variables.get(name);
|
||||||
|
return super.hget(name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
macro {
|
||||||
|
if(__allowSetGet && __custom__variables.exists("get_" + name))
|
||||||
|
return __callGetter(name);
|
||||||
|
if (__custom__variables.exists(name))
|
||||||
|
return __custom__variables.get(name);
|
||||||
|
return UnsafeReflect.getProperty(this, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hsetField = if(hasHsetInSuper) {
|
||||||
|
macro {
|
||||||
|
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||||
|
return __callSetter(name, val);
|
||||||
|
if (__custom__variables.exists(name)) {
|
||||||
|
__custom__variables.set(name, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
return super.hset(this, name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
macro {
|
||||||
|
if(__allowSetGet && __custom__variables.exists("set_" + name))
|
||||||
|
return __callSetter(name, val);
|
||||||
|
if (__custom__variables.exists(name)) {
|
||||||
|
__custom__variables.set(name, val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
UnsafeReflect.setProperty(this, name, val);
|
||||||
|
return UnsafeReflect.field(this, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if(hasHsetInSuper || hasHgetInSuper) return fields;
|
||||||
|
|
||||||
|
//trace(cl.name);
|
||||||
|
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "hset",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
access: hasHsetInSuper ? [AOverride, APublic] : [APublic],
|
||||||
|
kind: FFun({
|
||||||
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
|
params: [],
|
||||||
|
expr: hsetField,
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
opt: false,
|
||||||
|
meta: [],
|
||||||
|
type: TPath({name: "String", pack: []})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "val",
|
||||||
|
opt: false,
|
||||||
|
meta: [],
|
||||||
|
type: TPath({name: "Dynamic", pack: []})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
shadowClass.fields.push({
|
||||||
|
name: "hget",
|
||||||
|
pos: Context.currentPos(),
|
||||||
|
access: hasHgetInSuper ? [AOverride, APublic] : [APublic],
|
||||||
|
kind: FFun({
|
||||||
|
ret: TPath({name: 'Dynamic', pack: []}),
|
||||||
|
params: [],
|
||||||
|
expr: hgetField,
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
opt: false,
|
||||||
|
meta: [],
|
||||||
|
type: TPath({name: "String", pack: []})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
/*var p = new Printer();
|
||||||
|
var aa = p.printTypeDefinition(shadowClass);
|
||||||
|
if(aa.length < 5024)
|
||||||
|
trace(aa);
|
||||||
|
if(aa.indexOf("pack") >= 0)
|
||||||
|
if(cl.name == "FunkinShader")*/
|
||||||
|
|
||||||
|
Context.defineModule(cl.module, [shadowClass], imports);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
class ClassExtendMacro {
|
||||||
|
public var usedClass:Class<Dynamic>;
|
||||||
|
public var className:String;
|
||||||
|
}
|
||||||
|
#end
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package hscript.macros;
|
||||||
|
|
||||||
|
#if macro
|
||||||
|
import Type.ValueType;
|
||||||
|
import haxe.macro.ComplexTypeTools;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Context;
|
||||||
|
import haxe.macro.Printer;
|
||||||
|
import haxe.macro.Compiler;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
class UsingHandler {
|
||||||
|
public static function init() {
|
||||||
|
#if !display
|
||||||
|
if(Context.defined("display")) return;
|
||||||
|
for(apply in Config.ALLOWED_ABSTRACT_AND_ENUM) {
|
||||||
|
Compiler.addGlobalMetadata(apply, '@:build(hscript.macros.UsingHandler.build())');
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function build():Array<Field> {
|
||||||
|
var fields = Context.getBuildFields();
|
||||||
|
var clRef = Context.getLocalClass();
|
||||||
|
if (clRef == null) return fields;
|
||||||
|
var cl = clRef.get();
|
||||||
|
|
||||||
|
if (/* cl.name.startsWith("Flx") && */ cl.name.endsWith("_Impl_") && cl.params.length <= 0 && !cl.meta.has(":multiType") && !cl.name.contains("_HSC")) {
|
||||||
|
var metas = cl.meta.get();
|
||||||
|
|
||||||
|
var trimEnum = cl.name.substr(0, cl.name.length - 6);
|
||||||
|
|
||||||
|
var key = cl.module;
|
||||||
|
var fkey = cl.module + "." + trimEnum;
|
||||||
|
if(key == "lime.system.Locale") return fields; // Error: Unknown identifier : currentLocale, Due to Func
|
||||||
|
if(key == "cpp.Function") return fields; // Error: Unknown identifier : nativeGetProcAddress, Due to Func
|
||||||
|
if(key == "haxe.ds.Vector") return fields; // Error: haxe.ds._Vector.VectorData<blit.T> has no field blit, Due to Func
|
||||||
|
if(key == "haxe.display.Display") return fields; // Error: haxe.display.DisplayItemKind<haxe.display.DisplayLiteral<Dynamic>> has no field Null, Due to Func
|
||||||
|
if(key == "cpp.Callable") return fields; // Error: cpp.Function.fromStaticFunction must be called on static function, Due to Func
|
||||||
|
if(key == "haxe.display.JsonAnonStatusKind") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
|
||||||
|
if(key == "cpp.CharStar") return fields; // Error: cannot initialize a variable of type 'char *' with an rvalue of type 'const char *', Due to Func
|
||||||
|
if(Config.DISALLOW_ABSTRACT_AND_ENUM.contains(cl.module) || Config.DISALLOW_ABSTRACT_AND_ENUM.contains(fkey)) return fields;
|
||||||
|
if(cl.module.contains("_")) return fields; // Weird issue, sorry
|
||||||
|
|
||||||
|
var shadowClass = macro class {
|
||||||
|
|
||||||
|
};
|
||||||
|
shadowClass.kind = TDClass();
|
||||||
|
shadowClass.params = switch(cl.params.length) {
|
||||||
|
case 0:
|
||||||
|
null;
|
||||||
|
case 1:
|
||||||
|
[{
|
||||||
|
name: "T",
|
||||||
|
}];
|
||||||
|
default:
|
||||||
|
[for(k=>e in cl.params) {
|
||||||
|
name: "T" + Std.int(k+1)
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
shadowClass.name = '${cl.name.substr(0, cl.name.length - 6)}_HSC';
|
||||||
|
|
||||||
|
var imports = Context.getLocalImports().copy();
|
||||||
|
Utils.setupMetas(shadowClass, imports);
|
||||||
|
//trace(cl.module);
|
||||||
|
|
||||||
|
for(f in fields)
|
||||||
|
switch(f.kind) {
|
||||||
|
case FFun(fun):
|
||||||
|
if (f.access.contains(AStatic)) {
|
||||||
|
if (fun.expr != null) {
|
||||||
|
fun.expr = macro @:privateAccess $e{fun.expr};
|
||||||
|
shadowClass.fields.push(f);
|
||||||
|
|
||||||
|
/*var trimEnum = cl.name.substr(0, cl.name.length - 6);
|
||||||
|
|
||||||
|
var returns:Bool = !fun.ret.match(TPath({name: "Void"}));
|
||||||
|
|
||||||
|
var name = f.name;
|
||||||
|
|
||||||
|
var arguments = fun.args == null ? [] : [for(a in fun.args) macro $i{a.name}];
|
||||||
|
|
||||||
|
var expr:Expr = Context.parse('${returns?"return " : ""} $trimEnum.$name(${[for(a in fun.args) a.name].join(", ")})', f.pos);
|
||||||
|
|
||||||
|
var func:Function = {
|
||||||
|
ret: fun.ret,
|
||||||
|
params: fun.params.copy(),
|
||||||
|
expr: expr,
|
||||||
|
args: fun.args.copy()
|
||||||
|
};
|
||||||
|
|
||||||
|
var field:Field = {
|
||||||
|
pos: f.pos,
|
||||||
|
name: f.name,
|
||||||
|
meta: f.meta,
|
||||||
|
kind: FFun(func),
|
||||||
|
doc: null,//f.doc,
|
||||||
|
access: [APublic, AStatic]
|
||||||
|
}
|
||||||
|
shadowClass.fields.push(field);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case FProp(get, set, t, e):
|
||||||
|
if (get == "default" && (set == "never" || set == "null")) {
|
||||||
|
shadowClass.fields.push(f);
|
||||||
|
}
|
||||||
|
case FVar(t, e):
|
||||||
|
if (f.access.contains(AStatic) || cl.meta.has(":enum") || f.name.toUpperCase() == f.name) {
|
||||||
|
var name:String = f.name;
|
||||||
|
var enumType:String = cl.name;
|
||||||
|
var pack = cl.module.split(".");
|
||||||
|
|
||||||
|
//trace(pack, cl.name, name, cl.module);
|
||||||
|
|
||||||
|
if(pack[pack.length - 1] == trimEnum)
|
||||||
|
pack.pop();
|
||||||
|
|
||||||
|
var complexType:ComplexType = t;
|
||||||
|
if(complexType == null && e != null) {
|
||||||
|
complexType = switch(e.expr) {
|
||||||
|
case EConst(CRegexp(_)): TPath({ name: "EReg", pack: [] });
|
||||||
|
|
||||||
|
default: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(complexType == null) {
|
||||||
|
complexType = TPath({
|
||||||
|
name: trimEnum,
|
||||||
|
pack: [],//pack
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var code = Context.parse('@:privateAccess ($trimEnum.$name)', f.pos); // '${pack.join(".")}.${trimEnum}.$name'
|
||||||
|
|
||||||
|
var field:Field = {
|
||||||
|
pos: f.pos,
|
||||||
|
name: f.name,
|
||||||
|
meta: f.meta,
|
||||||
|
kind: FVar(null, code),
|
||||||
|
doc: f.doc,
|
||||||
|
access: [APublic, AStatic]
|
||||||
|
}
|
||||||
|
|
||||||
|
shadowClass.fields.push(field);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
//var printer = new Printer();
|
||||||
|
//for(field in shadowClass.fields)
|
||||||
|
// trace(printer.printField(field));
|
||||||
|
|
||||||
|
Context.defineModule(cl.module, [shadowClass], imports);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package hscript.macros;
|
||||||
|
|
||||||
|
#if macro
|
||||||
|
import haxe.macro.Type.ClassType;
|
||||||
|
import Type.ValueType;
|
||||||
|
import haxe.macro.Expr.Function;
|
||||||
|
import haxe.macro.Expr;
|
||||||
|
import haxe.macro.Type.MetaAccess;
|
||||||
|
import haxe.macro.Type.FieldKind;
|
||||||
|
import haxe.macro.Type.ClassField;
|
||||||
|
import haxe.macro.Type.VarAccess;
|
||||||
|
import haxe.macro.*;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
public static function fixStdTypes(type:ComplexType) {
|
||||||
|
switch(type) {
|
||||||
|
case TPath({name: "StdTypes"}):
|
||||||
|
var a:TypePath = type.getParameters()[0];
|
||||||
|
a.name = a.sub;
|
||||||
|
a.sub = null;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function setupMetas(shadowClass:TypeDefinition, imports) {
|
||||||
|
shadowClass.meta = [];
|
||||||
|
shadowClass.meta.push({name: ":dox", params: [macro hide], pos: Context.currentPos()});
|
||||||
|
shadowClass.meta.push({name: ":noCompletion", params: [], pos: Context.currentPos()});
|
||||||
|
var module = Context.getModule(Context.getLocalModule());
|
||||||
|
for(t in module) {
|
||||||
|
switch(t) {
|
||||||
|
case TInst(t, params):
|
||||||
|
if (t != null) {
|
||||||
|
var e = t.get();
|
||||||
|
processModule(shadowClass, e.module, e.name);
|
||||||
|
processImport(imports, e.module, e.name);
|
||||||
|
}
|
||||||
|
case TEnum(t, params):
|
||||||
|
if (t != null) {
|
||||||
|
var e = t.get();
|
||||||
|
processModule(shadowClass, e.module, e.name);
|
||||||
|
processImport(imports, e.module, e.name);
|
||||||
|
}
|
||||||
|
case TType(t, params):
|
||||||
|
if (t != null) {
|
||||||
|
var e = t.get();
|
||||||
|
processModule(shadowClass, e.module, e.name);
|
||||||
|
processImport(imports, e.module, e.name);
|
||||||
|
}
|
||||||
|
case TAbstract(t, params):
|
||||||
|
if (t != null) {
|
||||||
|
var e = t.get();
|
||||||
|
processModule(shadowClass, e.module, e.name);
|
||||||
|
processImport(imports, e.module, e.name);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// not needed?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function processModule(shadowClass:TypeDefinition, module:String, n:String) {
|
||||||
|
if (n.endsWith("_Impl_"))
|
||||||
|
n = n.substr(0, n.length - 6);
|
||||||
|
if (module.endsWith("_Impl_"))
|
||||||
|
module = module.substr(0, module.length - 6);
|
||||||
|
|
||||||
|
shadowClass.meta.push(
|
||||||
|
{
|
||||||
|
name: ':access',
|
||||||
|
params: [
|
||||||
|
Context.parse(fixModuleName(module.endsWith('.${n}') ? module : '${module}.${n}'), Context.currentPos())
|
||||||
|
],
|
||||||
|
pos: Context.currentPos()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*public static function getModuleName(path:Type) {
|
||||||
|
switch(path) {
|
||||||
|
case TPath(name, pack):// | TDClass(name, pack):
|
||||||
|
var str = "";
|
||||||
|
for(p in pack) {
|
||||||
|
str += p + ".";
|
||||||
|
}
|
||||||
|
str += name;
|
||||||
|
return str;
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return "INVALID";
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public static function fixModuleName(name:String) {
|
||||||
|
return [for(s in name.split(".")) if (s.charAt(0) == "_") s.substr(1) else s].join(".");
|
||||||
|
}
|
||||||
|
public static function processImport(imports:Array<ImportExpr>, module:String, n:String) {
|
||||||
|
if (n.endsWith("_Impl_"))
|
||||||
|
n = n.substr(0, n.length - 6);
|
||||||
|
module = fixModuleName(module);
|
||||||
|
if (module.endsWith("_Impl_"))
|
||||||
|
module = module.substr(0, module.length - 6);
|
||||||
|
|
||||||
|
imports.push({
|
||||||
|
path: [for(m in module.split(".")) {
|
||||||
|
name: m,
|
||||||
|
pos: Context.currentPos()
|
||||||
|
}],
|
||||||
|
mode: INormal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function cleanExpr(expr:Expr, oldFunc:String, newFunc:String) {
|
||||||
|
if (expr == null) return;
|
||||||
|
if (expr.expr == null) return;
|
||||||
|
switch(expr.expr) {
|
||||||
|
case EConst(c):
|
||||||
|
switch(c) {
|
||||||
|
case CIdent(s):
|
||||||
|
if (s == oldFunc)
|
||||||
|
expr.expr = EConst(CIdent(newFunc));
|
||||||
|
case CString(s, b):
|
||||||
|
if (s == oldFunc)
|
||||||
|
expr.expr = EConst(CString(s, b));
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
case EField(e, field):
|
||||||
|
if (field == oldFunc && e != null) {
|
||||||
|
switch(e.expr) {
|
||||||
|
case EConst(c):
|
||||||
|
switch(c) {
|
||||||
|
case CIdent(s):
|
||||||
|
if (s == "super")
|
||||||
|
expr.expr = EField(e, newFunc);
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EParenthesis(e):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case EObjectDecl(fields):
|
||||||
|
for(f in fields) {
|
||||||
|
cleanExpr(f.expr, oldFunc, newFunc);
|
||||||
|
}
|
||||||
|
case EArrayDecl(values):
|
||||||
|
for(a in values) {
|
||||||
|
cleanExpr(a, oldFunc, newFunc);
|
||||||
|
}
|
||||||
|
case ECall(e, params):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case EBlock(exprs):
|
||||||
|
for(e in exprs)
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case EFor(it, expr):
|
||||||
|
cleanExpr(it, oldFunc, newFunc);
|
||||||
|
cleanExpr(expr, oldFunc, newFunc);
|
||||||
|
case EIf(econd, eif, eelse):
|
||||||
|
cleanExpr(econd, oldFunc, newFunc);
|
||||||
|
cleanExpr(eif, oldFunc, newFunc);
|
||||||
|
cleanExpr(eelse, oldFunc, newFunc);
|
||||||
|
case EWhile(econd, e, normalWhile):
|
||||||
|
cleanExpr(econd, oldFunc, newFunc);
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case ECast(e, t):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case ECheckType(e, t):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case ETry(e, catches):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
for(c in catches) {
|
||||||
|
cleanExpr(c.expr, oldFunc, newFunc);
|
||||||
|
}
|
||||||
|
case EThrow(e):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case ETernary(econd, eif, eelse):
|
||||||
|
cleanExpr(econd, oldFunc, newFunc);
|
||||||
|
cleanExpr(eif, oldFunc, newFunc);
|
||||||
|
cleanExpr(eelse, oldFunc, newFunc);
|
||||||
|
case ESwitch(e, cases, edef):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
for(c in cases) {
|
||||||
|
cleanExpr(c.expr, oldFunc, newFunc);
|
||||||
|
}
|
||||||
|
cleanExpr(edef, oldFunc, newFunc);
|
||||||
|
case EReturn(e):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case EIs(e, t):
|
||||||
|
cleanExpr(e, oldFunc, newFunc);
|
||||||
|
case EVars(vars):
|
||||||
|
for(v in vars) {
|
||||||
|
cleanExpr(v.expr, oldFunc, newFunc);
|
||||||
|
}
|
||||||
|
case ENew(t, params):
|
||||||
|
for(p in params) {
|
||||||
|
cleanExpr(p, oldFunc, newFunc);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#end
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
package hscript.utils;
|
||||||
|
|
||||||
|
#if cpp
|
||||||
|
import cpp.ObjectType;
|
||||||
|
#end
|
||||||
|
|
||||||
|
@:analyzer(ignore)
|
||||||
|
class UnsafeReflect {
|
||||||
|
public #if !cpp inline #end static function hasField(o:Dynamic, field:String):Bool {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
return o.__HasField(field);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.hasField(o, field);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function field(o:Dynamic, field:String):Dynamic {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
return o.__Field(field, untyped __cpp__("::hx::paccNever"));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.field(o, field);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function setField(o:Dynamic, field:String, value:Dynamic):Void {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
o.__SetField(field, value, untyped __cpp__("::hx::paccNever"));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.setField(o, field, value);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function getProperty(o:Dynamic, field:String):Dynamic {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
return o.__Field(field, untyped __cpp__("::hx::paccAlways"));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.getProperty(o, field);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function setProperty(o:Dynamic, field:String, value:Dynamic):Void {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
o.__SetField(field, value, untyped __cpp__("::hx::paccAlways"));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Reflect.setProperty(o, field);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function callFieldUnsafe(o:Dynamic, field:String, args:Array<Dynamic>):Dynamic {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
var func:Dynamic = o.__Field(field, untyped __cpp__("::hx::paccDynamic"));
|
||||||
|
untyped func.__SetThis(o);
|
||||||
|
return untyped func.__Run(args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.callMethod(o, func, args);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline static function callMethod(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||||
|
return Reflect.callMethod(o, func, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function callMethodSafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
if (func == null)
|
||||||
|
throw cpp.ErrorConstants.nullFunctionPointer;
|
||||||
|
untyped func.__SetThis(o);
|
||||||
|
return untyped func.__Run(args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.callMethod(o, func, args);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function callMethodUnsafe(o:Dynamic, func:haxe.Constraints.Function, args:Array<Dynamic>):Dynamic {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
untyped func.__SetThis(o);
|
||||||
|
return untyped func.__Run(args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.callMethod(o, func, args);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline static function fields(o:Dynamic):Array<String>
|
||||||
|
return Reflect.fields(o);
|
||||||
|
/*untyped {
|
||||||
|
if (o == null)
|
||||||
|
return new Array();
|
||||||
|
var a:Array<String> = [];
|
||||||
|
o.__GetFields(a);
|
||||||
|
return a;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function isFunction(f:Dynamic):Bool
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
return f.__GetType() == ObjectType.vtFunction;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.isFunction(f);
|
||||||
|
#end
|
||||||
|
|
||||||
|
public inline static function compare<T>(a:T, b:T):Int {
|
||||||
|
return Reflect.compare(a, b);
|
||||||
|
//return (a == b) ? 0 : (((a : Dynamic) > (b : Dynamic)) ? 1 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline static function compareMethods(f1:Dynamic, f2:Dynamic):Bool {
|
||||||
|
return Reflect.compareMethods(f1, f2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function isObject(v:Dynamic):Bool {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
var t:Int = v.__GetType();
|
||||||
|
return t == ObjectType.vtObject || t == ObjectType.vtClass || t == ObjectType.vtString || t == ObjectType.vtArray;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.isObject(v);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function isEnumValue(v:Dynamic):Bool {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
return v.__GetType() == ObjectType.vtEnum;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.isEnumValue(v);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function deleteField(o:Dynamic, field:String):Bool {
|
||||||
|
#if cpp
|
||||||
|
untyped {
|
||||||
|
return untyped __global__.__hxcpp_anon_remove(o, field);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return Reflect.deleteField(o, field);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public #if !cpp inline #end static function copy<T>(o:Null<T>):Null<T> {
|
||||||
|
#if cpp
|
||||||
|
if (o == null)
|
||||||
|
return null;
|
||||||
|
var t:Int = untyped o.__GetType();
|
||||||
|
if (t == ObjectType.vtString)
|
||||||
|
return o;
|
||||||
|
if (t == ObjectType.vtArray)
|
||||||
|
return untyped o.__Field("copy", untyped __cpp__("::hx::paccDynamic"))();
|
||||||
|
var o2:Dynamic = {};
|
||||||
|
for (f in UnsafeReflect.fields(o))
|
||||||
|
UnsafeReflect.setField(o2, f, UnsafeReflect.field(o, f));
|
||||||
|
return o2;
|
||||||
|
#else
|
||||||
|
return Reflect.copy(o);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
@:overload(function(f:Array<Dynamic>->Void):Dynamic {})
|
||||||
|
public static function makeVarArgs(f:Array<Dynamic>->Dynamic):Dynamic {
|
||||||
|
#if cpp
|
||||||
|
return untyped __global__.__hxcpp_create_var_args(f);
|
||||||
|
#else
|
||||||
|
return inline Reflect.makeVarArgs(f);
|
||||||
|
#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
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
bin/*
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Haxe project
|
||||||
|
|
||||||
|
This is an example Haxe project scaffolded by Visual Studio Code.
|
||||||
|
|
||||||
|
Without further changes the structure is following:
|
||||||
|
|
||||||
|
* `src/Main.hx`: Entry point Haxe source file
|
||||||
|
* `build.hxml`: Haxe command line file used to build the project
|
||||||
|
* `README.md`: This file
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Generic Haxe stuffs to make the project compile
|
||||||
|
-cp src
|
||||||
|
-cp ../
|
||||||
|
-D analyzer-optimize
|
||||||
|
-main Main
|
||||||
|
|
||||||
|
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
|
||||||
|
# --library hxcpp-debug-server
|
||||||
|
|
||||||
|
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
|
||||||
|
--define hscriptPos
|
||||||
|
--define HXCPP_CHECK_POINTER
|
||||||
|
--define HXCPP_STACK_LINE
|
||||||
|
--define HXCPP_DEBUG_LINK
|
||||||
|
--dce no
|
||||||
|
|
||||||
|
# File Specific Defines
|
||||||
|
--define linux
|
||||||
|
|
||||||
|
# Copying the assets from source code to bin folder
|
||||||
|
# --cmd rm -rf bin/cpp/assets
|
||||||
|
# --cmd cp -r assets bin/cpp/assets
|
||||||
|
--cpp bin/cpp
|
||||||
|
--cmd cd bin/cpp
|
||||||
|
--cmd ./Main
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Generic Haxe stuffs to make the project compile
|
||||||
|
-cp src
|
||||||
|
-cp ../
|
||||||
|
-D analyzer-optimize
|
||||||
|
-main Main
|
||||||
|
|
||||||
|
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
|
||||||
|
# --library hscript-improved
|
||||||
|
# --library hxcpp-debug-server
|
||||||
|
|
||||||
|
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
|
||||||
|
--define hscriptPos
|
||||||
|
--define HXCPP_CHECK_POINTER
|
||||||
|
--define HXCPP_STACK_LINE
|
||||||
|
--define HXCPP_DEBUG_LINK
|
||||||
|
# -D HXCPP_M64
|
||||||
|
# -D HXCPP_ARM64
|
||||||
|
--dce no
|
||||||
|
|
||||||
|
# File Specific Defines
|
||||||
|
--define macos
|
||||||
|
--define mac
|
||||||
|
|
||||||
|
--macro keep('IntIterator')
|
||||||
|
# --macro hscript.macros.UsingHandler.init()
|
||||||
|
# --macro hscript.macros.ClassExtendMacro.init()
|
||||||
|
|
||||||
|
|
||||||
|
# Copying the assets from source code to bin folder
|
||||||
|
# --cmd rm -rf bin/cpp/assets
|
||||||
|
# --cmd cp -r assets bin/cpp/assets
|
||||||
|
--cpp bin/cpp
|
||||||
|
--cmd cd bin/cpp
|
||||||
|
--cmd ./Main
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package;
|
||||||
|
|
||||||
|
import hscript.Interp;
|
||||||
|
import hscript.Expr;
|
||||||
|
|
||||||
|
@:access(hscript.Interp)
|
||||||
|
@:access(hscript.Parser)
|
||||||
|
class HScriptRunner {
|
||||||
|
public var interp:Interp;
|
||||||
|
public var headerCode:String = "";
|
||||||
|
public var tailCode:String = "";
|
||||||
|
|
||||||
|
public var clearPrevious:Bool = true;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function getNewInterp() {
|
||||||
|
var interp = Util.getInterp();
|
||||||
|
interp.errorHandler = Util.errorHandler;
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setup() {
|
||||||
|
interp = getNewInterp();
|
||||||
|
}
|
||||||
|
public function run() {}
|
||||||
|
public function teardown() {}
|
||||||
|
|
||||||
|
private var lastExpr:Expr;
|
||||||
|
|
||||||
|
public function execute(script:String):Dynamic {
|
||||||
|
var interp = clearPrevious ? getNewInterp() : this.interp;
|
||||||
|
lastExpr = Util.parse(headerCode + script + tailCode);
|
||||||
|
if(lastExpr == null)
|
||||||
|
return "ERROR";
|
||||||
|
if (clearPrevious)
|
||||||
|
return interp.execute(lastExpr);
|
||||||
|
else
|
||||||
|
return interp.exprReturn(lastExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeWithVars(script:String, vars:Dynamic):Dynamic {
|
||||||
|
var interp = clearPrevious ? getNewInterp() : this.interp;
|
||||||
|
lastExpr = Util.parse(headerCode + script + tailCode);
|
||||||
|
if(lastExpr == null)
|
||||||
|
return "ERROR";
|
||||||
|
for(v in Reflect.fields(vars))
|
||||||
|
interp.variables.set(v, Reflect.field(vars, v));
|
||||||
|
if (clearPrevious)
|
||||||
|
return interp.execute(lastExpr);
|
||||||
|
else
|
||||||
|
return interp.exprReturn(lastExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeUnsafe(script:String):Dynamic {
|
||||||
|
var interp = clearPrevious ? getNewInterp() : this.interp;
|
||||||
|
lastExpr = Util.parseUnsafe(headerCode + script + tailCode);
|
||||||
|
if (clearPrevious)
|
||||||
|
return interp.execute(lastExpr);
|
||||||
|
else
|
||||||
|
return interp.exprReturn(lastExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeWithVarsUnsafe(script:String, vars:Dynamic):Dynamic {
|
||||||
|
var interp = clearPrevious ? getNewInterp() : this.interp;
|
||||||
|
lastExpr = Util.parseUnsafe(headerCode + script + tailCode);
|
||||||
|
for(v in Reflect.fields(vars))
|
||||||
|
interp.variables.set(v, Reflect.field(vars, v));
|
||||||
|
if (clearPrevious)
|
||||||
|
return interp.execute(lastExpr);
|
||||||
|
else
|
||||||
|
return interp.exprReturn(lastExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#if BENCHMARK
|
||||||
|
import benchmark.*;
|
||||||
|
#else
|
||||||
|
import tests.*;
|
||||||
|
#end
|
||||||
|
|
||||||
|
class Main {
|
||||||
|
public static var SHOW_KNOWN_BUGS:Bool = false;
|
||||||
|
|
||||||
|
static function main() {
|
||||||
|
#if BENCHMARK
|
||||||
|
Sys.println("Running benchmark");
|
||||||
|
|
||||||
|
//new Benchmark("Test", 1000);
|
||||||
|
var haxeTotalTime:Float = 0;
|
||||||
|
var hscriptTotalTime:Float = 0;
|
||||||
|
|
||||||
|
var haxeTimes:Array<Float> = [];
|
||||||
|
var hscriptTimes:Array<Float> = [];
|
||||||
|
|
||||||
|
var iterations:Int = 10;
|
||||||
|
for (i in 0...iterations) {
|
||||||
|
var benchmark:TestBenchmark = new TestBenchmark();
|
||||||
|
|
||||||
|
haxeTimes.push(benchmark.haxeTotalTime);
|
||||||
|
hscriptTimes.push(benchmark.hscriptTotalTime);
|
||||||
|
|
||||||
|
haxeTotalTime += benchmark.haxeTotalTime;
|
||||||
|
hscriptTotalTime += benchmark.hscriptTotalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
haxeTimes.sort((a, b) -> return a > b ? 1 : -1);
|
||||||
|
hscriptTimes.sort((a, b) -> return a > b ? 1 : -1);
|
||||||
|
|
||||||
|
var slowestHaxeTime:Float = haxeTimes[haxeTimes.length-1];
|
||||||
|
var slowestHscriptTime:Float = hscriptTimes[hscriptTimes.length-1];
|
||||||
|
var fastestHaxeTime:Float = haxeTimes[0];
|
||||||
|
var fastestHscriptTime:Float = hscriptTimes[0];
|
||||||
|
|
||||||
|
var haxeWon = hscriptTotalTime > haxeTotalTime;
|
||||||
|
Sys.println('${haxeWon ? "Haxe" : "Hscript"} was faster overall (Faster by: ${Util.roundWith((haxeWon ? hscriptTotalTime/haxeTotalTime : haxeTotalTime/hscriptTotalTime), 100)}x)');
|
||||||
|
Sys.println('Haxe average time: ${Util.convertToReadableTime(haxeTotalTime/iterations)} (Highest: ${Util.convertToReadableTime(slowestHaxeTime)}) (Lowest: ${Util.convertToReadableTime(fastestHaxeTime)})');
|
||||||
|
Sys.println('Hscript average time: ${Util.convertToReadableTime(hscriptTotalTime/iterations)} (Highest: ${Util.convertToReadableTime(slowestHscriptTime)}) (Lowest: ${Util.convertToReadableTime(fastestHscriptTime)})');
|
||||||
|
#else
|
||||||
|
Sys.println("Beginning tests");
|
||||||
|
if(!Main.SHOW_KNOWN_BUGS) {
|
||||||
|
Sys.println("Hiding known bugs [TEMPORARY]");
|
||||||
|
}
|
||||||
|
runTest("Array", new ArrayCase());
|
||||||
|
runTest("BinOp", new BinOpCase());
|
||||||
|
runTest("Enum", new EnumCase());
|
||||||
|
runTest("EvalOrder", new EvalOrderCase());
|
||||||
|
runTest("Error", new ErrorCase());
|
||||||
|
runTest("Float", new FloatCase());
|
||||||
|
runTest("IntIterator", new IntIteratorCase());
|
||||||
|
runTest("Lambda", new LambdaCase());
|
||||||
|
runTest("List", new ListCase());
|
||||||
|
runTest("Math", new MathCase());
|
||||||
|
runTest("Map", new MapCase());
|
||||||
|
runTest("Misc", new MiscCase());
|
||||||
|
runTest("Reflect", new ReflectCase());
|
||||||
|
runTest("Regex", new RegexCase());
|
||||||
|
runTest("Std", new StdCase());
|
||||||
|
runTest("String", new StringCase());
|
||||||
|
runTest("StringBuf", new StringBufCase());
|
||||||
|
runTest("StringTools", new StringToolsCase());
|
||||||
|
runTest("SwitchStatement", new SwitchCase());
|
||||||
|
// TODO: UnicodeCase.hx?
|
||||||
|
// TODO: UnicodeStringCase.hx?
|
||||||
|
runTest("Final", new FinalCase());
|
||||||
|
Util.printTestResults();
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !BENCHMARK
|
||||||
|
static function runTest(name:String, test:TestCase) {
|
||||||
|
Util.startUnitTest(name);
|
||||||
|
test.setup();
|
||||||
|
test.run();
|
||||||
|
test.teardown();
|
||||||
|
Util.endUnitTest();
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
}
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
import hscript.Expr.Error;
|
||||||
|
import hscript.Printer;
|
||||||
|
import haxe.Constraints.IMap;
|
||||||
|
import haxe.EnumTools.EnumValueTools;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
class Util {
|
||||||
|
static inline function isNull(a:Dynamic):Bool {
|
||||||
|
return Type.enumEq(Type.typeof(a), TNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline function isFunction(a:Dynamic):Bool {
|
||||||
|
return Type.enumEq(Type.typeof(a), TFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check this for bugs
|
||||||
|
// Code from https://github.com/elnabo/equals/blob/master/src/equals/Equal.hx, (MIT License), but updated to work with haxe 4
|
||||||
|
public static function deepEqual<T> (a:T, b:T) : Bool {
|
||||||
|
if (a == b) { return true; } // if physical equality
|
||||||
|
if (isNull(a) || isNull(b)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Type.typeof(a)) {
|
||||||
|
case TNull, TInt, TBool, TUnknown:
|
||||||
|
return a == b;
|
||||||
|
case TFloat:
|
||||||
|
return Math.isNaN(cast a) && Math.isNaN(cast b); // only valid true result remaining
|
||||||
|
case TFunction:
|
||||||
|
return Reflect.compareMethods(a, b); // only physical equality can be tested for function
|
||||||
|
case TEnum(_):
|
||||||
|
if (EnumValueTools.getIndex(cast a) != EnumValueTools.getIndex(cast b)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var a_args = EnumValueTools.getParameters(cast a);
|
||||||
|
var b_args = EnumValueTools.getParameters(cast b);
|
||||||
|
return deepEqual(a_args, b_args);
|
||||||
|
case TClass(_):
|
||||||
|
if ((a is String) && (b is String)) {
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
if ((a is Array) && (b is Array)) {
|
||||||
|
var a = cast(a, Array<Dynamic>);
|
||||||
|
var b = cast(b, Array<Dynamic>);
|
||||||
|
if (a.length != b.length) { return false; }
|
||||||
|
for (i in 0...a.length) {
|
||||||
|
if (!deepEqual(a[i], b[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((a is IMap) && (b is IMap)) {
|
||||||
|
var a = cast(a, IMap<Dynamic, Dynamic>);
|
||||||
|
var b = cast(b, IMap<Dynamic, Dynamic>);
|
||||||
|
var a_keys = [ for (key in a.keys()) key ];
|
||||||
|
var b_keys = [ for (key in b.keys()) key ];
|
||||||
|
a_keys.sort(Reflect.compare);
|
||||||
|
b_keys.sort(Reflect.compare);
|
||||||
|
if (!deepEqual(a_keys, b_keys)) { return false; }
|
||||||
|
for (key in a_keys) {
|
||||||
|
if (!deepEqual(a.get(key), b.get(key))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((a is Date) && (b is Date)) {
|
||||||
|
return cast(a, Date).getTime() == cast(b, Date).getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((a is haxe.io.Bytes) && (b is haxe.io.Bytes)) {
|
||||||
|
return deepEqual(cast(a, haxe.io.Bytes).getData(), cast(b, haxe.io.Bytes).getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
case TObject:
|
||||||
|
}
|
||||||
|
|
||||||
|
for (field in Reflect.fields(a)) {
|
||||||
|
var pa = Reflect.field(a, field);
|
||||||
|
var pb = Reflect.field(b, field);
|
||||||
|
if (isFunction(pa)) {
|
||||||
|
// ignore function as only physical equality can be tested, unless null
|
||||||
|
if (isNull(pa) != isNull(pb)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!deepEqual(pa, pb)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function passed() {
|
||||||
|
passedTestUnits++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function failed() {
|
||||||
|
failedTestUnits++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function assert(value:Bool, message:String, ?pos:haxe.PosInfos) {
|
||||||
|
if (value) {
|
||||||
|
return passed();
|
||||||
|
} else {
|
||||||
|
Sys.println("Assertion failed: " + message);
|
||||||
|
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||||
|
return failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function assertEq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
|
||||||
|
var equals = deepEqual(value, expected);
|
||||||
|
var _passed = equals;
|
||||||
|
|
||||||
|
if (_passed) {
|
||||||
|
return passed();
|
||||||
|
} else {
|
||||||
|
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
|
||||||
|
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||||
|
return failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function assertEqPrintable(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
|
||||||
|
var equals = deepEqual(value, expected);
|
||||||
|
var _passed = equals;
|
||||||
|
|
||||||
|
if (_passed) {
|
||||||
|
return passed();
|
||||||
|
} else {
|
||||||
|
Sys.println("Assertion failed: " + message + " Expected: " + Printer.getEscapedString(expected) + " Got: " + Printer.getEscapedString(value));
|
||||||
|
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||||
|
return failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function assertNeq(value:Dynamic, expected:Dynamic, message:String, ?pos:haxe.PosInfos) {
|
||||||
|
var equals = deepEqual(value, expected);
|
||||||
|
var _passed = !equals;
|
||||||
|
|
||||||
|
// WARNING THIS MIGHT NOT WORK
|
||||||
|
|
||||||
|
if (_passed) {
|
||||||
|
return passed();
|
||||||
|
} else {
|
||||||
|
Sys.println("Assertion failed: " + message + " Expected: " + expected + " Got: " + value);
|
||||||
|
Sys.println("> At " + pos.fileName + ":" + pos.lineNumber);
|
||||||
|
return failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var currentTestPath:Array<String> = [];
|
||||||
|
static var totalTestUnits:Int = 0;
|
||||||
|
static var passedTestUnits:Int = 0;
|
||||||
|
static var failedTestUnits:Int = 0;
|
||||||
|
static var skippedKnownBugs:Int = 0;
|
||||||
|
|
||||||
|
public static function runKnownBug(name:String, func:Void->Void) {
|
||||||
|
if (Main.SHOW_KNOWN_BUGS) {
|
||||||
|
Sys.println("Running known bug: " + name);
|
||||||
|
func();
|
||||||
|
} else {
|
||||||
|
skippedKnownBugs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function startUnitTest(path:String) {
|
||||||
|
Sys.println("Running unit tests in " + path);
|
||||||
|
currentTestPath.push(path);
|
||||||
|
totalTestUnits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function endUnitTest() {
|
||||||
|
var path = currentTestPath.pop();
|
||||||
|
//Sys.println("Finished unit tests " + path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function printTestResults() {
|
||||||
|
Sys.println(Util.getTitle('RESULTS'));
|
||||||
|
Sys.println("Total Units: " + totalTestUnits);
|
||||||
|
Sys.println("Total tests passed: " + passedTestUnits);
|
||||||
|
Sys.println("Total tests failed: " + failedTestUnits);
|
||||||
|
if(skippedKnownBugs > 0)
|
||||||
|
Sys.println("Skipped known bugs: " + skippedKnownBugs);
|
||||||
|
//Sys.println("Finished unit tests " + currentTestPath.pop());
|
||||||
|
Sys.println(Util.getTitle('RESULTS'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function parse(str:String) {
|
||||||
|
var p = new Parser();
|
||||||
|
p.allowTypes = true;
|
||||||
|
p.allowMetadata = true;
|
||||||
|
p.allowJSON = true;
|
||||||
|
var result = null;
|
||||||
|
try {
|
||||||
|
result = p.parseString(str);
|
||||||
|
} catch (e:Dynamic) {
|
||||||
|
Sys.println("## Error parsing: " + str);
|
||||||
|
Sys.println("## Error: " + e);
|
||||||
|
// Print stack trace
|
||||||
|
var stack = haxe.CallStack.toString(haxe.CallStack.exceptionStack());
|
||||||
|
Sys.println("## Stack trace: " + stack);
|
||||||
|
Sys.exit(1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a string without checking for errors
|
||||||
|
*/
|
||||||
|
public static function parseUnsafe(str:String) {
|
||||||
|
var p = new Parser();
|
||||||
|
p.allowTypes = true;
|
||||||
|
p.allowMetadata = true;
|
||||||
|
p.allowJSON = true;
|
||||||
|
return p.parseString(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var defaultImport = true;
|
||||||
|
|
||||||
|
public static function getInterp() {
|
||||||
|
var interp = new Interp();
|
||||||
|
//interp.importEnabled = true;
|
||||||
|
//interp.allowStaticVariables = true;
|
||||||
|
//interp.allowPublicVariables = true;
|
||||||
|
if(defaultImport) {
|
||||||
|
interp.variables.set("Math", Math);
|
||||||
|
interp.variables.set("Std", Std);
|
||||||
|
interp.variables.set("Type", Type);
|
||||||
|
interp.variables.set("StringTools", StringTools);
|
||||||
|
}
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function getTime():Float {
|
||||||
|
return untyped __global__.__time_stamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// expandScientificNotation but its WAY too long to write out
|
||||||
|
public static function exScienceN(value:Float):String {
|
||||||
|
var parts = Std.string(value).split("e");
|
||||||
|
var coefficient = Std.parseFloat(parts[0]);
|
||||||
|
var exponent = parts.length > 1 ? Std.parseInt(parts[1]) : 0;
|
||||||
|
var result = "";
|
||||||
|
|
||||||
|
if (exponent > 0) {
|
||||||
|
result += StringTools.replace(Std.string(coefficient), ".", "");
|
||||||
|
var decimalLength = Std.string(coefficient).split(".")[1].length;
|
||||||
|
var additionalZeros:Int = Std.int(Math.abs(exponent - decimalLength));
|
||||||
|
result += StringTools.lpad("", "0", additionalZeros); // repeat
|
||||||
|
} else {
|
||||||
|
result += "0.";
|
||||||
|
var leadingZeros:Int = Std.int(Math.abs(exponent) - 1);
|
||||||
|
result += StringTools.lpad("", "0", leadingZeros); // repeat
|
||||||
|
result += StringTools.replace(Std.string(coefficient), ".", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function convertToReadableTime(seconds:Float) {
|
||||||
|
if (seconds >= 1) return seconds + " s";
|
||||||
|
var milliseconds = seconds * 1000; // 1 second = 1,000 ms
|
||||||
|
if (milliseconds >= 1) return milliseconds + " ms";
|
||||||
|
var microseconds = seconds * 1000000; // 1 second = 1,000,000 μs
|
||||||
|
if (microseconds >= 1) return microseconds + " μs";
|
||||||
|
var nanoseconds = seconds * 1000000000; // 1 second = 1,000,000,000 ns
|
||||||
|
return nanoseconds + " ns";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function errorHandler(error:Error) {
|
||||||
|
#if hscriptPos
|
||||||
|
var fileName = error.origin;
|
||||||
|
var fn = '$fileName:${error.line}: ';
|
||||||
|
#else
|
||||||
|
var fn = '';
|
||||||
|
#end
|
||||||
|
var err = Std.string(error);
|
||||||
|
if (err.startsWith(fn)) err = err.substr(fn.length);
|
||||||
|
|
||||||
|
Sys.println("ERROR: " + fn + err);
|
||||||
|
//Logs.traceColored([
|
||||||
|
// Logs.logText(fn, GREEN),
|
||||||
|
// Logs.logText(err, RED)
|
||||||
|
//], ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function deepCompareArrays(a1: Array<Dynamic>, a2: Array<Dynamic>): Bool {
|
||||||
|
if (a1.length != a2.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (i in 0...a1.length) {
|
||||||
|
var item1 = a1[i];
|
||||||
|
var item2 = a2[i];
|
||||||
|
|
||||||
|
if (Std.isOfType(item1, Array) && Std.isOfType(item2, Array)) {
|
||||||
|
if (!deepCompareArrays(item1, item2))
|
||||||
|
return false;
|
||||||
|
} else if (item1 != item2)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function roundDecimal(Value:Float, Precision:Int):Float {
|
||||||
|
var mult:Float = 1;
|
||||||
|
for (i in 0...Precision)
|
||||||
|
mult *= 10;
|
||||||
|
return Math.fround(Value * mult) / mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline static function roundWith(Value:Float, Mult:Int):Float {
|
||||||
|
return Math.fround(Value * Mult) / Mult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getTitle(title:String, ?dashsLen:Int = 46) {
|
||||||
|
var l = StringTools.lpad("", "-", Std.int((dashsLen - title.length - 2)/2));
|
||||||
|
return l + ' $title ' + l;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package benchmark;
|
||||||
|
|
||||||
|
import hscript.Expr;
|
||||||
|
|
||||||
|
class Benchmark extends HScriptRunner {
|
||||||
|
public var haxeTimes:Array<Float> = [];
|
||||||
|
public var haxeTotalTime:Float = 0;
|
||||||
|
|
||||||
|
public var hscriptTimes:Array<Float> = [];
|
||||||
|
public var hscriptTotalTime:Float = 0;
|
||||||
|
|
||||||
|
public function new(name:String, iterations:Int) {
|
||||||
|
super();
|
||||||
|
doBenchmark(name, iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doBenchmark(name:String, iterations:Int) {
|
||||||
|
haxeTimes = cast new haxe.ds.Vector<Float>(iterations);
|
||||||
|
hscriptTimes = cast new haxe.ds.Vector<Float>(iterations);
|
||||||
|
|
||||||
|
for (i in 0...iterations) {
|
||||||
|
reset();
|
||||||
|
var haxeStartTime:Float = Util.getTime();
|
||||||
|
haxeBenchmark();
|
||||||
|
var haxeEndTime:Float = Util.getTime();
|
||||||
|
haxeTimes[i] = haxeEndTime-haxeStartTime;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
var hscriptStartTime:Float = Util.getTime();
|
||||||
|
hscriptBenchmark();
|
||||||
|
var hscriptEndTime:Float = Util.getTime();
|
||||||
|
hscriptTimes[i] = hscriptEndTime-hscriptStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
haxeTimes.sort(Reflect.compare);
|
||||||
|
hscriptTimes.sort(Reflect.compare);
|
||||||
|
|
||||||
|
for (time in haxeTimes) haxeTotalTime += time;
|
||||||
|
for (time in hscriptTimes) hscriptTotalTime += time;
|
||||||
|
|
||||||
|
Sys.println(Util.getTitle('$name BENCHMARK'));
|
||||||
|
|
||||||
|
if ((haxeTotalTime == 0 || hscriptTotalTime == 0) || (haxeTimes.length <= 0 || hscriptTimes.length <= 0)) {
|
||||||
|
Sys.println('BENCHMARK UNNOTICABLE, TOOK 0 SECONDS');
|
||||||
|
} else {
|
||||||
|
var haxeWon = hscriptTotalTime > haxeTotalTime;
|
||||||
|
Sys.println('${haxeWon ? "Haxe" : "Hscript"} was faster overall (Faster by: ${Util.roundWith((haxeWon ? hscriptTotalTime/haxeTotalTime : haxeTotalTime/hscriptTotalTime), 100)}x)');
|
||||||
|
Sys.println('Haxe average time: ${Util.convertToReadableTime(haxeTotalTime/iterations)} (Highest: ${haxeTimes[haxeTimes.length-1]})');
|
||||||
|
Sys.println('Hscript average time: ${Util.convertToReadableTime(hscriptTotalTime/iterations)} (Highest: ${hscriptTimes[haxeTimes.length-1]})');
|
||||||
|
}
|
||||||
|
|
||||||
|
Sys.println(Util.getTitle('$name BENCHMARK'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset() {}
|
||||||
|
|
||||||
|
public var expr:Expr;
|
||||||
|
|
||||||
|
public inline function cacheExpr(script:String) {
|
||||||
|
interp = getNewInterp();
|
||||||
|
interp.variables.set("self", this);
|
||||||
|
interp.scriptObject = this;
|
||||||
|
expr = Util.parse(headerCode + script + tailCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function execute(script:String):Dynamic {
|
||||||
|
if (expr == null)
|
||||||
|
cacheExpr(script);
|
||||||
|
return interp.execute(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function haxeBenchmark() {}
|
||||||
|
public function hscriptBenchmark() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package benchmark;
|
||||||
|
|
||||||
|
@:dce(off)
|
||||||
|
class TestBenchmark extends Benchmark {
|
||||||
|
public function new() {
|
||||||
|
super("Test", 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//var hscript = "var a:Array<Float> = []; for (i in 0...1000) a.push(i * 2 + 1 / 6); if(true == true) a.push(1);";
|
||||||
|
var hscript = "
|
||||||
|
var a:Array<Float> = [];
|
||||||
|
for(i in 0...1000) {
|
||||||
|
switch(5) {
|
||||||
|
case true: a.push(1);
|
||||||
|
default: a.push(i / 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(('a' == 'a') ? true : false) a.push(1);
|
||||||
|
if(true == true)
|
||||||
|
a.push(1);";
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aaa = null;
|
||||||
|
|
||||||
|
public override function reset() {
|
||||||
|
super.reset();
|
||||||
|
aaa = {
|
||||||
|
test: test
|
||||||
|
};
|
||||||
|
if(expr == null)
|
||||||
|
cacheExpr(hscript);
|
||||||
|
interp.variables.remove("a");
|
||||||
|
//interp.variables.set("aaa", aaa);
|
||||||
|
interp.variables.set("test", test);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function haxeBenchmark() {
|
||||||
|
var a:Array<Float> = [];
|
||||||
|
//for (i in 0...1000) a.push(i * 2 + 1 / 6);
|
||||||
|
//for (i in 0...1000) a.push(i / 2 + 3 / 6);
|
||||||
|
for(i in 0...1000) {
|
||||||
|
switch(5) {
|
||||||
|
case 0: a.push(1);
|
||||||
|
default: a.push(i / 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(('a' == 'a') ? true : false) a.push(1);
|
||||||
|
if(true == true) a.push(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function hscriptBenchmark() {
|
||||||
|
interp.execute(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import hscript.Interp;
|
||||||
|
import hscript.Parser;
|
||||||
|
|
||||||
|
import Util;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class ArrayCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
assertEq("[].length", 0);
|
||||||
|
assertEq("[1].length", 1);
|
||||||
|
assertEq("[1,2,3].length", 3);
|
||||||
|
assertEq("var a = [];\na[4] = 1;\na.length", 5);
|
||||||
|
assertEq("var a = [];\na[4] = 1;\na[a.length] = 1;\na.length", 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class BinOpCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
headerCode = "
|
||||||
|
var a = 1;
|
||||||
|
var b = 2;
|
||||||
|
var c = 3;
|
||||||
|
";
|
||||||
|
|
||||||
|
//tailCode = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var a = 1;
|
||||||
|
var b = 2;
|
||||||
|
var c = 3;
|
||||||
|
|
||||||
|
assertEq("a + b", a + b);
|
||||||
|
assertEq("a - b", a - b);
|
||||||
|
assertEq("a * b", a * b);
|
||||||
|
assertEq("a / b", a / b);
|
||||||
|
assertEq("a % b", a % b);
|
||||||
|
assertEq("a & b", a & b);
|
||||||
|
assertEq("a | b", a | b);
|
||||||
|
assertEq("a ^ b", a ^ b);
|
||||||
|
assertEq("5 ^ 3", 5 ^ 3);
|
||||||
|
assertEq("a << b", a << b);
|
||||||
|
assertEq("a >> b", a >> b);
|
||||||
|
assertEq("a >>> b", a >>> b);
|
||||||
|
assertEq("a == b", a == b);
|
||||||
|
assertEq("a != b", a != b);
|
||||||
|
assertEq("a >= b", a >= b);
|
||||||
|
assertEq("a <= b", a <= b);
|
||||||
|
assertEq("a > b", a > b);
|
||||||
|
assertEq("a < b", a < b);
|
||||||
|
//assertEq("a || b", a || b);
|
||||||
|
//assertEq("a && b", a && b);
|
||||||
|
//assertEq("a is b", false);
|
||||||
|
|
||||||
|
// Order of operations
|
||||||
|
assertEq("a + b * c", a + b * c);
|
||||||
|
assertEq("a * b + c", a * b + c);
|
||||||
|
assertEq("a * (b + c)", a * (b + c));
|
||||||
|
assertEq("(a + b) * c", (a + b) * c);
|
||||||
|
assertEq("a / b / c", a / b / c);
|
||||||
|
assertEq("a % b % c", a % b % c);
|
||||||
|
assertEq("a & b & c", a & b & c);
|
||||||
|
assertEq("a | b | c", a | b | c);
|
||||||
|
assertEq("a ^ b ^ c", a ^ b ^ c);
|
||||||
|
assertEq("a << b << c", a << b << c);
|
||||||
|
assertEq("a >> b >> c", a >> b >> c);
|
||||||
|
assertEq("a >>> b >>> c", a >>> b >>> c);
|
||||||
|
|
||||||
|
// Precedence
|
||||||
|
assertEq("a + b + c", a + b + c);
|
||||||
|
assertEq("a + b - c", a + b - c);
|
||||||
|
assertEq("a * b / c", a * b / c);
|
||||||
|
assertEq("a / b * c", a / b * c);
|
||||||
|
assertEq("a % b | c", a % b | c);
|
||||||
|
assertEq("a & b ^ c", a & b ^ c);
|
||||||
|
assertEq("a << b >> c", a << b >> c);
|
||||||
|
assertEq("a >>> b << c", a >>> b << c);
|
||||||
|
|
||||||
|
|
||||||
|
headerCode = "
|
||||||
|
var a = null;
|
||||||
|
var b = 'hello';
|
||||||
|
var c = 'world';
|
||||||
|
var d = '!';
|
||||||
|
";
|
||||||
|
|
||||||
|
var a = null;
|
||||||
|
var b = 'hello';
|
||||||
|
var c = 'world';
|
||||||
|
var d = '!';
|
||||||
|
|
||||||
|
assertEq("a + b", a + b);
|
||||||
|
|
||||||
|
#if (haxe >= "4.3.0")
|
||||||
|
assertEq("a ?? b", a ?? b);
|
||||||
|
assertEq("a ?? b ?? c", a ?? b ?? c);
|
||||||
|
assertEq("a ?? b ?? c ?? d", a ?? b ?? c ?? d);
|
||||||
|
#else
|
||||||
|
Sys.println("Tests are inaccurate with Haxe 4.2.x");
|
||||||
|
assertEq("a ?? b", nc(a, b));
|
||||||
|
assertEq("a ?? b ?? c", nc(a, nc(b, c)));
|
||||||
|
assertEq("a ?? b ?? c ?? d", nc(a, nc(b, nc(c, d))));
|
||||||
|
#end
|
||||||
|
|
||||||
|
var tests:Array<Array<Dynamic>> = [
|
||||||
|
[null, "null"],
|
||||||
|
[{}, "{}"],
|
||||||
|
[{a: 1}, "{a: 1}"],
|
||||||
|
[{a: {b: 1}}, "{a: {b: 1}}"],
|
||||||
|
[{a: {b: {c: 1}}}, "{a: {b: {c: 1}}}"],
|
||||||
|
[{a: {b: {c: {d: 1}}}}, "{a: {b: {c: {d: 1}}}}"]
|
||||||
|
];
|
||||||
|
|
||||||
|
for(code in tests) {
|
||||||
|
headerCode = 'var a = ${code[1]};';
|
||||||
|
var a = code[0];
|
||||||
|
|
||||||
|
#if (haxe >= "4.3.0")
|
||||||
|
assertEq("a?.b", a?.b);
|
||||||
|
assertEq("a?.b?.c", a?.b?.c);
|
||||||
|
assertEq("a?.b?.c?.d", a?.b?.c?.d);
|
||||||
|
#else
|
||||||
|
assertEq("a?.b", nf(a, b));
|
||||||
|
assertEq("a?.b?.c", nf(nf(a, b), c));
|
||||||
|
assertEq("a?.b?.c?.d", nf(nf(nf(a, b), c), d));
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
headerCode = "
|
||||||
|
var a = null;
|
||||||
|
var b = 'hello';
|
||||||
|
";
|
||||||
|
|
||||||
|
var a = null;
|
||||||
|
var b = 'hello';
|
||||||
|
|
||||||
|
#if (haxe >= "4.3.0")
|
||||||
|
a ??= b;
|
||||||
|
assertEq("a ??"+"= b; a", a);
|
||||||
|
#else
|
||||||
|
if(a == null) a = b;
|
||||||
|
assertEq("a ??"+"= b; a", a);
|
||||||
|
#end
|
||||||
|
|
||||||
|
headerCode = "
|
||||||
|
var a = 'test';
|
||||||
|
var b = 'hello';
|
||||||
|
";
|
||||||
|
|
||||||
|
var a = 'test';
|
||||||
|
var b = 'hello';
|
||||||
|
|
||||||
|
#if (haxe >= "4.3.0")
|
||||||
|
a ??= b;
|
||||||
|
assertEq("a ??"+"= b; a", a);
|
||||||
|
#else
|
||||||
|
if(a == null) a = b;
|
||||||
|
assertEq("a ??"+"= b; a", a);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function nc(a:Dynamic, b:Dynamic):Dynamic {
|
||||||
|
return (a == null) ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline function nf(a:Dynamic, b:String):Dynamic {
|
||||||
|
return (a == null) ? null : Reflect.field(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class EnumCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
var importEnabled = true;
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("assertEq", Util.assertEq);
|
||||||
|
if(importEnabled)
|
||||||
|
interp.variables.set("SomeEnum", SomeEnum);
|
||||||
|
interp.variables.set("Type", Type);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
|
||||||
|
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
|
||||||
|
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
|
||||||
|
|
||||||
|
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
|
||||||
|
|
||||||
|
importEnabled = false;
|
||||||
|
headerCode = "import tests.EnumCase.SomeEnum;";
|
||||||
|
|
||||||
|
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
|
||||||
|
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
|
||||||
|
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
|
||||||
|
|
||||||
|
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
|
||||||
|
|
||||||
|
headerCode = "import tests.SomeEnum;";
|
||||||
|
|
||||||
|
assertEq("SomeEnum.NoArguments;", SomeEnum.NoArguments);
|
||||||
|
assertEq("SomeEnum.OneArgument('foo');", SomeEnum.OneArgument("foo"));
|
||||||
|
assertEq("SomeEnum.TwoArguments('foo', 'bar');", SomeEnum.TwoArguments("foo", "bar"));
|
||||||
|
|
||||||
|
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments);", Type.enumEq(SomeEnum.NoArguments, SomeEnum.NoArguments));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.NoArguments, SomeEnum.OneArgument("foo")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.OneArgument('foo'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.OneArgument("foo")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.OneArgument('foo'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.OneArgument("foo"), SomeEnum.TwoArguments("foo", "bar")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.TwoArguments('foo', 'bar'));", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.TwoArguments("foo", "bar")));
|
||||||
|
assertEq("Type.enumEq(SomeEnum.TwoArguments('foo', 'bar'), SomeEnum.NoArguments);", Type.enumEq(SomeEnum.TwoArguments("foo", "bar"), SomeEnum.NoArguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SomeEnum {
|
||||||
|
NoArguments;
|
||||||
|
OneArgument(arg:String);
|
||||||
|
TwoArguments(arg1:String, arg2:String);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class ErrorCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
Sys.println("TODO: write tests for errors");
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class EvalOrderCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
headerCode = '
|
||||||
|
function func(i1:Dynamic, i2:Dynamic, i3:Dynamic) {
|
||||||
|
return i1 + ";" + i2 + ";" + i3;
|
||||||
|
}
|
||||||
|
';
|
||||||
|
|
||||||
|
//tailCode = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var a = 1;
|
||||||
|
var b = 2;
|
||||||
|
var c = 3;
|
||||||
|
|
||||||
|
function func(i1:Dynamic, i2:Dynamic, i3:Dynamic) {
|
||||||
|
return i1 + ";" + i2 + ";" + i3;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearPrevious = true;
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
assertEq("var i = 0; func(i++, i++, i++)", func(i++, i++, i++));
|
||||||
|
var i = 0;
|
||||||
|
assertEq("var i = 0; [i++, i++, i++].join(';')", [i++, i++, i++].join(";"));
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
var obj = {
|
||||||
|
a: i++,
|
||||||
|
b: i++,
|
||||||
|
c: i++
|
||||||
|
}
|
||||||
|
assertEq("var i = 0; var obj = {
|
||||||
|
a: i++,
|
||||||
|
b: i++,
|
||||||
|
c: i++
|
||||||
|
}; func(obj.a, obj.b, obj.c)", func(obj.a, obj.b, obj.c));
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
assertEq("var i = 0; func(i++, [i++, i++].join(';'), i++)", func(i++, [i++, i++].join(";"), i++));
|
||||||
|
|
||||||
|
clearPrevious = true;
|
||||||
|
|
||||||
|
headerCode += '
|
||||||
|
var buf:Array<Int> = [];
|
||||||
|
|
||||||
|
function a() {
|
||||||
|
buf.push(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function b() {
|
||||||
|
buf.push(2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function c() {
|
||||||
|
buf.push(3);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function d() {
|
||||||
|
buf.push(4);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function e() {
|
||||||
|
buf.push(5);
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
buf.push(6);
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
function begin() {
|
||||||
|
buf = [];
|
||||||
|
return function() {
|
||||||
|
return buf.join("_");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
';
|
||||||
|
|
||||||
|
var buf:Array<Int> = [];
|
||||||
|
|
||||||
|
function a() {
|
||||||
|
buf.push(1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function b() {
|
||||||
|
buf.push(2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function c() {
|
||||||
|
buf.push(3);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
function d() {
|
||||||
|
buf.push(4);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function e() {
|
||||||
|
buf.push(5);
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
buf.push(6);
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
function begin() {
|
||||||
|
buf = [];
|
||||||
|
return function() {
|
||||||
|
return buf.join("_");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// &&
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 0 && (c() + d()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 0 && (c() + d()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 99 && (c() + d()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 99 && (c() + d()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 0 && (c() + d()) >= 0 && (e() + f()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 0 && (c() + d()) >= 0 && (e() + f()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 99 && (c() + d()) >= 0 && (e() + f()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 99 && (c() + d()) >= 0 && (e() + f()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 0 && (c() + d()) >= 99 && (e() + f()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 0 && (c() + d()) >= 99 && (e() + f()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
// ||
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 0 || (c() + d()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 0 || (c() + d()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 99 || (c() + d()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 99 || (c() + d()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 0 || (c() + d()) >= 0 || (e() + f()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 0 || (c() + d()) >= 0 || (e() + f()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 99 || (c() + d()) >= 0 || (e() + f()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 99 || (c() + d()) >= 0 || (e() + f()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
(a() + b()) >= 99 || (c() + d()) >= 99 || (e() + f()) >= 0;
|
||||||
|
assertEq('var end = begin();
|
||||||
|
(a() + b()) >= 99 || (c() + d()) >= 99 || (e() + f()) >= 0;
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
headerCode += '
|
||||||
|
function arr(x, y) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function idx(x, y) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f1() {
|
||||||
|
buf.push(1);
|
||||||
|
return function(i) { };
|
||||||
|
}
|
||||||
|
|
||||||
|
function f2() {
|
||||||
|
buf.push(2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
';
|
||||||
|
// []
|
||||||
|
|
||||||
|
function arr(x, y) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function idx(x, y) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f1() {
|
||||||
|
buf.push(1);
|
||||||
|
return function(i) { };
|
||||||
|
}
|
||||||
|
|
||||||
|
function f2() {
|
||||||
|
buf.push(2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = begin();
|
||||||
|
var _ = (arr(a(), b()))[idx(c(), d())];
|
||||||
|
assertEq('var end = begin();
|
||||||
|
var _ = (arr(a(), b()))[idx(c(), d())];
|
||||||
|
end()', end());
|
||||||
|
|
||||||
|
headerCode += '
|
||||||
|
var d:Dynamic = { f1: f1 };
|
||||||
|
|
||||||
|
function f3() {
|
||||||
|
buf.push(3);
|
||||||
|
d.f1 = function f3df1(i) {
|
||||||
|
buf.push(4);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
';
|
||||||
|
|
||||||
|
var d:Dynamic = { f1: f1 };
|
||||||
|
|
||||||
|
function f3() {
|
||||||
|
buf.push(3);
|
||||||
|
d.f1 = function(i) {
|
||||||
|
buf.push(4);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.runKnownBug("Function arguments get called first, before function is evaluated", () ->{
|
||||||
|
var end = begin();
|
||||||
|
d.f1()(f3());
|
||||||
|
d.f1(f2());
|
||||||
|
assertEq('var end = begin();
|
||||||
|
// Expected internal behavior for `d.f1()(f3());`
|
||||||
|
//var func = d.f1();
|
||||||
|
//func(f3());
|
||||||
|
|
||||||
|
d.f1()(f3());
|
||||||
|
d.f1(f2());
|
||||||
|
end()', end());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class FinalCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
Util.defaultImport = false;
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
Util.defaultImport = true;
|
||||||
|
//interp.variables.set("Std", Std);
|
||||||
|
//interp.variables.set("String", String);
|
||||||
|
//interp.variables.set("Bool", Bool);
|
||||||
|
//interp.variables.set("Float", Float);
|
||||||
|
//interp.variables.set("Array", Array);
|
||||||
|
//interp.variables.set("Int", Int);
|
||||||
|
//interp.variables.set("IntIterator", IntIterator);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
headerCode = "var i = 5; var x = 10;";
|
||||||
|
|
||||||
|
var i = 5; var x = 10;
|
||||||
|
|
||||||
|
assertDisplay("(i - x) * 30 + 90", ((i-x) * 30) + 90);
|
||||||
|
assertDisplay("i - x * 30 + 90", i-x * 30 + 90);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Test operator precedence
|
||||||
|
assertDisplay("i / x * 30 + 90", i / x * 30 + 90);
|
||||||
|
assertDisplay("i / (x * 30) + 90", i / (x * 30) + 90);
|
||||||
|
|
||||||
|
assertDisplay("x * 30 / i + 90", x * 30 / i + 90);
|
||||||
|
//assertEq("(x * 30) / i + 90", x * 30 / i + 90);
|
||||||
|
|
||||||
|
// (_) -> {a = false;}
|
||||||
|
// Shouldnt be converted to (_) -> return {a = false;}
|
||||||
|
|
||||||
|
Util.parse("openCodesList(false, codesOpened ? false : true, previousOpen != open ? true : false);");
|
||||||
|
Util.parse("openCodesList(false, 'HELLO' ? false : false, 'WORLD' ? true : true);");
|
||||||
|
Util.parse('FlxTween.tween(newSprite, {"scale.x": 1, "scale.y": 1, alpha: 1, angle: 0}, 0.3, {ease: FlxEase.qaudInOut});');
|
||||||
|
Util.parse('
|
||||||
|
bloomShader.dim = dim = .8 + (.3 * FlxMath.fastSin(__totalTime));
|
||||||
|
bloomShader.size = size = 18 + (8 * FlxMath.fastSin(__totalTime));
|
||||||
|
');
|
||||||
|
*/
|
||||||
|
|
||||||
|
Util.parse('sprite.setPosition(codesList.x + (sprite.ID % 2 == 1 ? 240 : 67),codesList.y + (15 * sprite.ID) + (sprite.ID%2 == 1 ? 35 : 54));');
|
||||||
|
|
||||||
|
headerCode = "import tests.FinalCase.TestEnum;";
|
||||||
|
|
||||||
|
assertEq("TestEnum.A", TestEnum.A);
|
||||||
|
assertEq("TestEnum.B", TestEnum.B);
|
||||||
|
assertEq("TestEnum.C(1, 2)", TestEnum.C(1, 2));
|
||||||
|
assertEq("TestEnum.D(1, 2, 3)", TestEnum.D(1, 2, 3));
|
||||||
|
|
||||||
|
Util.parse('test ? 0xFF343434 : 0xFF92A2FF');
|
||||||
|
Util.parse('test ? 1 : 0.75;');
|
||||||
|
|
||||||
|
Util.runKnownBug("Function then array causes error, unless the function ends with ;", function() {
|
||||||
|
headerCode = "";
|
||||||
|
function isEven(i:Int):Bool { return i % 2 == 0; }
|
||||||
|
assertEq("function isEven(i:Int):Bool { return i % 2 == 0; }[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertEq("[for(i in 0...10) i]", [for(i in 0...10) i]);
|
||||||
|
assertEq("[for(i in 0...10) i => null]", [for(i in 0...10) i => null]);
|
||||||
|
assertEq("[for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']", [for(i in 0...10) if(i % 2 == 0) i => 'even' else i => 'odd']);
|
||||||
|
assertEq("[for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']", [for(i in 0...10) if(i % 2 == 0) 'even' else 'odd']);
|
||||||
|
assertEq("[for(i in 0...10) for(j in 0...10) i * j]", [for(i in 0...10) for(j in 0...10) i * j]);
|
||||||
|
|
||||||
|
headerCode = "function isEven(i:Int):Bool { return i % 2 == 0; };";
|
||||||
|
function isEven(i:Int):Bool { return i % 2 == 0; }
|
||||||
|
|
||||||
|
assertEq("[for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]", [for(i in 0...10) if(i % 2 == 0) i => isEven(i) else i => isEven(i)]);
|
||||||
|
assertEq("[for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]", [for(i in 0...10) if(i % 2 == 0) isEven(i) else isEven(i)]);
|
||||||
|
headerCode = "function area(a:Int, b:Int):Int { return a * b; };";
|
||||||
|
function area(a:Int, b:Int):Int { return a * b; }
|
||||||
|
|
||||||
|
assertEq("[for(i in 0...10) for(j in 0...10) area(i, j)]", [for(i in 0...10) for(j in 0...10) area(i, j)]);
|
||||||
|
|
||||||
|
|
||||||
|
headerCode = "";
|
||||||
|
|
||||||
|
assertEq("[0=>'hello', 1=>'world']", [0=>'hello', 1=>'world']);
|
||||||
|
assertEq("[0=>'hello', 1=>'world'][1]", [0=>'hello', 1=>'world'][1]);
|
||||||
|
assertEq("[0=>'hello', 5=>'world'][1]", [0=>'hello', 5=>'world'][1]);
|
||||||
|
|
||||||
|
assertEq("
|
||||||
|
import tests.FinalCase;
|
||||||
|
|
||||||
|
FinalCase.test1();
|
||||||
|
", FinalCase.test1());
|
||||||
|
|
||||||
|
assertEq("
|
||||||
|
import tests.FinalCase as FC;
|
||||||
|
|
||||||
|
FC.test1();
|
||||||
|
", FinalCase.test1());
|
||||||
|
|
||||||
|
assertEq("
|
||||||
|
import tests.FinalCase as FC;
|
||||||
|
import tests.FinalCase.test;
|
||||||
|
|
||||||
|
FC.test1() + test();
|
||||||
|
", FinalCase.test1() + test());
|
||||||
|
|
||||||
|
assertEq("
|
||||||
|
//import Std.isOfType;
|
||||||
|
|
||||||
|
Std.isOfType('', String);
|
||||||
|
", Std.isOfType('', String));
|
||||||
|
|
||||||
|
assertEq("
|
||||||
|
import Std.isOfType;
|
||||||
|
|
||||||
|
isOfType('', String);
|
||||||
|
", Std.isOfType('', String));
|
||||||
|
|
||||||
|
assertEq("
|
||||||
|
import Math.round as F;
|
||||||
|
F(2.9);", Math.round(2.9));
|
||||||
|
|
||||||
|
assertEq("
|
||||||
|
import Math.round;
|
||||||
|
round(1.9);", Math.round(1.9));
|
||||||
|
|
||||||
|
// Test EOF with preprocessor
|
||||||
|
}
|
||||||
|
|
||||||
|
static function test1() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
static function test() {
|
||||||
|
return "world";
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertDisplay(script:String, expected:Dynamic) {
|
||||||
|
var _script = script;
|
||||||
|
script = '"${script}";\n' + script;
|
||||||
|
assertEq(script, expected, _script);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestEnum {
|
||||||
|
A;
|
||||||
|
B;
|
||||||
|
C(a:Int, b:Int);
|
||||||
|
D(a:Int, b:Int, c:Int);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class FloatCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
headerCode = "
|
||||||
|
var nan = Math.NaN;
|
||||||
|
var pinf = Math.POSITIVE_INFINITY;
|
||||||
|
var ninf = Math.NEGATIVE_INFINITY;
|
||||||
|
var fl:Float = 0.0;
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var nan = Math.NaN;
|
||||||
|
var pinf = Math.POSITIVE_INFINITY;
|
||||||
|
var ninf = Math.NEGATIVE_INFINITY;
|
||||||
|
var fl:Float = 0.0;
|
||||||
|
|
||||||
|
assertEq("fl > nan", fl > nan);
|
||||||
|
assertEq("fl < nan", fl < nan);
|
||||||
|
assertEq("fl >= nan", fl >= nan);
|
||||||
|
assertEq("fl <= nan", fl <= nan);
|
||||||
|
assertEq("fl == nan", fl == nan);
|
||||||
|
assertEq("fl != nan", fl != nan);
|
||||||
|
|
||||||
|
assertEq("nan > nan", nan > nan);
|
||||||
|
assertEq("nan < nan", nan < nan);
|
||||||
|
assertEq("nan >= nan", nan >= nan);
|
||||||
|
assertEq("nan <= nan", nan <= nan);
|
||||||
|
assertEq("nan == nan", nan == nan);
|
||||||
|
assertEq("nan != nan", nan != nan);
|
||||||
|
|
||||||
|
assertEq("pinf > nan", pinf > nan);
|
||||||
|
assertEq("pinf < nan", pinf < nan);
|
||||||
|
assertEq("pinf >= nan", pinf >= nan);
|
||||||
|
assertEq("pinf <= nan", pinf <= nan);
|
||||||
|
assertEq("pinf == nan", pinf == nan);
|
||||||
|
assertEq("pinf != nan", pinf != nan);
|
||||||
|
|
||||||
|
assertEq("ninf > nan", ninf > nan);
|
||||||
|
assertEq("ninf < nan", ninf < nan);
|
||||||
|
assertEq("ninf >= nan", ninf >= nan);
|
||||||
|
assertEq("ninf <= nan", ninf <= nan);
|
||||||
|
assertEq("ninf == nan", ninf == nan);
|
||||||
|
assertEq("ninf != nan", ninf != nan);
|
||||||
|
|
||||||
|
assertEq("nan > fl", nan > fl);
|
||||||
|
assertEq("nan < fl", nan < fl);
|
||||||
|
assertEq("nan >= fl", nan >= fl);
|
||||||
|
assertEq("nan <= fl", nan <= fl);
|
||||||
|
assertEq("nan == fl", nan == fl);
|
||||||
|
assertEq("nan != fl", nan != fl);
|
||||||
|
|
||||||
|
assertEq("nan > pinf", nan > pinf);
|
||||||
|
assertEq("nan < pinf", nan < pinf);
|
||||||
|
assertEq("nan >= pinf", nan >= pinf);
|
||||||
|
assertEq("nan <= pinf", nan <= pinf);
|
||||||
|
assertEq("nan == pinf", nan == pinf);
|
||||||
|
assertEq("nan != pinf", nan != pinf);
|
||||||
|
|
||||||
|
assertEq("nan > ninf", nan > ninf);
|
||||||
|
assertEq("nan < ninf", nan < ninf);
|
||||||
|
assertEq("nan >= ninf", nan >= ninf);
|
||||||
|
assertEq("nan <= ninf", nan <= ninf);
|
||||||
|
assertEq("nan == ninf", nan == ninf);
|
||||||
|
assertEq("nan != ninf", nan != ninf);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class IntIteratorCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("assertEq", Util.assertEq);
|
||||||
|
interp.variables.set("IntIterator", IntIterator);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var ii = new IntIterator(0, 2);
|
||||||
|
var line = 19;
|
||||||
|
execute('
|
||||||
|
var ii = new IntIterator(0, 2);
|
||||||
|
assertEq(ii.hasNext(), ${ii.hasNext()}, "l${line++}");
|
||||||
|
assertEq(ii.next(), ${ii.next()}, "l${line++}");
|
||||||
|
assertEq(ii.hasNext(), ${ii.hasNext()}, "l${line++}");
|
||||||
|
assertEq(ii.next(), ${ii.next()}, "l${line++}");
|
||||||
|
assertEq(ii.hasNext(), ${ii.hasNext()}, "l${line++}");
|
||||||
|
');
|
||||||
|
|
||||||
|
var ii = new IntIterator(0, 2);
|
||||||
|
var r = [];
|
||||||
|
for (i in ii)
|
||||||
|
r.push(i);
|
||||||
|
assertEq('
|
||||||
|
var ii = new IntIterator(0, 2);
|
||||||
|
var r = [];
|
||||||
|
for (i in ii)
|
||||||
|
r.push(i);
|
||||||
|
r
|
||||||
|
', r);
|
||||||
|
|
||||||
|
var ii = new IntIterator(0, 2);
|
||||||
|
var r = [];
|
||||||
|
for (i in ii)
|
||||||
|
r.push(i);
|
||||||
|
for (i in ii)
|
||||||
|
r.push(i);
|
||||||
|
assertEq('
|
||||||
|
var ii = new IntIterator(0, 2);
|
||||||
|
var r = [];
|
||||||
|
for (i in ii)
|
||||||
|
r.push(i);
|
||||||
|
for (i in ii)
|
||||||
|
r.push(i);
|
||||||
|
r
|
||||||
|
', r);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class LambdaCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("Lambda", Lambda);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
// has
|
||||||
|
assertEq("Lambda.has([1,2,3],1)", Lambda.has([1,2,3],1));
|
||||||
|
assertEq("Lambda.has([1,2,3],4)", Lambda.has([1,2,3],4));
|
||||||
|
assertEq("Lambda.has([],null)", Lambda.has([],null));
|
||||||
|
assertEq("Lambda.has([null],null)", Lambda.has([null],null));
|
||||||
|
|
||||||
|
// exists
|
||||||
|
assertEq("Lambda.exists([1, 2, 3], function(i) return i == 2)", Lambda.exists([1, 2, 3], function(i) return i == 2));
|
||||||
|
assertEq("Lambda.exists([1, 2, 3], function(i) return i == 4)", Lambda.exists([1, 2, 3], function(i) return i == 4));
|
||||||
|
assertEq("Lambda.exists([], function(x) return true)", Lambda.exists([], function(x) return true));
|
||||||
|
|
||||||
|
// foreach
|
||||||
|
assertEq("Lambda.foreach([2, 4, 6],function(i) return i % 2 == 0)", Lambda.foreach([2, 4, 6],function(i) return i % 2 == 0));
|
||||||
|
assertEq("Lambda.foreach([2, 4, 7],function(i) return i % 2 == 0)", Lambda.foreach([2, 4, 7],function(i) return i % 2 == 0));
|
||||||
|
assertEq("Lambda.foreach([], function(x) return false)", Lambda.foreach([], function(x) return false));
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class ListCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("List", List);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var l4 = new List();
|
||||||
|
l4.add(1);
|
||||||
|
l4.add(2);
|
||||||
|
l4.add(3);
|
||||||
|
l4.add(5);
|
||||||
|
l4.add(8);
|
||||||
|
|
||||||
|
headerCode = "var l4 = new List();
|
||||||
|
l4.add(1);
|
||||||
|
l4.add(2);
|
||||||
|
l4.add(3);
|
||||||
|
l4.add(5);
|
||||||
|
l4.add(8);";
|
||||||
|
|
||||||
|
assertEq("[for (k=>v in l4) k]", [for (k=>v in l4) k]);
|
||||||
|
assertEq("[for (k=>v in l4) v]", [for (k=>v in l4) v]);
|
||||||
|
assertEq("[for (k=>v in l4) k*v]", [for (k=>v in l4) k*v]);
|
||||||
|
|
||||||
|
assertEq("[for (v in l4) v]", [for (v in l4) v]);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class MapCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("IntMap", haxe.ds.IntMap);
|
||||||
|
interp.variables.set("ObjectMap", haxe.ds.ObjectMap);
|
||||||
|
interp.variables.set("StringMap", haxe.ds.StringMap);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
Sys.println("TODO: Write tests for Map");
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class MathCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
assertEq("Math.PI", Math.PI);
|
||||||
|
assertEq("1 + 2", 3);
|
||||||
|
assertEq("1 + 2 + 3", 6);
|
||||||
|
assertEq("1 + 2 + 3 + 4", 10);
|
||||||
|
assertEq("(1 + 2) + 3", 6);
|
||||||
|
assertEq("(1 + 2) + 3 + 4", 10);
|
||||||
|
assertEq("(1 + 2) * 3", 9);
|
||||||
|
assertEq("(1 + 2) * 3 * 4", 36);
|
||||||
|
assertEq("((1 + 2) * 3 * 4) / 2", 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class MiscCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("Std", Std);
|
||||||
|
interp.variables.set("String", String);
|
||||||
|
interp.variables.set("Bool", Bool);
|
||||||
|
interp.variables.set("Float", Float);
|
||||||
|
interp.variables.set("Array", Array);
|
||||||
|
interp.variables.set("Int", Int);
|
||||||
|
interp.variables.set("IntIterator", IntIterator);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
assertEq("", null);
|
||||||
|
assertEq("true", true);
|
||||||
|
assertEq("false", false);
|
||||||
|
assertEq("null", null);
|
||||||
|
|
||||||
|
Util.runKnownBug("Redefining a variable with the same name as a function", () ->{
|
||||||
|
// Redefining a function with the same name as a variable
|
||||||
|
var a = 1; function a() { return 4; }
|
||||||
|
assertEq("var a = 1; function a() { return 4; }; a", a);
|
||||||
|
});
|
||||||
|
|
||||||
|
var a = 1; var a = 2;
|
||||||
|
assertEq("var a = 1; var a = 2; a", a);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
assertEq("0",0);
|
||||||
|
assertEq("0xFF", 255);
|
||||||
|
assertEq("0xFF_FF", 0xFFFF);
|
||||||
|
assertEq("0b101", 5); //assertEq("0b101", 0b101);
|
||||||
|
#if !(php || python)
|
||||||
|
#if haxe3
|
||||||
|
assertEq("0xBFFFFFFF", 0xBFFFFFFF);
|
||||||
|
assertEq("0x7FFFFFFF", 0x7FFFFFFF);
|
||||||
|
#elseif !neko
|
||||||
|
assertEq("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
|
||||||
|
assertEq("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
assertEq("-123",-123);
|
||||||
|
assertEq("- 123",-123);
|
||||||
|
assertEq("1.546",1.546);
|
||||||
|
assertEq(".545",.545);
|
||||||
|
assertEq("'bla'","bla");
|
||||||
|
assertEq("null",null);
|
||||||
|
assertEq("true",true);
|
||||||
|
assertEq("false",false);
|
||||||
|
assertEq("1 == 2",false);
|
||||||
|
assertEq("1.3 == 1.3",true);
|
||||||
|
assertEq("5 > 3",true);
|
||||||
|
assertEq("0 < 0",false);
|
||||||
|
assertEq("-1 <= -1",true);
|
||||||
|
assertEq("1 + 2",3);
|
||||||
|
assertEq("~545",-546);
|
||||||
|
assertEq("'abc' + 55","abc55");
|
||||||
|
assertEq("'abc' + 'de'","abcde");
|
||||||
|
assertEq("-1 + 2",1);
|
||||||
|
assertEq("1 / 5",0.2);
|
||||||
|
assertEq("3 * 2 + 5",11);
|
||||||
|
assertEq("3 * (2 + 5)",21);
|
||||||
|
assertEq("3 * 2 // + 5 \n + 6",12);
|
||||||
|
assertEq("3 /* 2\n */ + 5",8);
|
||||||
|
assertEq("[55,66,77][1]",66);
|
||||||
|
assertEq("var a = [55]; a[0] *= 2; a[0]",110);
|
||||||
|
assertEq("x",55,{ x : 55 });
|
||||||
|
assertEq("var y = 33; y",33);
|
||||||
|
assertEq("{ 1; 2; 3; }",3);
|
||||||
|
assertEq("o.val",55,{ o : { val : 55 } });
|
||||||
|
assertEq("o.val",null,{ o : {} });
|
||||||
|
assertEq("var a = 1; a++",1);
|
||||||
|
assertEq("var a = 1; a++; a",2);
|
||||||
|
assertEq("var a = 1; ++a",2);
|
||||||
|
assertEq("var a = 1; a *= 3",3);
|
||||||
|
assertEq("a = b = 3; a + b",6);
|
||||||
|
assertEq("add(1,2)",3,{ add : function(x,y) return x + y });
|
||||||
|
assertEq("a.push(5); a.pop() + a.pop()",{var a = [3]; a.push(5); a.pop() + a.pop();},{ a : [3] });
|
||||||
|
assertEq("if( true ) 1 else 2",1);
|
||||||
|
assertEq("if( false ) 1 else 2",2);
|
||||||
|
assertEq("var t = 0; for( x in [1,2,3] ) t += x; t",6);
|
||||||
|
assertEq("var a = new Array(); for( x in 0...5 ) a[x] = x; a.join('-')","0-1-2-3-4");
|
||||||
|
assertEq("(function(a,b) return a + b)(4,5)",9);
|
||||||
|
assertEq("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10);
|
||||||
|
assertEq("var a = false; do { a = true; } while (!a); a;",true);
|
||||||
|
assertEq("var t = 0; for( x in 1...10 ) t += x; t", 45);
|
||||||
|
#if haxe3
|
||||||
|
assertEq("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45);
|
||||||
|
#else
|
||||||
|
assertEq("var t = 0; for( x in new IntIter(1,10) ) t +=x; t", 45);
|
||||||
|
#end
|
||||||
|
assertEq("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",{var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x;});
|
||||||
|
assertEq("var i=2; if( true ) --i; i",1);
|
||||||
|
assertEq("var i=0; if( i++ > 0 ) i=3; i",1);
|
||||||
|
assertEq("var a = 5/2; a",2.5);
|
||||||
|
assertEq("{ x = 3; x; }", 3);
|
||||||
|
assertEq("{ x : 3, y : {} }.x", 3);
|
||||||
|
assertEq("function bug(){ \n }\nbug().x", null);
|
||||||
|
assertEq("1 + 2 == 3", true);
|
||||||
|
assertEq("-2 == 3 - 5", true);
|
||||||
|
assertEq("var x=-3; x", -3);
|
||||||
|
assertEq("var a:Array<Dynamic>=[1,2,4]; a[2]", 4);
|
||||||
|
assertEq("/**/0", 0);
|
||||||
|
assertEq("x=1;x*=-2", -2);
|
||||||
|
assertEq("var f = x -> x + 1; f(3)", 4);
|
||||||
|
assertEq("var f = () -> 55; f()", 55);
|
||||||
|
assertEq("var f = (x) -> x + 1; f(3)", 4);
|
||||||
|
assertEq("var f = (x:Int) -> x + 1; f(3)", 4);
|
||||||
|
assertEq("var f = (x,y) -> x + y; f(3,1)", 4);
|
||||||
|
assertEq("var f = (x,y:Int) -> x + y; f(3,1)", 4);
|
||||||
|
assertEq("var f = (x:Int,y:Int) -> x + y; f(3,1)", 4);
|
||||||
|
assertEq("var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1)", {var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1);});
|
||||||
|
assertEq("var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1)", {var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1);});
|
||||||
|
//assertEq("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", {var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2);});
|
||||||
|
//assertEq("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", {var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2);});
|
||||||
|
assertEq("var a = 10; var b = 5; a - -b", 15);
|
||||||
|
assertEq("var a = 10; var b = 5; a - b / 2", 7.5);
|
||||||
|
|
||||||
|
Util.runKnownBug("Redefining a variable in a scope overrides the previous definition", () -> {
|
||||||
|
assertEq("{ var x = 0; } x",55,{ x : 55 });
|
||||||
|
});
|
||||||
|
Util.runKnownBug("Global Y isnt updated from the inside of the function", () -> {
|
||||||
|
assertEq("var y = 0; var add = function(a) y += a; add(5); add(3); y", {var y = 0; var add = function(a) y += a; add(5); add(3); y;});
|
||||||
|
});
|
||||||
|
Util.runKnownBug("Throwing an exception inside a function doesnt return the correct value", () -> {
|
||||||
|
assertEq("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",{
|
||||||
|
var x = 1;
|
||||||
|
var f:Dynamic = function(x) throw x;
|
||||||
|
try
|
||||||
|
f(55)
|
||||||
|
catch( e:Dynamic )
|
||||||
|
e + x;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
assertEq("var a = if( true ) 1 else 2; a",1);
|
||||||
|
assertEq("var a = if( false ) 1 else 2; a",2);
|
||||||
|
assertEq("if(true) 1; null", null);
|
||||||
|
assertEq("if(false) 1; null", null);
|
||||||
|
|
||||||
|
assertEq("[55,66,77][1]",66);
|
||||||
|
|
||||||
|
assertEq("{
|
||||||
|
var a = 1;
|
||||||
|
function b() { return a; }
|
||||||
|
a = 2;
|
||||||
|
b();
|
||||||
|
}", 2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class ReflectCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("Reflect", Reflect);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
headerCode = "var x = { a: 1, b: null };";
|
||||||
|
var x = { a: 1, b: null };
|
||||||
|
|
||||||
|
assertEq("Reflect.field(x, 'a')", Reflect.field(x, 'a'));
|
||||||
|
assertEq("Reflect.field(x, 'b')", Reflect.field(x, 'b'));
|
||||||
|
assertEq("Reflect.field(x, 'c')", Reflect.field(x, 'c'));
|
||||||
|
|
||||||
|
assertEq("Reflect.hasField(x, 'a')", Reflect.hasField(x, 'a'));
|
||||||
|
assertEq("Reflect.hasField(x, 'b')", Reflect.hasField(x, 'b'));
|
||||||
|
assertEq("Reflect.hasField(x, 'c')", Reflect.hasField(x, 'c'));
|
||||||
|
|
||||||
|
headerCode = "var n = null;";
|
||||||
|
var n = null;
|
||||||
|
|
||||||
|
assertEq("Reflect.field(n, n)", Reflect.field(n, n));
|
||||||
|
assertEq("Reflect.field(1, 'foo')", Reflect.field(1, "foo"));
|
||||||
|
|
||||||
|
headerCode = "var x = { a: 1, b: null };";
|
||||||
|
|
||||||
|
Reflect.setField(x, 'a', 2); assertEq("Reflect.setField(x, 'a', 2); x.a", x.a);
|
||||||
|
Reflect.setField(x, 'c', 'foo'); assertEq("Reflect.setField(x, 'c', 'foo'); Reflect.field(x, 'c')", Reflect.field(x, 'c'));
|
||||||
|
|
||||||
|
headerCode = "var x = { a: 1, b: null };";
|
||||||
|
|
||||||
|
Reflect.setProperty(x, 'a', 2); assertEq("Reflect.setProperty(x, 'a', 2); x.a", x.a);
|
||||||
|
Reflect.setProperty(x, 'c', 'foo'); assertEq("Reflect.setProperty(x, 'c', 'foo'); Reflect.field(x, 'c')", Reflect.field(x, 'c'));
|
||||||
|
|
||||||
|
headerCode = "var x = function(t) return 1; var y = function(t) return -1; var z = function(t) return 1;";
|
||||||
|
var x = function(t) return 1;
|
||||||
|
var y = function(t) return -1;
|
||||||
|
var z = function(t) return 1;
|
||||||
|
|
||||||
|
assertEq("Reflect.compareMethods(x,y)", Reflect.compareMethods(x,y));
|
||||||
|
assertEq("Reflect.compareMethods(x,z)", Reflect.compareMethods(x,z));
|
||||||
|
assertEq("Reflect.compareMethods(y,z)", Reflect.compareMethods(y,z));
|
||||||
|
assertEq("Reflect.compareMethods(x,x)", Reflect.compareMethods(x,x));
|
||||||
|
assertEq("Reflect.compareMethods(y,y)", Reflect.compareMethods(y,y));
|
||||||
|
assertEq("Reflect.compareMethods(z,z)", Reflect.compareMethods(z,z));
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class RegexCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
/*headerCode = "
|
||||||
|
var r = ~/a/;
|
||||||
|
var rg = ~/a/g;
|
||||||
|
var rg2 = ~/aa/g;
|
||||||
|
";*/
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("assertEq", Util.assertEq);
|
||||||
|
interp.variables.set("EReg", EReg);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var r = new EReg("a", "");
|
||||||
|
var rg = new EReg("a", "g");
|
||||||
|
var rg2 = new EReg("aa", "g");
|
||||||
|
|
||||||
|
// TODO: implement better checking for regexes
|
||||||
|
//assertEq("new EReg('a', '');", r);
|
||||||
|
//assertEq("new EReg('a', 'g');", rg);
|
||||||
|
//assertEq("new EReg('aa', 'g');", rg2);
|
||||||
|
|
||||||
|
//@:privateAccess assertEq("new EReg('a', '').toString();", r.toString());
|
||||||
|
//@:privateAccess assertEq("new EReg('a', 'g').toString();", rg.toString());
|
||||||
|
//@:privateAccess assertEq("new EReg('aa', 'g').toString();", rg2.toString());
|
||||||
|
|
||||||
|
//assertEq(r.match("") == false);
|
||||||
|
//assertEq(r.match("b") == false);
|
||||||
|
//assertEq(r.match("a") == true);
|
||||||
|
//assertEq(r.matched(0) == "a");
|
||||||
|
//assertEq(r.matchedLeft() == "");
|
||||||
|
//assertEq(r.matchedRight() == "");
|
||||||
|
//var pos = r.matchedPos();
|
||||||
|
//assertEq(pos.pos == 0);
|
||||||
|
//assertEq(pos.len == 1);
|
||||||
|
|
||||||
|
headerCode = "var r = ~/a/;";
|
||||||
|
|
||||||
|
assertEq('[r.match(""), r.match("b"), r.match("a"), r.matched(0), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]', [r.match(""), r.match("b"), r.match("a"), r.matched(0), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]);
|
||||||
|
assertEq('[r.match("aa"), r.matched(0), r.match("a"), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]', [r.match("aa"), r.matched(0), r.match("a"), r.matchedLeft(), r.matchedRight(), {var pos = r.matchedPos(); [pos.pos, pos.len];}]);
|
||||||
|
|
||||||
|
|
||||||
|
headerCode = "var rg = ~/a/g;";
|
||||||
|
|
||||||
|
assertEq('[rg.match("aa"), rg.matched(0), rg.matchedLeft(), rg.matchedRight(), {var pos = rg.matchedPos(); [pos.pos, pos.len];}]', [rg.match("aa"), rg.matched(0), rg.matchedLeft(), rg.matchedRight(), {var pos = rg.matchedPos(); [pos.pos, pos.len];}]);
|
||||||
|
|
||||||
|
headerCode = "var rg2 = ~/aa/g;";
|
||||||
|
|
||||||
|
assertEq('[rg2.match("aa"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]', [rg2.match("aa"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]);
|
||||||
|
assertEq('[rg2.match("AaaBaaC"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]', [rg2.match("AaaBaaC"), rg2.matched(0), rg2.matchedLeft(), rg2.matchedRight(), {var pos = rg2.matchedPos(); [pos.pos, pos.len];}]);
|
||||||
|
|
||||||
|
headerCode = "";
|
||||||
|
|
||||||
|
// split
|
||||||
|
assertEq('~/a/.split("")', ~/a/.split(""));
|
||||||
|
assertEq('~/a/.split("a")', ~/a/.split("a"));
|
||||||
|
assertEq('~/a/.split("aa")', ~/a/.split("aa"));
|
||||||
|
assertEq('~/a/.split("b")', ~/a/.split("b"));
|
||||||
|
assertEq('~/a/.split("ab")', ~/a/.split("ab"));
|
||||||
|
assertEq('~/a/.split("ba")', ~/a/.split("ba"));
|
||||||
|
assertEq('~/a/.split("aba")', ~/a/.split("aba"));
|
||||||
|
assertEq('~/a/.split("bab")', ~/a/.split("bab"));
|
||||||
|
assertEq('~/a/.split("baba")', ~/a/.split("baba"));
|
||||||
|
|
||||||
|
// split + g
|
||||||
|
assertEq('~/a/g.split("")', ~/a/g.split(""));
|
||||||
|
assertEq('~/a/g.split("a")', ~/a/g.split("a"));
|
||||||
|
assertEq('~/a/g.split("aa")', ~/a/g.split("aa"));
|
||||||
|
assertEq('~/a/g.split("b")', ~/a/g.split("b"));
|
||||||
|
assertEq('~/a/g.split("ab")', ~/a/g.split("ab"));
|
||||||
|
assertEq('~/a/g.split("ba")', ~/a/g.split("ba"));
|
||||||
|
assertEq('~/a/g.split("aba")', ~/a/g.split("aba"));
|
||||||
|
assertEq('~/a/g.split("bab")', ~/a/g.split("bab"));
|
||||||
|
assertEq('~/a/g.split("baba")', ~/a/g.split("baba"));
|
||||||
|
|
||||||
|
// replace
|
||||||
|
assertEq('~/a/.replace("", "z")', ~/a/.replace("", "z"));
|
||||||
|
assertEq('~/a/.replace("a", "z")', ~/a/.replace("a", "z"));
|
||||||
|
assertEq('~/a/.replace("aa", "z")', ~/a/.replace("aa", "z"));
|
||||||
|
assertEq('~/a/.replace("b", "z")', ~/a/.replace("b", "z"));
|
||||||
|
assertEq('~/a/.replace("ab", "z")', ~/a/.replace("ab", "z"));
|
||||||
|
assertEq('~/a/.replace("ba", "z")', ~/a/.replace("ba", "z"));
|
||||||
|
assertEq('~/a/.replace("aba", "z")', ~/a/.replace("aba", "z"));
|
||||||
|
assertEq('~/a/.replace("bab", "z")', ~/a/.replace("bab", "z"));
|
||||||
|
assertEq('~/a/.replace("baba", "z")', ~/a/.replace("baba", "z"));
|
||||||
|
|
||||||
|
//Util.runKnownBug("Regex Syntax doesn't work", () -> {
|
||||||
|
assertCompiles("~/a/;");
|
||||||
|
assertCompiles("~/(a)a\\0\\//g;");
|
||||||
|
assertCompiles("~/(a)a\\0\\//g+5;");
|
||||||
|
assertError("~/(a)a\\0\\//ga;", Parser.getBaseError(ECustom("Invalid regex expression option \"a\"")));
|
||||||
|
//assertEq("~/a/+5", null);//~/a/+5);
|
||||||
|
//});
|
||||||
|
|
||||||
|
// replace + g
|
||||||
|
assertEq('~/a/g.replace("", "z")', ~/a/g.replace("", "z"));
|
||||||
|
assertEq('~/a/g.replace("a", "z")', ~/a/g.replace("a", "z"));
|
||||||
|
assertEq('~/a/g.replace("aa", "z")', ~/a/g.replace("aa", "z"));
|
||||||
|
assertEq('~/a/g.replace("b", "z")', ~/a/g.replace("b", "z"));
|
||||||
|
assertEq('~/a/g.replace("ab", "z")', ~/a/g.replace("ab", "z"));
|
||||||
|
assertEq('~/a/g.replace("ba", "z")', ~/a/g.replace("ba", "z"));
|
||||||
|
assertEq('~/a/g.replace("aba", "z")', ~/a/g.replace("aba", "z"));
|
||||||
|
assertEq('~/a/g.replace("bab", "z")', ~/a/g.replace("bab", "z"));
|
||||||
|
assertEq('~/a/g.replace("baba", "z")', ~/a/g.replace("baba", "z"));
|
||||||
|
|
||||||
|
// var 0 = 5; // Missing variable identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class StdCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("Std", Std);
|
||||||
|
interp.variables.set("String", String);
|
||||||
|
interp.variables.set("Bool", Bool);
|
||||||
|
interp.variables.set("Float", Float);
|
||||||
|
interp.variables.set("Array", Array);
|
||||||
|
interp.variables.set("Int", Int);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var known:String = null; assertEq('var known:String = null; known is String;', known is String);
|
||||||
|
var unknown = null; assertEq('var unknown = null; unknown is String;', unknown is String);
|
||||||
|
assertEq('null is String;', null is String);
|
||||||
|
|
||||||
|
assertEq('"" is String;', ("" is String));
|
||||||
|
assertEq('false is Bool;', (false is Bool));
|
||||||
|
assertEq("1 is Int;", (1 is Int));
|
||||||
|
assertEq("1.5 is Int;", (1.5 is Int));
|
||||||
|
assertEq("1.5 is Float;", (1.5 is Float));
|
||||||
|
assertEq("[] is Array;", ([] is Array));
|
||||||
|
|
||||||
|
// isOfType
|
||||||
|
assertEq("var known:String = null; Std.isOfType(known, String);", Std.isOfType(known, String));
|
||||||
|
|
||||||
|
assertEq("var unknown = null; Std.isOfType(unknown, String);", Std.isOfType(unknown, String));
|
||||||
|
assertEq("Std.isOfType(null, String);", Std.isOfType(null, String));
|
||||||
|
|
||||||
|
assertEq("Std.isOfType('', String);", Std.isOfType("", String));
|
||||||
|
assertEq("Std.isOfType(false, Bool);", Std.isOfType(false, Bool));
|
||||||
|
assertEq("Std.isOfType(1, Int)", Std.isOfType(1, Int));
|
||||||
|
assertEq("Std.isOfType(1.5, Int);", Std.isOfType(1.5, Int));
|
||||||
|
assertEq("Std.isOfType(1.5, Float);", Std.isOfType(1.5, Float));
|
||||||
|
assertEq("Std.isOfType([], Array);", Std.isOfType([], Array));
|
||||||
|
// Std.isOfType(cast unit.MyEnum.A, Array) == false;
|
||||||
|
|
||||||
|
// instance
|
||||||
|
#if !js
|
||||||
|
assertEq("Std.downcast('', String)", Std.downcast("", String));
|
||||||
|
Std.downcast("", String) == "";
|
||||||
|
#end
|
||||||
|
|
||||||
|
var a = [];
|
||||||
|
assertEq("var a = []; Std.downcast(a, Array)", Std.downcast(a, Array));
|
||||||
|
assertEq("Std.downcast(null, Array)", Std.downcast(null, Array));
|
||||||
|
assertEq("Std.downcast(null, String)", Std.downcast(null, String));
|
||||||
|
|
||||||
|
// string
|
||||||
|
/*var cwts = new ClassWithToString();
|
||||||
|
var cwtsc = new ClassWithToStringChild();
|
||||||
|
var cwtsc2 = new ClassWithToStringChild2();
|
||||||
|
|
||||||
|
Std.string(cwts) == "ClassWithToString.toString()";
|
||||||
|
Std.string(cwtsc) == "ClassWithToString.toString()";
|
||||||
|
Std.string(cwtsc2) == "ClassWithToStringChild2.toString()";
|
||||||
|
|
||||||
|
Std.string(SomeEnum.NoArguments) == "NoArguments";
|
||||||
|
Std.string(SomeEnum.OneArgument("foo")) == "OneArgument(foo)";*/
|
||||||
|
|
||||||
|
assertEq("Std.string(null)", Std.string(null));
|
||||||
|
|
||||||
|
// int
|
||||||
|
assertEq("Std.int(-1.7)", Std.int(-1.7));
|
||||||
|
assertEq("Std.int(-1.2)", Std.int(-1.2));
|
||||||
|
assertEq("Std.int(-0.7)", Std.int(-0.7));
|
||||||
|
assertEq("Std.int(-0.2)", Std.int(-0.2));
|
||||||
|
assertEq("Std.int(0.7)", Std.int(0.7));
|
||||||
|
assertEq("Std.int(0.2)", Std.int(0.2));
|
||||||
|
|
||||||
|
// general
|
||||||
|
assertEq("Std.parseInt('0')", Std.parseInt("0"));
|
||||||
|
assertEq("Std.parseInt('-1')", Std.parseInt("-1"));
|
||||||
|
|
||||||
|
// preceeding zeroes
|
||||||
|
assertEq("Std.parseInt('0001')", Std.parseInt("0001"));
|
||||||
|
assertEq("Std.parseInt('0010')", Std.parseInt("0010"));
|
||||||
|
// trailing text
|
||||||
|
assertEq("Std.parseInt('100x123')", Std.parseInt("100x123"));
|
||||||
|
assertEq("Std.parseInt('12foo13')", Std.parseInt("12foo13"));
|
||||||
|
assertEq("Std.parseInt('23e2')", Std.parseInt("23e2"));
|
||||||
|
assertEq("Std.parseInt('0x10z')", Std.parseInt("0x10z"));
|
||||||
|
assertEq("Std.parseInt('0x10x123')", Std.parseInt("0x10x123"));
|
||||||
|
assertEq("Std.parseInt('0x10x123\\n')", Std.parseInt("0x10x123\n"));
|
||||||
|
assertEq("Std.parseInt('0xff\\n')", Std.parseInt("0xff\n"));
|
||||||
|
|
||||||
|
// hexadecimals
|
||||||
|
assertEq("Std.parseInt('0xff')", Std.parseInt("0xff"));
|
||||||
|
assertEq("Std.parseInt('0x123')", Std.parseInt("0x123"));
|
||||||
|
assertEq("Std.parseInt('0XFF')", Std.parseInt("0XFF"));
|
||||||
|
assertEq("Std.parseInt('0X123')", Std.parseInt("0X123"));
|
||||||
|
assertEq("Std.parseInt('0X01')", Std.parseInt("0X01"));
|
||||||
|
assertEq("Std.parseInt('0x01')", Std.parseInt("0x01"));
|
||||||
|
|
||||||
|
// signs
|
||||||
|
assertEq("Std.parseInt('123')", Std.parseInt("123"));
|
||||||
|
assertEq("Std.parseInt('+123')", Std.parseInt("+123"));
|
||||||
|
assertEq("Std.parseInt('-123')", Std.parseInt("-123"));
|
||||||
|
assertEq("Std.parseInt('0xa0')", Std.parseInt("0xa0"));
|
||||||
|
assertEq("Std.parseInt('+0xa0')", Std.parseInt("+0xa0"));
|
||||||
|
assertEq("Std.parseInt('-0xa0')", Std.parseInt("-0xa0"));
|
||||||
|
|
||||||
|
// whitespace: space, horizontal tab, newline, vertical tab, form feed, and carriage return
|
||||||
|
assertEq("Std.parseInt(' 5')", Std.parseInt(" 5"));
|
||||||
|
|
||||||
|
// whitespace and signs
|
||||||
|
assertEq("Std.parseInt(' 16')", Std.parseInt(" 16"));
|
||||||
|
assertEq("Std.parseInt(' -16')", Std.parseInt(" -16"));
|
||||||
|
assertEq("Std.parseInt(' +16')", Std.parseInt(" +16"));
|
||||||
|
assertEq("Std.parseInt(' 0x10')", Std.parseInt(" 0x10"));
|
||||||
|
assertEq("Std.parseInt(' -0x10')", Std.parseInt(" -0x10"));
|
||||||
|
assertEq("Std.parseInt(' +0x10')", Std.parseInt(" +0x10"));
|
||||||
|
|
||||||
|
// binary and octal unsupported
|
||||||
|
assertEq("Std.parseInt('010')", Std.parseInt("010"));
|
||||||
|
assertEq("Std.parseInt('0b10')", Std.parseInt("0b10"));
|
||||||
|
// null
|
||||||
|
assertEq("Std.parseInt(null)", Std.parseInt(null));
|
||||||
|
// no number
|
||||||
|
assertEq("Std.parseInt('')", Std.parseInt(""));
|
||||||
|
assertEq("Std.parseInt('abcd')", Std.parseInt("abcd"));
|
||||||
|
assertEq("Std.parseInt('a10')", Std.parseInt("a10"));
|
||||||
|
// invalid use of signs
|
||||||
|
assertEq("Std.parseInt('++123')", Std.parseInt("++123"));
|
||||||
|
assertEq("Std.parseInt('+-123')", Std.parseInt("+-123"));
|
||||||
|
assertEq("Std.parseInt('-+123')", Std.parseInt("-+123"));
|
||||||
|
assertEq("Std.parseInt('--123')", Std.parseInt("--123"));
|
||||||
|
assertEq("Std.parseInt('+ 123')", Std.parseInt("+ 123"));
|
||||||
|
assertEq("Std.parseInt('- 123')", Std.parseInt("- 123"));
|
||||||
|
assertEq("Std.parseInt('++0x123')", Std.parseInt("++0x123"));
|
||||||
|
assertEq("Std.parseInt('+-0x123')", Std.parseInt("+-0x123"));
|
||||||
|
assertEq("Std.parseInt('-+0x123')", Std.parseInt("-+0x123"));
|
||||||
|
assertEq("Std.parseInt('--0x123')", Std.parseInt("--0x123"));
|
||||||
|
assertEq("Std.parseInt('+ 0x123')", Std.parseInt("+ 0x123"));
|
||||||
|
assertEq("Std.parseInt('- 0x123')", Std.parseInt("- 0x123"));
|
||||||
|
|
||||||
|
// hexadecimal prefix with no number
|
||||||
|
//unspec(Std.parseInt.bind("0x"));
|
||||||
|
//unspec(Std.parseInt.bind("0x C"));
|
||||||
|
//unspec(Std.parseInt.bind("0x+A"));
|
||||||
|
|
||||||
|
// parseFloat
|
||||||
|
|
||||||
|
// general
|
||||||
|
assertEq("Std.parseFloat('0')", Std.parseFloat('0'));
|
||||||
|
assertEq("Std.parseFloat('0.0')", Std.parseFloat('0.0'));
|
||||||
|
// preceeding zeroes
|
||||||
|
assertEq("Std.parseFloat('0001')", Std.parseFloat('0001'));
|
||||||
|
assertEq("Std.parseFloat('0010')", Std.parseFloat('0010'));
|
||||||
|
// trailing text
|
||||||
|
assertEq("Std.parseFloat('100x123')", Std.parseFloat('100x123'));
|
||||||
|
assertEq("Std.parseFloat('12foo13')", Std.parseFloat('12foo13'));
|
||||||
|
assertEq("Std.parseFloat('5.3 ')", Std.parseFloat('5.3 '));
|
||||||
|
assertEq("Std.parseFloat('5.3 1')", Std.parseFloat('5.3 1'));
|
||||||
|
// signs
|
||||||
|
assertEq("Std.parseFloat('123.45')", Std.parseFloat('123.45'));
|
||||||
|
assertEq("Std.parseFloat('+123.45')", Std.parseFloat('+123.45'));
|
||||||
|
// whitespace: space, horizontal tab, newline, vertical tab, form feed, and carriage return
|
||||||
|
// whitespace and signs
|
||||||
|
assertEq("Std.parseFloat(' 1.6')", Std.parseFloat(' 1.6'));
|
||||||
|
assertEq("Std.parseFloat(' -1.6')", Std.parseFloat(' -1.6'));
|
||||||
|
assertEq("Std.parseFloat(' +1.6')", Std.parseFloat(' +1.6'));
|
||||||
|
// exponent
|
||||||
|
assertEq("Std.parseFloat('2.426670815e12')", Std.parseFloat('2.426670815e12'));
|
||||||
|
assertEq("Std.parseFloat('2.426670815E12')", Std.parseFloat('2.426670815E12'));
|
||||||
|
assertEq("Std.parseFloat('2.426670815e+12')", Std.parseFloat('2.426670815e+12'));
|
||||||
|
assertEq("Std.parseFloat('2.426670815E+12')", Std.parseFloat('2.426670815E+12'));
|
||||||
|
assertEq("Std.parseFloat('2.426670815e-12')", Std.parseFloat('2.426670815e-12'));
|
||||||
|
assertEq("Std.parseFloat('2.426670815E-12')", Std.parseFloat('2.426670815E-12'));
|
||||||
|
|
||||||
|
#if !interp
|
||||||
|
assertEq("Std.parseFloat('6e')", Std.parseFloat('6e'));
|
||||||
|
assertEq("Std.parseFloat('6e')", Std.parseFloat('6e'));
|
||||||
|
#end
|
||||||
|
// null
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat(null))", Math.isNaN(Std.parseFloat(null)));
|
||||||
|
// no number
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat(''))", Math.isNaN(Std.parseFloat("")));
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('abcd'))", Math.isNaN(Std.parseFloat("abcd")));
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('a10'))", Math.isNaN(Std.parseFloat("a10")));
|
||||||
|
// invalid use of signs
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('++12.3'))", Math.isNaN(Std.parseFloat("++12.3")));
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('+-12.3'))", Math.isNaN(Std.parseFloat("+-12.3")));
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('-+12.3'))", Math.isNaN(Std.parseFloat("-+12.3")));
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('--12.3'))", Math.isNaN(Std.parseFloat("--12.3")));
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('+ 12.3'))", Math.isNaN(Std.parseFloat("+ 12.3")));
|
||||||
|
assertEq("Math.isNaN(Std.parseFloat('- 12.3'))", Math.isNaN(Std.parseFloat("- 12.3")));
|
||||||
|
|
||||||
|
// random
|
||||||
|
// var x = Std.random(2);
|
||||||
|
// //x in [0,1];
|
||||||
|
// Std.random(1) == 0;
|
||||||
|
// Std.random(0) == 0;
|
||||||
|
// Std.random(-100) == 0;
|
||||||
|
|
||||||
|
|
||||||
|
Util.runKnownBug("Not all escape sequences are supported", () -> {
|
||||||
|
assertEq("Std.parseInt(' \\t\\n\\x0b\\x0c\\r16')", Std.parseInt(" \t\n\x0b\x0c\r16"));
|
||||||
|
assertEq("Std.parseInt(' \\t\\n\\x0b\\x0c\\r0xa')", Std.parseInt(" \t\n\x0b\x0c\r0xa"));
|
||||||
|
assertEq("Std.parseFloat(' \\t\\n\\x0b\\x0c\\r1.6')", Std.parseFloat('\t\n\x0b\x0c\r1.6'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class StringBufCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("Std", Std);
|
||||||
|
interp.variables.set("StringBuf", StringBuf);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
var x = new StringBuf();
|
||||||
|
x.addSub("abcdefg", 1);
|
||||||
|
assertEq('var x = new StringBuf(); x.addSub("abcdefg", 1); x.toString()', x.toString());
|
||||||
|
|
||||||
|
Util.runKnownBug("Emojis dont work", () -> {
|
||||||
|
var x = new StringBuf();
|
||||||
|
x.add("👽");
|
||||||
|
assertEq('var x = new StringBuf(); x.add("👽"); x.toString()', x.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
var x = new StringBuf();
|
||||||
|
x.add(0x1F47D);
|
||||||
|
assertEq('var x = new StringBuf(); x.add(0x1F47D); x.toString()', x.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class StringCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("Std", Std);
|
||||||
|
interp.variables.set("String", String);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
assertEq('"foo"', "foo");
|
||||||
|
var str = "foo";
|
||||||
|
assertEq('var str = "foo"; str == new String(str)', str == new String(str));
|
||||||
|
|
||||||
|
assertEq('"foo".toUpperCase()', "foo".toUpperCase());
|
||||||
|
assertEq('"_bar".toUpperCase()', "_bar".toUpperCase());
|
||||||
|
assertEq('"123b".toUpperCase()', "123b".toUpperCase());
|
||||||
|
assertEq('"".toUpperCase()', "".toUpperCase());
|
||||||
|
assertEq('"A".toUpperCase()', "A".toUpperCase());
|
||||||
|
assertEq('"FOO".toLowerCase()', "FOO".toLowerCase());
|
||||||
|
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
|
||||||
|
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
|
||||||
|
assertEq('"123B".toLowerCase()', "123B".toLowerCase());
|
||||||
|
assertEq('"".toLowerCase()', "".toLowerCase());
|
||||||
|
assertEq('"a".toLowerCase()', "a".toLowerCase());
|
||||||
|
|
||||||
|
headerCode = 'var s = "foo1bar";';
|
||||||
|
var s = "foo1bar";
|
||||||
|
|
||||||
|
assertEq("s.charAt(0)", s.charAt(0));
|
||||||
|
assertEq("s.charAt(1)", s.charAt(1));
|
||||||
|
assertEq("s.charAt(2)", s.charAt(2));
|
||||||
|
assertEq("s.charAt(3)", s.charAt(3));
|
||||||
|
assertEq("s.charAt(4)", s.charAt(4));
|
||||||
|
assertEq("s.charAt(5)", s.charAt(5));
|
||||||
|
assertEq("s.charAt(6)", s.charAt(6));
|
||||||
|
assertEq("s.charAt(7)", s.charAt(7));
|
||||||
|
assertEq("s.charAt(-1)", s.charAt(-1));
|
||||||
|
assertEq('"".charAt(0)', "".charAt(0));
|
||||||
|
assertEq('"".charAt(1)', "".charAt(1));
|
||||||
|
assertEq('"".charAt(-1)', "".charAt(-1));
|
||||||
|
|
||||||
|
headerCode = 'var s = "foo1bar";';
|
||||||
|
var s = "foo1bar";
|
||||||
|
|
||||||
|
assertEq("s.charCodeAt(0)", s.charCodeAt(0));
|
||||||
|
assertEq("s.charCodeAt(1)", s.charCodeAt(1));
|
||||||
|
assertEq("s.charCodeAt(2)", s.charCodeAt(2));
|
||||||
|
assertEq("s.charCodeAt(3)", s.charCodeAt(3));
|
||||||
|
assertEq("s.charCodeAt(4)", s.charCodeAt(4));
|
||||||
|
assertEq("s.charCodeAt(5)", s.charCodeAt(5));
|
||||||
|
assertEq("s.charCodeAt(6)", s.charCodeAt(6));
|
||||||
|
assertEq("s.charCodeAt(7)", s.charCodeAt(7));
|
||||||
|
assertEq("s.charCodeAt(-1)", s.charCodeAt(-1));
|
||||||
|
|
||||||
|
headerCode = '';
|
||||||
|
|
||||||
|
assertEq('("3" > "11")', ("3" > "11"));
|
||||||
|
assertEq('(" 3" < "3")', (" 3" < "3"));
|
||||||
|
assertEq('("a" < "b")', ("a" < "b"));
|
||||||
|
assertEq('("a" <= "b")', ("a" <= "b"));
|
||||||
|
assertEq('("a" > "b")', ("a" > "b"));
|
||||||
|
assertEq('("a" >= "b")', ("a" >= "b"));
|
||||||
|
|
||||||
|
assertEq('"foo".toUpperCase()', "foo".toUpperCase());
|
||||||
|
assertEq('"_bar".toUpperCase()', "_bar".toUpperCase());
|
||||||
|
assertEq('"123b".toUpperCase()', "123b".toUpperCase());
|
||||||
|
assertEq('"".toUpperCase()', "".toUpperCase());
|
||||||
|
assertEq('"A".toUpperCase()', "A".toUpperCase());
|
||||||
|
assertEq('"FOO".toLowerCase()', "FOO".toLowerCase());
|
||||||
|
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
|
||||||
|
assertEq('"_BAR".toLowerCase()', "_BAR".toLowerCase());
|
||||||
|
assertEq('"123B".toLowerCase()', "123B".toLowerCase());
|
||||||
|
assertEq('"".toLowerCase()', "".toLowerCase());
|
||||||
|
assertEq('"a".toLowerCase()', "a".toLowerCase());
|
||||||
|
|
||||||
|
assertEqPrintable("' \\t\\n\\x0b\\x0c\\r16'", " \t\n\x0b\x0c\r16");
|
||||||
|
assertEqPrintable("' \\t\\n\\x0b\\x0c\\r0xa'", " \t\n\x0b\x0c\r0xa");
|
||||||
|
assertEqPrintable("' \\t\\n\\x0b\\x0c\\r1.6'", ' \t\n\x0b\x0c\r1.6');
|
||||||
|
|
||||||
|
assertEqPrintable("'The \\x54\\141b\\tch\\141r\\141ct\\145r:\\n'", 'The \x54\141b\tch\141r\141ct\145r:\n');
|
||||||
|
assertEqPrintable("'\\t\\\"\\101scii\\\"\\n'", '\t\"\101scii\"\n');
|
||||||
|
assertEqPrintable("'\\tcontains\\n'", '\tcontains\n');
|
||||||
|
assertEqPrintable("'\\tspecial\\\\backslash\\\\codes,\\n'", '\tspecial\\backslash\\codes,\n');
|
||||||
|
assertEqPrintable("'\\tdouble quotes \\\"like this\\\",\\n'", '\tdouble quotes \"like this\",\n');
|
||||||
|
assertEqPrintable("'\\tsingle quotes \\'and this\\',\\n'", '\tsingle quotes \'and this\',\n');
|
||||||
|
assertEqPrintable("'\\tASCII bell\\x07and others.\\n'", '\tASCII bell\x07and others.\n');
|
||||||
|
assertEqPrintable("'Unicode samples: Greek \\u03B1 (alpha), smiley \\u263A,\\n'", 'Unicode samples: Greek \u03B1 (alpha), smiley \u263A,\n');
|
||||||
|
assertEqPrintable("'regional indicators \\u{1F1FA}\\u{1F1F8}, and musical note \\u{1F3B5}.'", 'regional indicators \u{1F1FA}\u{1F1F8}, and musical note \u{1F3B5}.');
|
||||||
|
assertEqPrintable("'\\u{10FFFF}\\u{1F1FA}\\u{3042}\\u{3B1}\\u{F1}\\u{A}'", '\u{10FFFF}\u{1F1FA}\u{3042}\u{3B1}\u{F1}\u{A}');
|
||||||
|
|
||||||
|
//trace(hscript.Printer.getEscapedString("\u{10FFFF}\u{1F1FA}\u{3042}\u{3B1}\u{F1}\u{A}"));
|
||||||
|
|
||||||
|
assertEq('"\\""', "\"");
|
||||||
|
assertEq("'\\''", '\'');
|
||||||
|
assertEq("'\\\\'", '\\');
|
||||||
|
assertEq("'\\n'", '\n');
|
||||||
|
assertEq("'\\r'", '\r');
|
||||||
|
assertEq("'\\t'", '\t');
|
||||||
|
assertEq("'\\101'", '\101');
|
||||||
|
assertEq("'/'", '/');
|
||||||
|
|
||||||
|
assertEq('"$${5}"', "${5}");
|
||||||
|
assertEq('"$${5},$${({})}"', "${5},${({})}");
|
||||||
|
assertEq('"$${5},$${\\"Hello\\"}"', "${5},${\"Hello\"}");
|
||||||
|
assertEq('"$${5},$${\'Hello\'}"', "${5},${'Hello'}");
|
||||||
|
assertEq("'$${({})}'", '$${({})}');
|
||||||
|
|
||||||
|
assertEq("'${5},${({})}'", '${5},${({})}');
|
||||||
|
assertEq("'${5},${\"Hello\"}'", '${5},${"Hello"}');
|
||||||
|
assertEq("'${5},${'Hello'}'", '${5},${'Hello'}');
|
||||||
|
assertEq("'$${5}'", '$${5}');
|
||||||
|
assertEq("'${5}'", '${5}');
|
||||||
|
assertEq("'${'${6}world'}'", '${'${6}world'}');
|
||||||
|
var a = 5;
|
||||||
|
headerCode = 'var a = 5;';
|
||||||
|
assertEq("'$a'", '$a');
|
||||||
|
assertEq("'$$a'", '$$a');
|
||||||
|
assertEq("'$ '", '$ ');
|
||||||
|
assertEq("'$0'", '$0');
|
||||||
|
assertEq("'$a+5 Hello ${a+5}'", '$a+5 Hello ${a+5}');
|
||||||
|
//assertEq("'$a+5 Hello ${a+5}'", "" + a + "+5 Hello " + a + 5);
|
||||||
|
headerCode = '';
|
||||||
|
|
||||||
|
assertEq("'hello ${5} world'", 'hello ${5} world');
|
||||||
|
assertEq("'hello ${5}'", 'hello ${5}');
|
||||||
|
|
||||||
|
assertEq('"f".code', "f".code);
|
||||||
|
assertEq('"o".code', "o".code);
|
||||||
|
assertEq('"1".code', "1".code);
|
||||||
|
assertEq('"b".code', "b".code);
|
||||||
|
assertEq('"a".code', "a".code);
|
||||||
|
assertEq('"r".code', "r".code);
|
||||||
|
assertEq('"r".code', "r".code);
|
||||||
|
assertError('"foo".code', Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI))); // multiple chars causes error
|
||||||
|
assertError('"bar".code', Parser.getBaseError(EPreset(INVALID_CHAR_CODE_MULTI))); // multiple chars causes error
|
||||||
|
|
||||||
|
headerCode = '';
|
||||||
|
var a = 103;
|
||||||
|
|
||||||
|
assertEq('String.fromCharCode(65)', String.fromCharCode(65));
|
||||||
|
assertEq('String.fromCharCode(97)', String.fromCharCode(97));
|
||||||
|
assertEq('String.fromCharCode(98)', String.fromCharCode(98));
|
||||||
|
assertEq('String.fromCharCode(99)', String.fromCharCode(99));
|
||||||
|
assertEq('String.fromCharCode(100)', String.fromCharCode(100));
|
||||||
|
assertEq('String.fromCharCode(101)', String.fromCharCode(101));
|
||||||
|
assertEq('String.fromCharCode(102)', String.fromCharCode(102));
|
||||||
|
assertEq('String.fromCharCode(103)', String.fromCharCode(103));
|
||||||
|
assertEq('var a = 103; String.fromCharCode(a)', String.fromCharCode(a));
|
||||||
|
|
||||||
|
assertError("'hello ${}'", Parser.getBaseError(EPreset(EMPTY_INTERPOLATION)));
|
||||||
|
|
||||||
|
assertEq('"".charAt(0)', "".charAt(0));
|
||||||
|
assertEq('"".charAt(1)', "".charAt(1));
|
||||||
|
assertEq('"".charAt(-1)', "".charAt(-1));
|
||||||
|
|
||||||
|
headerCode = '';
|
||||||
|
|
||||||
|
assertEq("'foo1bar'.charCodeAt(0)", 'foo1bar'.charCodeAt(0));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(1)", 'foo1bar'.charCodeAt(1));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(2)", 'foo1bar'.charCodeAt(2));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(3)", 'foo1bar'.charCodeAt(3));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(4)", 'foo1bar'.charCodeAt(4));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(5)", 'foo1bar'.charCodeAt(5));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(6)", 'foo1bar'.charCodeAt(6));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(7)", 'foo1bar'.charCodeAt(7));
|
||||||
|
assertEq("'foo1bar'.charCodeAt(-1)", 'foo1bar'.charCodeAt(-1));
|
||||||
|
assertEq("'foo1bar'.length", 'foo1bar'.length);
|
||||||
|
assertEq("'foo1bar'.split('1')", 'foo1bar'.split('1'));
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
using StringTools;
|
||||||
|
|
||||||
|
class StringToolsCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function getNewInterp() {
|
||||||
|
var interp = super.getNewInterp();
|
||||||
|
interp.variables.set("Std", Std);
|
||||||
|
interp.variables.set("String", String);
|
||||||
|
return interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
Util.runKnownBug("Using StringTools; not working", () -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
class SwitchCase extends TestCase {
|
||||||
|
override function setup() {
|
||||||
|
super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function run() {
|
||||||
|
assertEq("switch(1) { case 1: 1; case 2: 2; default: -1; }", switch(1) { case 1: 1; case 2: 2; default: -1; });
|
||||||
|
assertEq("switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; });
|
||||||
|
assertEq("switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(3) { case 1: 1; case 2: 2; case 3: 3; default: -1; });
|
||||||
|
assertEq("switch(6) { case 1: 1; case 2: 2; case 3: 3; default: -1; }", switch(6) { case 1: 1; case 2: 2; case 3: 3; default: -1; });
|
||||||
|
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-enums.html
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-variable-capture.html (including case _)
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-structure.html
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-array.html
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-guards.html
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-tuples.html
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-extractors.html
|
||||||
|
// TODO: detect https://haxe.org/manual/lf-pattern-matching-exhaustiveness.html
|
||||||
|
// TODO: maybe? https://haxe.org/manual/lf-pattern-matching-unused.html
|
||||||
|
// TODO: add https://haxe.org/manual/lf-pattern-matching-single.html
|
||||||
|
|
||||||
|
assertEq("switch(5) { case 1|4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1|4: 'error'; case 5: 0; default: -1; });
|
||||||
|
assertEq("switch(5) { case 1|4: 'error'; default: -1; }", switch(5) { case 1|4: 'error'; default: -1; });
|
||||||
|
assertEq("switch(5) { case 1,4: 'error'; case 5: 0; default: -1; }", switch(5) { case 1,4: 'error'; case 5: 0; default: -1; });
|
||||||
|
|
||||||
|
Util.runKnownBug("Switch statement with multiple cases in parentheses", () -> {
|
||||||
|
assertEq("switch(5) { case (1|4): 'error'; default: -1; }", switch(5) { case (1|4): 'error'; default: -1; });
|
||||||
|
assertEq("switch(6) { case (1|4|(6|7)): 6; default: -1; }", switch(6) { case (1|4|(6|7)): 6; default: -1; });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override function teardown() {
|
||||||
|
super.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
import hscript.Printer;
|
||||||
|
import hscript.Tools;
|
||||||
|
import hscript.Expr.Error;
|
||||||
|
|
||||||
|
@:access(hscript.Interp)
|
||||||
|
@:access(hscript.Parser)
|
||||||
|
class TestCase extends HScriptRunner {
|
||||||
|
public function assertEq(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
if(message == null)
|
||||||
|
message = script;
|
||||||
|
var result = if(vars != null)
|
||||||
|
executeWithVars(script, vars);
|
||||||
|
else
|
||||||
|
execute(script);
|
||||||
|
if(!Util.assertEq(result, expected, message, pos)) {
|
||||||
|
Sys.println("> " + Printer.convertExprToString(lastExpr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertCompiles(script:String, ?message:String, ?pos:haxe.PosInfos) {
|
||||||
|
if(message == null)
|
||||||
|
message = script;
|
||||||
|
try {
|
||||||
|
var result = Util.parseUnsafe(script);
|
||||||
|
} catch(e:Error) {
|
||||||
|
var e = Printer.getPrintableError(e);
|
||||||
|
Sys.println("# Error trying to compile: " + script);
|
||||||
|
Sys.println("## Error: " + e);
|
||||||
|
return Util.failed();
|
||||||
|
}
|
||||||
|
return Util.passed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertError(script:String, expectedError:Error, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
var expectedError = Printer.getPrintableError(expectedError);
|
||||||
|
if(message == null)
|
||||||
|
message = script;
|
||||||
|
try {
|
||||||
|
var result = if(vars != null)
|
||||||
|
executeWithVarsUnsafe(script, vars);
|
||||||
|
else
|
||||||
|
executeUnsafe(script);
|
||||||
|
Sys.println("# For script: " + script);
|
||||||
|
Sys.println("## Expected error: " + expectedError);
|
||||||
|
Sys.println("## Got result: " + result);
|
||||||
|
Sys.println("> " + Printer.convertExprToString(lastExpr));
|
||||||
|
return Util.failed();
|
||||||
|
} catch(e:hscript.Error) {
|
||||||
|
var e = Printer.getPrintableError(e);
|
||||||
|
if(Type.enumEq(e, expectedError)) {
|
||||||
|
return Util.passed();
|
||||||
|
} else {
|
||||||
|
Sys.println("# For script: " + script);
|
||||||
|
Sys.println("## Expected error: " + expectedError);
|
||||||
|
Sys.println("## Actual error: " + e);
|
||||||
|
return Util.failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Util.failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertEqPrintable(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
if(message == null)
|
||||||
|
message = script;
|
||||||
|
var result = if(vars != null)
|
||||||
|
executeWithVars(script, vars);
|
||||||
|
else
|
||||||
|
execute(script);
|
||||||
|
if(!Util.assertEqPrintable(result, expected, message, pos)) {
|
||||||
|
Sys.println("> " + Printer.convertExprToString(lastExpr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assertNeq(script:String, expected:Dynamic, ?message:String, ?vars:Dynamic, ?pos:haxe.PosInfos) {
|
||||||
|
if(message == null)
|
||||||
|
message = script;
|
||||||
|
var result = if(vars != null)
|
||||||
|
executeWithVars(script, vars);
|
||||||
|
else
|
||||||
|
execute(script);
|
||||||
|
if(!Util.assertNeq(result, expected, message, pos)) {
|
||||||
|
Sys.println("> " + Printer.convertExprToString(lastExpr));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Generic Haxe stuffs to make the project compile
|
||||||
|
-cp src
|
||||||
|
-cp ../
|
||||||
|
-D analyzer-optimize
|
||||||
|
-main Main
|
||||||
|
|
||||||
|
# Libraries (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
|
||||||
|
# --library hscript-improved
|
||||||
|
# --library hxcpp-debug-server
|
||||||
|
|
||||||
|
# Defines (MAKE SURE TO KEEP THIS IN SYNC WITH ALL BUILD FILES)
|
||||||
|
--define hscriptPos
|
||||||
|
--define HXCPP_CHECK_POINTER
|
||||||
|
--define HXCPP_STACK_LINE
|
||||||
|
--define HXCPP_DEBUG_LINK
|
||||||
|
--dce no
|
||||||
|
|
||||||
|
# File Specific Defines
|
||||||
|
--define windows
|
||||||
|
|
||||||
|
# Copying the assets from source code to bin folder
|
||||||
|
# --cmd Xcopy assets\ bin\cpp\assets\ /E /H /C /I /Y
|
||||||
|
--cpp bin/cpp
|
||||||
|
--cmd cd bin/cpp
|
||||||
|
--cmd Main.exe
|
||||||
Reference in New Issue
Block a user