Compare commits

..
71 Commits
Author SHA1 Message Date
ncannasse 2ed6f15fe1 2.3.0 2018-10-08 09:46:15 +02:00
Juraj KirchheimandNicolas Cannasse 575040be65 Fix Haxe 3. (#75) 2018-09-26 18:38:29 +02:00
Juraj KirchheimandNicolas Cannasse 206959adbd Support new function type syntax. (#74) 2018-09-26 14:06:16 +02:00
ncannasse dd1c97b6af more type checking 2018-09-25 17:01:09 +02:00
ncannasse 85463dcff0 implemented apply type parameters 2018-09-25 12:04:35 +02:00
ncannasse 7ff816417e fixes 2018-09-24 15:54:40 +02:00
ncannasse 3fa2f3898a fix 2018-09-24 15:54:24 +02:00
ncannasse 514ce9eec2 more type checks 2018-09-21 10:47:30 +02:00
ncannasse 5bc357b4b3 hscriptPos changes 2018-09-21 10:47:20 +02:00
ncannasse 56ddc3c24e more typing 2018-09-20 18:21:17 +02:00
ncannasse e407b2a307 optimize the ast (eliminate (function(_) e)(null) calls)
treat function literals as synchronized by default
2018-09-19 18:45:07 +02:00
ncannasse 1ac194ed1d more typing 2018-09-19 18:42:06 +02:00
ncannasse 62bf5d4cc9 more typing, added top down inference 2018-09-19 15:11:29 +02:00
ncannasse 27093064a9 added ECheckType, added short lambda parsing 2018-09-18 16:15:15 +02:00
ncannasse 860a3018a7 don't test on haxe 3.2.x anymore 2018-09-18 15:07:07 +02:00
ncannasse eb3992dbdb separate Checker / CheckerTypes 2018-09-18 14:35:36 +02:00
ncannasse 8c5f9cc1d3 more type checking 2018-09-17 11:52:29 +02:00
ncannasse ed78703090 added type checker (partial) 2018-09-12 18:00:34 +02:00
ncannasse 29b61bcee9 fixed invalid char position 2018-09-12 18:00:34 +02:00
Andy Li 0ff49845a7 fix hscript.Bytes with -D hscriptPos (close #64) 2018-05-11 17:58:04 +08:00
Andy Li c17366b2b1 [AppVeyor] update flash install script 2018-05-11 16:39:39 +08:00
Andy Li 5da267fb31 [TravisCI] recompile hxcpp ndll for haxe 3.2.1 2018-05-11 16:18:20 +08:00
Andy Li dffa3345d0 [TravisCI] turn off php deprecated function warning 2018-05-11 16:01:16 +08:00
Andy Li a23c767fb0 [AppVeyor] enable windows update service 2018-05-11 14:05:39 +08:00
Andy Li 5d6ec51302 [CI] testing 3.4.7 2018-05-11 14:01:38 +08:00
Andy Li 585f277bf5 [TravisCI] fix homebrew php and python 2018-05-11 12:55:32 +08:00
Andy Li aae5f431cb use Bytes#getInt32 and BytesBuffer#addInt32
fix test for python and php targets
2018-05-11 11:00:40 +08:00
Andy Li 4eb84e5c32 better pos info in test 2018-05-11 10:48:14 +08:00
Nicolas Cannasse 70b11ec7ad 2.2.0 2018-04-14 12:23:12 +02:00
ncannasse f2944e0bae added rethrow() 2018-03-02 17:26:12 +01:00
ncannasse fdec33d086 support async transform with -D hscriptPos 2018-03-02 17:25:43 +01:00
ncannasse 0702074469 added ECustom error message 2018-03-02 17:22:35 +01:00
ncannasse 6d5cbf6974 correctly handle "return" in sync parts 2018-03-02 11:25:53 +01:00
Nicolas Cannasse 5429a3a4d4 added typeToString() 2018-02-04 22:50:28 +01:00
Nicolas Cannasse c124076279 added typedef parsing support 2018-02-04 20:54:08 +01:00
Nicolas Cannasse 0d1d811d2c use error instead of throw 2018-02-04 12:31:36 +01:00
ncannasse 8c04831339 don't pass return value in SReturn : big problem in JS that does a Std.string on exception value 2017-11-24 12:43:39 +01:00
ncannasse 4a8b2690aa fixed trace() with multiple arguments 2017-11-23 16:14:59 +01:00
ncannasse e2bc716ea7 fix 2017-11-23 15:05:04 +01:00
ncannasse 6c5987f9af fixed error when no inline triggered 2017-11-23 14:05:12 +01:00
ncannasse 1e79880c72 more conformance to haxe syntax wrt mandatory ;
added static and macro accessors
2017-11-23 12:40:50 +01:00
ncannasse f62d891c63 reduce OpNeg(CInt/CFloat) to negative Int/Float like haxe parser does 2017-11-23 12:17:23 +01:00
ncannasse 930cb8853c don't reset line + preprocessor fix 2017-11-23 11:34:44 +01:00
ncannasse 22104ff64e fixed preprocessor bug 2017-11-22 18:41:32 +01:00
ncannasse 17564441b5 added #if/#else support
added function arg default value expr (only if allowTypes, not supported by interp)
allowed local "inline function"
added parseModule
added exponent support in floats (1.5e-3)
2017-11-22 17:30:16 +01:00
ncannasse a9140a4403 bugfix haxe 4 2017-11-21 17:16:30 +01:00
ncannasse ce47ca0a8a fixed break & continue in sync-optimized expression 2017-11-21 15:55:38 +01:00
Alexander KuzmenkoandNicolas Cannasse 852fee170e [php] don't expect iterator to have iterator field (fixes #69) (#71)
* [php] don't expect IntIterator to have a field which does not exist (fixes #69)

* cleanup

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