Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b32dde3c91 | ||
|
|
b9a566350b | ||
|
|
ce9ce674ea | ||
|
|
b8641779bd | ||
|
|
5e620d784e | ||
|
|
013bcfaffb | ||
|
|
2504c96927 | ||
|
|
e2f4d1481e | ||
|
|
ea3d68573b | ||
|
|
c6cb594ef1 | ||
|
|
948123bc7a | ||
|
|
68f5b26af9 | ||
|
|
5d493630b1 | ||
|
|
f4db6faa80 | ||
|
|
e340f7f5ad | ||
|
|
b4d923b74c | ||
|
|
965f334d59 | ||
|
|
54463b78d2 | ||
|
|
600aec1f50 | ||
|
|
bcfbe81979 | ||
|
|
31e8851bcc | ||
|
|
f464767ee7 | ||
|
|
22eb58b3de | ||
|
|
0daee20545 | ||
|
|
d98073dc33 | ||
|
|
fb199e7dea | ||
|
|
6cea0d97c2 | ||
|
|
058dd2e01e | ||
|
|
975d122491 | ||
|
|
eef4c66374 | ||
|
|
5ce4fbd74d | ||
|
|
f4d8cff26b | ||
|
|
46b6b3c151 | ||
|
|
796b217dec | ||
|
|
1ccc35f464 |
+11
-16
@@ -4,11 +4,11 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
sudo: false
|
||||
dist: trusty
|
||||
dist: bionic
|
||||
|
||||
haxe:
|
||||
- "3.4.7"
|
||||
- "4.0.3"
|
||||
- development
|
||||
|
||||
addons:
|
||||
@@ -19,27 +19,20 @@ addons:
|
||||
- g++-multilib
|
||||
# Python
|
||||
- python3
|
||||
# PHP
|
||||
- php-cli
|
||||
- php-mbstring
|
||||
# C#
|
||||
- mono-devel
|
||||
- mono-mcs
|
||||
# Flash
|
||||
- libglib2.0
|
||||
- libfreetype6
|
||||
|
||||
install:
|
||||
# os-specific config
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||
export DISPLAY=:99.0;
|
||||
export AUDIODEV=null;
|
||||
phpenv global 7.0;
|
||||
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
|
||||
brew update;
|
||||
brew install mono;
|
||||
brew upgrade python;
|
||||
brew install php;
|
||||
fi
|
||||
# Download and setup a standalone flash player debugger
|
||||
- haxe flash/install.hxml
|
||||
# Install haxelibs
|
||||
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ]; then
|
||||
haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git;
|
||||
@@ -67,8 +60,10 @@ script:
|
||||
- haxe bin/build-neko.hxml && neko bin/Test.n
|
||||
- haxe bin/build-js.hxml && node bin/Test.js
|
||||
- haxe bin/build-java.hxml && java -jar bin/Test.jar
|
||||
- haxe bin/build-cs.hxml && mono bin/bin/Test.exe
|
||||
# Only test C# for Haxe 4+
|
||||
- if [[ $(haxe -version 2&>1 | head -c 1) > 3 ]]; then
|
||||
haxe bin/build-cs.hxml && mono bin/bin/Test.exe;
|
||||
fi
|
||||
- haxe bin/build-cpp.hxml && ./bin/Test
|
||||
- haxe bin/build-flash.hxml -D fdb && haxe flash/run.hxml bin/Test.swf
|
||||
- haxe bin/build-php.hxml && php bin/index.php
|
||||
- haxe bin/build-php.hxml -D php7 && php bin/index.php
|
||||
- haxe bin/build-python.hxml && python3 bin/Test.py
|
||||
|
||||
@@ -68,24 +68,36 @@ You can use `parser.allowMetadata` to parse metadata before expressions on in an
|
||||
|
||||
You can use `new hscript.Macro(pos).convert(ast)` to convert an hscript AST to a Haxe macros one.
|
||||
|
||||
You can use `hscript.Checker` in order to type check and even get completion, using `haxe -xml` output for type information.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
Compared to Haxe, limitations are :
|
||||
|
||||
- no type declarations (classes, enums, typedefs) : only expressions
|
||||
- `switch` construct is supported but not pattern matching (no variable capture, we use strict equality to compare `case` values and `switch` value)
|
||||
- only one variable declaration is allowed in `var`
|
||||
- the parser supports optional types for `var` and `function` if `allowTypes` is set, but the interpreter ignores them
|
||||
- you can enable per-expression position tracking by compiling with `-D hscriptPos`
|
||||
- you can parse some type declarations (import, class, typedef, etc.) with parseModule
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
In order to install Haxe Script, use `haxelib install hscript` and compile your program with `-lib hscript`.
|
||||
|
||||
There are only three files in hscript :
|
||||
These are the main required files in hscript :
|
||||
|
||||
- `hscript.Expr` : contains enums declarations
|
||||
- `hscript.Parser` : a small parser that turns a string into an expression structure (AST)
|
||||
- `hscript.Interp` : a small interpreter that execute the AST and returns the latest evaluated value
|
||||
|
||||
Some other optional files :
|
||||
|
||||
- `hscript.Async` : converts Expr into asynchronous version
|
||||
- `hscript.Bytes` : Expr serializer/unserializer
|
||||
- `hscript.Checker` : type checking and completion for hscript Expr
|
||||
- `hscript.Macro` : convert Haxe macro into hscript Expr
|
||||
- `hscript.Printer` : convert hscript Expr to String
|
||||
- `hscript.Tools` : utility functions (map/iter)
|
||||
|
||||
@@ -4,6 +4,7 @@ import hscript.Macro;
|
||||
import hscript.Tools;
|
||||
import hscript.Async;
|
||||
import hscript.Printer;
|
||||
import hscript.Checker;
|
||||
import haxe.unit.*;
|
||||
|
||||
class Test extends TestCase {
|
||||
|
||||
+1
-9
@@ -8,17 +8,11 @@ install:
|
||||
# http://help.appveyor.com/discussions/problems/5616-not-able-to-build-due-to-problem-in-chocolateyinstallps1
|
||||
- ps: Set-Service wuauserv -StartupType Manual
|
||||
# Install the haxe chocolatey package (https://chocolatey.org/packages/haxe)
|
||||
- cinst haxe -version 3.4.7 -y
|
||||
- cinst haxe -version 4.0.3 -y
|
||||
- RefreshEnv
|
||||
# Setup haxelib
|
||||
- mkdir "%HAXELIB_ROOT%"
|
||||
- haxelib setup "%HAXELIB_ROOT%"
|
||||
# Install test dependencies
|
||||
- ps: Set-Service wuauserv -StartupType Manual
|
||||
- cinst php -y
|
||||
- RefreshEnv
|
||||
# Download and setup a standalone flash player debugger
|
||||
- haxe flash/install.hxml
|
||||
# Install project dependencies
|
||||
- haxelib install hxcpp > log.txt || type log.txt && cmd /C exit 1
|
||||
- haxelib install hxjava > log.txt || type log.txt && cmd /C exit 1
|
||||
@@ -39,6 +33,4 @@ test_script:
|
||||
- haxe bin/build-java.hxml && java -jar bin\Test.jar
|
||||
- haxe bin/build-cs.hxml && bin\bin\Test.exe
|
||||
- haxe bin/build-cpp.hxml && bin\Test.exe
|
||||
- haxe bin/build-flash.hxml -D fdb && haxe flash\run.hxml bin\Test.swf
|
||||
- haxe bin/build-php.hxml && php bin\index.php
|
||||
- haxe bin/build-python.hxml && C:\Python34-x64\python.exe bin\Test.py
|
||||
@@ -1,5 +0,0 @@
|
||||
*.exe
|
||||
*.app
|
||||
flashplayerdebugger
|
||||
LGPL/
|
||||
license.pdf
|
||||
@@ -1,91 +0,0 @@
|
||||
import Sys.*;
|
||||
import sys.FileSystem.*;
|
||||
import sys.io.File.*;
|
||||
import haxe.*;
|
||||
import haxe.io.*;
|
||||
|
||||
class Install {
|
||||
// https://www.adobe.com/support/flashplayer/downloads.html
|
||||
static var fpDownload(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flash_player_sa_linux_debug.x86_64.tar.gz";
|
||||
case "Mac":
|
||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.dmg";
|
||||
case "Windows":
|
||||
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.exe";
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
||||
static var mmcfg(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), "mm.cfg"]);
|
||||
case "Mac":
|
||||
"/Library/Application Support/Macromedia/mm.cfg";
|
||||
case "Windows":
|
||||
Path.join([getEnv("HOMEDRIVE") + getEnv("HOMEPATH"), "mm.cfg"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
// http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7c95.html
|
||||
static var fpTrust(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/#Security/FlashPlayerTrust"]);
|
||||
case "Mac":
|
||||
"/Library/Application Support/Macromedia/FlashPlayerTrust";
|
||||
case "Windows":
|
||||
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "#Security", "FlashPlayerTrust"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
static function main() {
|
||||
switch (systemName()) {
|
||||
case "Linux":
|
||||
// Download and unzip flash player
|
||||
if (command("wget", [fpDownload]) != 0)
|
||||
throw "failed to download flash player";
|
||||
if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0)
|
||||
throw "failed to extract flash player";
|
||||
deleteFile(Path.withoutDirectory(fpDownload));
|
||||
case "Mac":
|
||||
if (command("brew", ["cask", "install", "flash-player-debugger"]) != 0)
|
||||
throw "failed to install flash-player-debugger";
|
||||
case "Windows":
|
||||
// Download flash player
|
||||
download(fpDownload, "flash\\flashplayer.exe");
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
|
||||
|
||||
// Create a configuration file so the trace log is enabled
|
||||
createDirectory(Path.directory(mmcfg));
|
||||
saveContent(mmcfg, "ErrorReportingEnable=1\nTraceOutputFileEnable=1");
|
||||
|
||||
// Add the current directory as trusted, so exit() can be used
|
||||
createDirectory(fpTrust);
|
||||
saveContent(Path.join([fpTrust, "test.cfg"]), getCwd());
|
||||
}
|
||||
static function download(url:String, saveAs:String):Void {
|
||||
var http = new Http(url);
|
||||
http.onError = function(e) {
|
||||
throw e;
|
||||
};
|
||||
http.customRequest(false, write(saveAs));
|
||||
}
|
||||
static function createDirectory(dir:String):Void {
|
||||
try {
|
||||
sys.FileSystem.createDirectory(dir);
|
||||
} catch(e:Dynamic) {
|
||||
switch (systemName()) {
|
||||
case "Mac", "Linux":
|
||||
if (command("sudo", ["mkdir", "-p", dir]) != 0)
|
||||
throw 'cannot create $dir';
|
||||
if (command("sudo", ["chmod", "a+rw", dir]) != 0)
|
||||
throw 'cannot set permission of $dir';
|
||||
case _:
|
||||
throw 'cannot create $dir: $e';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import Sys.*;
|
||||
import sys.FileSystem.*;
|
||||
import sys.io.File.*;
|
||||
import haxe.io.*;
|
||||
|
||||
class Run {
|
||||
// https://helpx.adobe.com/flash-player/kb/configure-debugger-version-flash-player.html
|
||||
static var flashlog(default, never) = switch (systemName()) {
|
||||
case "Linux":
|
||||
Path.join([getEnv("HOME"), ".macromedia/Flash_Player/Logs/flashlog.txt"]);
|
||||
case "Mac":
|
||||
Path.join([getEnv("HOME"), "Library/Preferences/Macromedia/Flash Player/Logs/flashlog.txt"]);
|
||||
case "Windows":
|
||||
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "Logs", "flashlog.txt"]);
|
||||
case _:
|
||||
throw "unsupported system";
|
||||
}
|
||||
static function main() {
|
||||
var args = args();
|
||||
var swf = args[0];
|
||||
var exitCode = switch (systemName()) {
|
||||
case "Linux":
|
||||
// The flash player has some issues with unexplained crashes,
|
||||
// but if it runs about 16 times, it should succeed one of those...
|
||||
var c = -1;
|
||||
for (i in 0...16) {
|
||||
if ((c = command("xvfb-run", ["-a", "flash/flashplayerdebugger", swf])) == 0)
|
||||
break;
|
||||
println('retry... (${i+1})');
|
||||
sleep(1.5);
|
||||
}
|
||||
c;
|
||||
case "Mac":
|
||||
command("/Applications/Flash Player Debugger.app/Contents/MacOS/Flash Player Debugger", [fullPath(swf)]);
|
||||
case "Windows":
|
||||
command("flash\\flashplayer.exe", [fullPath(swf)]);
|
||||
case _:
|
||||
throw "unsupported platform";
|
||||
}
|
||||
if (exists(flashlog))
|
||||
println(getContent(flashlog));
|
||||
else {
|
||||
println('does not exist: $flashlog');
|
||||
var parts = Path.normalize(flashlog).split("/");
|
||||
println(parts);
|
||||
for (i in 0...parts.length-1) {
|
||||
var path = parts.splice(0, i+1).join("/");
|
||||
println('ls $path');
|
||||
command("ls", [path]);
|
||||
}
|
||||
}
|
||||
exit(exitCode);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
-cp flash
|
||||
--run Install
|
||||
@@ -1,2 +0,0 @@
|
||||
-cp flash
|
||||
--run Run
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
"url": "https://github.com/HaxeFoundation/hscript",
|
||||
"license": "MIT",
|
||||
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
|
||||
"version": "2.3.0",
|
||||
"releasenote": "",
|
||||
"version": "2.4.0",
|
||||
"releasenote": "Haxe 4.0 final + type checking/completion (hscript.Checker)",
|
||||
"contributors": ["ncannasse"]
|
||||
}
|
||||
+45
-20
@@ -22,9 +22,16 @@
|
||||
package hscript;
|
||||
import hscript.Expr;
|
||||
|
||||
|
||||
enum VarMode {
|
||||
Defined;
|
||||
ForceSync;
|
||||
}
|
||||
|
||||
class Async {
|
||||
|
||||
var varNames : Array<String>;
|
||||
var definedVars : Array<{ n : String, prev : Null<VarMode> }>;
|
||||
var vars : Map<String,VarMode>;
|
||||
var currentFun : String;
|
||||
var currentLoop : Expr;
|
||||
var currentBreak : Expr -> Expr;
|
||||
@@ -68,15 +75,33 @@ class Async {
|
||||
return a.build(e, topLevelSync);
|
||||
}
|
||||
|
||||
public dynamic function getTopLevelEnd() {
|
||||
return ignore();
|
||||
}
|
||||
|
||||
public function build( e : Expr, topLevelSync = false ) {
|
||||
if( topLevelSync ) {
|
||||
return buildSync(e,null);
|
||||
} else {
|
||||
var nothing = ignore();
|
||||
return new Async().toCps(e, nothing, nothing);
|
||||
var end = getTopLevelEnd();
|
||||
return toCps(e, end, end);
|
||||
}
|
||||
}
|
||||
|
||||
function defineVar( v : String, mode ) {
|
||||
definedVars.push({ n : v, prev : vars.get(v) });
|
||||
vars.set(v, mode);
|
||||
}
|
||||
|
||||
function lookupFunctions( el : Array<Expr> ) {
|
||||
for( e in el )
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_, _, name, _) if( name != null ): defineVar(name, Defined);
|
||||
case EMeta("sync",_,expr(_) => EFunction(_,_,name,_)) if( name != null ): defineVar(name, ForceSync);
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
function buildSync( e : Expr, exit : Expr ) : Expr {
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_,_,name,_):
|
||||
@@ -85,16 +110,14 @@ class Async {
|
||||
return e;
|
||||
case EBlock(el):
|
||||
var v = saveVars();
|
||||
for( e in el )
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_, _, name, _) if( name != null ): varNames.push(name);
|
||||
default:
|
||||
}
|
||||
lookupFunctions(el);
|
||||
var e = block([for(e in el) buildSync(e,exit)], e);
|
||||
restoreVars(v);
|
||||
return e;
|
||||
case EMeta("async", _, e):
|
||||
return toCps(e, ignore(), ignore());
|
||||
case EMeta("sync", args, ef = expr(_) => EFunction(fargs, body, name, ret)):
|
||||
return mk(EMeta("sync",args,mk(EFunction(fargs, buildSync(body,null), name, ret),ef)),e);
|
||||
case EBreak if( currentBreak != null ):
|
||||
return currentBreak(e);
|
||||
case EContinue if( currentLoop != null ):
|
||||
@@ -115,7 +138,8 @@ class Async {
|
||||
}
|
||||
|
||||
public function new() {
|
||||
varNames = [];
|
||||
vars = new Map();
|
||||
definedVars = [];
|
||||
}
|
||||
|
||||
function ignore(?e) : Expr {
|
||||
@@ -199,11 +223,14 @@ class Async {
|
||||
}
|
||||
|
||||
function saveVars() {
|
||||
return varNames.length;
|
||||
return definedVars.length;
|
||||
}
|
||||
|
||||
function restoreVars(k) {
|
||||
while( varNames.length > k ) varNames.pop();
|
||||
while( definedVars.length > k ) {
|
||||
var v = definedVars.pop();
|
||||
if( v.prev == null ) vars.remove(v.n) else vars.set(v.n, v.prev);
|
||||
}
|
||||
}
|
||||
|
||||
public function toCps( e : Expr, rest : Expr, exit : Expr ) : Expr {
|
||||
@@ -213,12 +240,7 @@ class Async {
|
||||
case EBlock(el):
|
||||
var el = el.copy();
|
||||
var vold = saveVars();
|
||||
// local recursion
|
||||
for( e in el )
|
||||
switch( expr(e) ) {
|
||||
case EFunction(_, _, name, null): if( name != null ) varNames.push(name);
|
||||
default:
|
||||
}
|
||||
lookupFunctions(el);
|
||||
while( el.length > 0 ) {
|
||||
var e = toCps(el.pop(), rest, exit);
|
||||
rest = ignore(e);
|
||||
@@ -228,9 +250,9 @@ class Async {
|
||||
case EFunction(args, body, name, t):
|
||||
var vold = saveVars();
|
||||
if( name != null )
|
||||
varNames.push(name);
|
||||
defineVar(name, Defined);
|
||||
for( a in args )
|
||||
varNames.push(a.name);
|
||||
defineVar(a.name, Defined);
|
||||
args.unshift( { name : "_onEnd", t : null } );
|
||||
var frest = ident("_onEnd",e);
|
||||
var oldFun = currentFun;
|
||||
@@ -251,7 +273,8 @@ class Async {
|
||||
var args = [for( a in args ) fun("_rest", toCps(block([a],a), ident("_rest",a), exit))];
|
||||
return call(ident("split",e), [rest, mk(EArrayDecl(args),e)],e);
|
||||
case ECall(expr(_) => EIdent(i), args):
|
||||
return makeCall( ident( varNames.indexOf(i) < 0 ? "a_" + i : i,e) , args, rest, exit);
|
||||
var mode = vars.get(i);
|
||||
return makeCall( ident( mode != null ? i : "a_" + i,e) , args, rest, exit, mode == ForceSync);
|
||||
case ECall(expr(_) => EField(e, f), args):
|
||||
return makeCall(field(e,"a_"+f,e), args, rest, exit);
|
||||
case EFor(v, eit, eloop):
|
||||
@@ -391,6 +414,8 @@ class Async {
|
||||
return toCps(v, mk(EFunction([ { name : "_c", t : null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
|
||||
case EThrow(v):
|
||||
return toCps(v, mk(EFunction([ { name : "_v", t : null } ], mk(EThrow(v),v)), v), exit);
|
||||
case EMeta(name,_,e) if( name.charCodeAt(0) == ":".code ): // ignore custom ":" metadata
|
||||
return toCps(e, rest, exit);
|
||||
//case EDoWhile(_), ETry(_), ECall(_):
|
||||
default:
|
||||
throw "Unsupported async expression " + Printer.toString(e);
|
||||
|
||||
+296
-52
@@ -23,6 +23,7 @@ enum TType {
|
||||
TAbstract( a : CAbstract, args : Array<TType> );
|
||||
TFun( args : Array<{ name : String, opt : Bool, t : TType }>, ret : TType );
|
||||
TAnon( fields : Array<{ name : String, opt : Bool, t : TType }> );
|
||||
TLazy( f : Void -> TType );
|
||||
}
|
||||
|
||||
private enum WithType {
|
||||
@@ -53,13 +54,15 @@ typedef CClass = {> CNamedType,
|
||||
|
||||
typedef CField = {
|
||||
var isPublic : Bool;
|
||||
var canWrite : Bool;
|
||||
var complete : Bool;
|
||||
var params : Array<TType>;
|
||||
var name : String;
|
||||
var t : TType;
|
||||
}
|
||||
|
||||
typedef CEnum = {> CNamedType,
|
||||
var constructors : Map<String,TType>;
|
||||
var constructors : Array<{ name : String, ?args : Array<{ name : String, opt : Bool, t : TType }> }>;
|
||||
}
|
||||
|
||||
typedef CTypedef = {> CNamedType,
|
||||
@@ -67,6 +70,16 @@ typedef CTypedef = {> CNamedType,
|
||||
}
|
||||
|
||||
typedef CAbstract = {> CNamedType,
|
||||
var t : TType;
|
||||
}
|
||||
|
||||
class Completion {
|
||||
public var expr : Expr;
|
||||
public var t : TType;
|
||||
public function new(expr,t) {
|
||||
this.expr = expr;
|
||||
this.t = t;
|
||||
}
|
||||
}
|
||||
|
||||
@:allow(hscript.Checker)
|
||||
@@ -118,13 +131,17 @@ class CheckerTypes {
|
||||
for( f in c.fields ) {
|
||||
if( f.isOverride || f.name.substr(0,4) == "get_" || f.name.substr(0,4) == "set_" ) continue;
|
||||
var skip = false;
|
||||
for( m in f.meta )
|
||||
if( m.name == ":noScript" ) {
|
||||
var complete = !StringTools.startsWith(f.name,"__"); // __uid, etc. (no metadata in such fields)
|
||||
for( m in f.meta ) {
|
||||
if( m.name == ":noScript" ) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if( m.name == ":noCompletion" )
|
||||
complete = false;
|
||||
}
|
||||
if( skip ) continue;
|
||||
var fl : CField = { isPublic : f.isPublic, params : [], name : f.name, t : null };
|
||||
var fl : CField = { isPublic : f.isPublic, canWrite : f.set.match(RNormal | RCall(_) | RDynamic), complete : complete, params : [], name : f.name, t : null };
|
||||
for( p in f.params ) {
|
||||
var pt = TParam(p);
|
||||
var key = f.name+"."+p;
|
||||
@@ -148,12 +165,14 @@ class CheckerTypes {
|
||||
var en : CEnum = {
|
||||
name : e.path,
|
||||
params : [],
|
||||
constructors: new Map(),
|
||||
constructors: [],
|
||||
};
|
||||
for( p in e.params )
|
||||
en.params.push(TParam(p));
|
||||
todo.push(function() {
|
||||
localParams = [for( t in en.params ) e.path+"."+Checker.typeStr(t) => t];
|
||||
for( c in e.constructors )
|
||||
en.constructors.push({ name : c.name, args : c.args == null ? null : [for( a in c.args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }] });
|
||||
localParams = null;
|
||||
});
|
||||
types.set(en.name, CTEnum(en));
|
||||
@@ -179,11 +198,13 @@ class CheckerTypes {
|
||||
var ta : CAbstract = {
|
||||
name : a.path,
|
||||
params : [],
|
||||
t : null,
|
||||
};
|
||||
for( p in a.params )
|
||||
ta.params.push(TParam(p));
|
||||
todo.push(function() {
|
||||
localParams = [for( t in ta.params ) a.path+"."+Checker.typeStr(t) => t];
|
||||
ta.t = makeXmlType(a.athis);
|
||||
localParams = null;
|
||||
});
|
||||
types.set(a.path, CTAbstract(ta));
|
||||
@@ -220,8 +241,18 @@ class CheckerTypes {
|
||||
if( t != null ) return t;
|
||||
}
|
||||
var t = resolve(name,args);
|
||||
if( t == null )
|
||||
if( t == null ) {
|
||||
var pack = name.split(".");
|
||||
if( pack.length > 1 ) {
|
||||
// bugfix for some args reported as pack._Name.Name while they are not private
|
||||
var priv = pack[pack.length-2];
|
||||
if( priv.charCodeAt(0) == "_".code ) {
|
||||
pack.remove(priv);
|
||||
return getType(pack.join("."), args);
|
||||
}
|
||||
}
|
||||
return TUnresolved(name); // most likely private class
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -250,14 +281,29 @@ class Checker {
|
||||
var locals : Map<String,TType>;
|
||||
var globals : Map<String,TType> = new Map();
|
||||
var events : Map<String,TType> = new Map();
|
||||
var currentFunType : TType;
|
||||
var isCompletion : Bool;
|
||||
var allowDefine : Bool;
|
||||
public var allowAsync : Bool;
|
||||
public var allowReturn : Null<TType>;
|
||||
public var allowGlobalsDefine : Bool;
|
||||
public var allowUntypedMeta : Bool;
|
||||
|
||||
public function new( ?types ) {
|
||||
if( types == null ) types = new CheckerTypes();
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
public function setGlobals( cl : CClass ) {
|
||||
for( f in cl.fields )
|
||||
if( f.isPublic )
|
||||
setGlobal(f.name, f.params.length == 0 ? f.t : TLazy(function() return apply(f.t,f.params,[for( i in 0...f.params.length) makeMono()])));
|
||||
}
|
||||
|
||||
public function removeGlobal( name : String ) {
|
||||
globals.remove(name);
|
||||
}
|
||||
|
||||
public function setGlobal( name : String, type : TType ) {
|
||||
globals.set(name, type);
|
||||
}
|
||||
@@ -270,9 +316,54 @@ class Checker {
|
||||
return globals;
|
||||
}
|
||||
|
||||
public function check( expr : Expr, ?withType : WithType ) {
|
||||
function typeArgs( args : Array<Argument>, pos : Expr ) {
|
||||
return [for( i in 0...args.length ) {
|
||||
var a = args[i];
|
||||
var at = a.t == null ? makeMono() : makeType(a.t, pos);
|
||||
{ name : a.name, opt : a.opt, t : at };
|
||||
}];
|
||||
}
|
||||
|
||||
public function check( expr : Expr, ?withType : WithType, ?isCompletion = false ) {
|
||||
if( withType == null ) withType = NoValue;
|
||||
locals = new Map();
|
||||
allowDefine = allowGlobalsDefine;
|
||||
this.isCompletion = isCompletion;
|
||||
switch( edef(expr) ) {
|
||||
case EBlock(el):
|
||||
var delayed = [];
|
||||
var last = TVoid;
|
||||
for( e in el ) {
|
||||
while( true ) {
|
||||
switch( edef(e) ) {
|
||||
case EMeta(_,_,e2): e = e2;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
switch( edef(e) ) {
|
||||
case EFunction(args,_,name,ret) if( name != null ):
|
||||
var tret = ret == null ? makeMono() : makeType(ret, e);
|
||||
var ft = TFun(typeArgs(args,e),tret);
|
||||
locals.set(name, ft);
|
||||
delayed.push(function() {
|
||||
currentFunType = ft;
|
||||
typeExpr(e, NoValue);
|
||||
return ft;
|
||||
});
|
||||
default:
|
||||
for( f in delayed ) f();
|
||||
delayed = [];
|
||||
if( el[el.length-1] == e )
|
||||
last = typeExpr(e, withType);
|
||||
else
|
||||
typeExpr(e, NoValue);
|
||||
}
|
||||
}
|
||||
for( f in delayed )
|
||||
last = f();
|
||||
return last;
|
||||
default:
|
||||
}
|
||||
return typeExpr(expr,withType);
|
||||
}
|
||||
|
||||
@@ -287,7 +378,7 @@ class Checker {
|
||||
inline function error( msg : String, curExpr : Expr ) {
|
||||
var e = ECustom(msg);
|
||||
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
|
||||
throw e;
|
||||
if( !isCompletion ) throw e;
|
||||
}
|
||||
|
||||
function saveLocals() {
|
||||
@@ -297,8 +388,11 @@ class Checker {
|
||||
function makeType( t : CType, e : Expr ) : TType {
|
||||
return switch (t) {
|
||||
case CTPath(path, params):
|
||||
var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(t,e)]);
|
||||
if( ct == null ) error("Unknown type "+path, e);
|
||||
var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(p,e)]);
|
||||
if( ct == null ) {
|
||||
error("Unknown type "+path, e);
|
||||
ct = TDynamic;
|
||||
}
|
||||
return ct;
|
||||
case CTFun(args, ret):
|
||||
var i = 0;
|
||||
@@ -307,6 +401,10 @@ class Checker {
|
||||
return TAnon([for( f in fields ) { name : f.name, opt : false, t : makeType(f.t,e) }]);
|
||||
case CTParent(t):
|
||||
return makeType(t,e);
|
||||
case CTNamed(n, t):
|
||||
return makeType(t,e);
|
||||
case CTOpt(t):
|
||||
return makeType(t,e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,7 +414,11 @@ class Checker {
|
||||
case TMono(r): r.r == null ? "Unknown" : typeStr(r.r);
|
||||
case TInst(c, args): c.name + makeArgs(args);
|
||||
case TEnum(e, args): e.name + makeArgs(args);
|
||||
case TType(t, args): t.name + makeArgs(args);
|
||||
case TType(t, args):
|
||||
if( t.name == "hscript.TypeCheck" )
|
||||
typeStr(args[1]);
|
||||
else
|
||||
t.name + makeArgs(args);
|
||||
case TAbstract(a, args): a.name + makeArgs(args);
|
||||
case TFun(args, ret): "(" + [for( a in args ) (a.opt?"?":"")+(a.name == "" ? "" : a.name+":")+typeStr(a.t)].join(", ")+") -> "+typeStr(ret);
|
||||
case TAnon(fields): "{" + [for( f in fields ) (f.opt?"?":"")+f.name+":"+typeStr(f.t)].join(", ")+"}";
|
||||
@@ -505,6 +607,25 @@ class Checker {
|
||||
if( !typeEq(pl1[i],pl2[i]) )
|
||||
return false;
|
||||
return true;
|
||||
case [TInst(cl1,pl1),TAnon(fl)]:
|
||||
for( i in 0...fl.length ) {
|
||||
var f2 = fl[i];
|
||||
var f1 = null;
|
||||
var cl = cl1;
|
||||
while( true ) {
|
||||
f1 = cl.fields.get(f2.name);
|
||||
if( f1 != null ) break;
|
||||
if( cl.superClass == null )
|
||||
return false;
|
||||
cl = switch( cl.superClass ) {
|
||||
case TInst(c,_): c;
|
||||
default: throw "assert";
|
||||
}
|
||||
}
|
||||
if( !typeEq(f1.t,f2.t) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case [TInt, TFloat]:
|
||||
return true;
|
||||
case [TFun(_), TAbstract({ name : "haxe.Function" },_)]:
|
||||
@@ -514,12 +635,12 @@ class Checker {
|
||||
return typeEq(t1,t2);
|
||||
}
|
||||
|
||||
function unify( t1 : TType, t2 : TType, e : Expr ) {
|
||||
public function unify( t1 : TType, t2 : TType, e : Expr ) {
|
||||
if( !tryUnify(t1,t2) )
|
||||
error(typeStr(t1)+" should be "+typeStr(t2),e);
|
||||
}
|
||||
|
||||
function apply( t : TType, params : Array<TType>, args : Array<TType> ) {
|
||||
public function apply( t : TType, params : Array<TType>, args : Array<TType> ) {
|
||||
if( args.length != params.length ) throw "Invalid number of type parameters";
|
||||
if( args.length == 0 )
|
||||
return t;
|
||||
@@ -557,14 +678,17 @@ class Checker {
|
||||
return TFun([for( a in args ) { name : a.name, opt : a.opt, t : f(a.t) }], f(ret));
|
||||
case TAnon(fields):
|
||||
return TAnon([for( af in fields ) { name : af.name, opt : af.opt, t : f(af.t) }]);
|
||||
case TLazy(l):
|
||||
return f(l());
|
||||
}
|
||||
}
|
||||
|
||||
public function follow( t : TType ) {
|
||||
return switch( t ) {
|
||||
case TMono(r): if( r.r != null ) follow(r.r) else t;
|
||||
case TType(t,args): apply(t.t, t.params, args);
|
||||
case TType(t,args): follow(apply(t.t, t.params, args));
|
||||
case TNull(t): follow(t);
|
||||
case TLazy(f): follow(f());
|
||||
default: t;
|
||||
}
|
||||
}
|
||||
@@ -590,7 +714,7 @@ class Checker {
|
||||
return fields;
|
||||
}
|
||||
|
||||
function getField( t : TType, f : String, e : Expr ) {
|
||||
function getField( t : TType, f : String, e : Expr, forWrite = false ) {
|
||||
switch( follow(t) ) {
|
||||
case TInst(c, args):
|
||||
var cf = c.fields.get(f);
|
||||
@@ -598,19 +722,23 @@ class Checker {
|
||||
cf = c.fields.get("a_"+f);
|
||||
if( cf != null ) {
|
||||
var isPublic = true; // consider a_ prefixed as script specific
|
||||
cf = { isPublic : isPublic, params : cf.params, name : cf.name, t : unasync(cf.t) };
|
||||
cf = { isPublic : isPublic, canWrite : false, params : cf.params, name : cf.name, t : unasync(cf.t), complete : cf.complete };
|
||||
if( cf.t == null ) cf = null;
|
||||
}
|
||||
}
|
||||
if( cf == null ) {
|
||||
if( c.superClass == null ) return null;
|
||||
var ft = getField(c.superClass, f, e);
|
||||
var ft = getField(c.superClass, f, e, forWrite);
|
||||
if( ft != null ) ft = apply(ft, c.params, args);
|
||||
return ft;
|
||||
}
|
||||
if( !cf.isPublic )
|
||||
error("Can't access private field "+f+" on "+c.name, e);
|
||||
return apply(cf.t, c.params, args);
|
||||
if( forWrite && !cf.canWrite )
|
||||
error("Can't write readonly field "+f+" on "+c.name, e);
|
||||
var t = cf.t;
|
||||
if( cf.params != null ) t = apply(t, cf.params, [for( i in 0...cf.params.length ) makeMono()]);
|
||||
return apply(t, c.params, args);
|
||||
case TDynamic:
|
||||
return makeMono();
|
||||
case TAnon(fields):
|
||||
@@ -663,7 +791,28 @@ class Checker {
|
||||
return t.match(TInst({name:"String"},_));
|
||||
}
|
||||
|
||||
function onCompletion( expr : Expr, t : TType ) {
|
||||
if( isCompletion ) throw new Completion(expr, t);
|
||||
}
|
||||
|
||||
function typeField( o : Expr, f : String, expr : Expr, forWrite : Bool ) {
|
||||
var ot = typeExpr(o, Value);
|
||||
if( f == null )
|
||||
onCompletion(expr, ot);
|
||||
var ft = getField(ot, f, expr, forWrite);
|
||||
if( ft == null ) {
|
||||
error(typeStr(ot)+" has no field "+f, expr);
|
||||
ft = TDynamic;
|
||||
}
|
||||
return ft;
|
||||
}
|
||||
|
||||
function typeExpr( expr : Expr, withType : WithType ) : TType {
|
||||
if( expr == null && isCompletion )
|
||||
return switch( withType ) {
|
||||
case WithType(t): t;
|
||||
default: TDynamic;
|
||||
}
|
||||
switch( edef(expr) ) {
|
||||
case EConst(c):
|
||||
return switch (c) {
|
||||
@@ -675,7 +824,12 @@ class Checker {
|
||||
var l = locals.get(v);
|
||||
if( l != null ) return l;
|
||||
var g = globals.get(v);
|
||||
if( g != null ) return g;
|
||||
if( g != null ) {
|
||||
return switch( g ) {
|
||||
case TLazy(f): f();
|
||||
default: g;
|
||||
}
|
||||
}
|
||||
if( allowAsync ) {
|
||||
g = globals.get("a_"+v);
|
||||
if( g != null ) g = unasync(g);
|
||||
@@ -689,6 +843,7 @@ class Checker {
|
||||
case "trace":
|
||||
return TDynamic;
|
||||
default:
|
||||
if( isCompletion) return TDynamic;
|
||||
error("Unknown identifier "+v, expr);
|
||||
}
|
||||
case EBlock(el):
|
||||
@@ -714,32 +869,34 @@ class Checker {
|
||||
case TFun(args, ret):
|
||||
for( i in 0...params.length ) {
|
||||
var a = args[i];
|
||||
if( a == null ) error("Too many arguments", params[i]);
|
||||
var t = typeExpr(params[i], WithType(a.t));
|
||||
if( a == null ) {
|
||||
error("Too many arguments", params[i]);
|
||||
break;
|
||||
}
|
||||
var t = typeExpr(params[i], a == null ? Value : WithType(a.t));
|
||||
unify(t, a.t, params[i]);
|
||||
}
|
||||
for( i in params.length...args.length )
|
||||
if( !args[i].opt )
|
||||
error("Missing argument '"+args[i].name+"'", expr);
|
||||
error("Missing argument "+args[i].name+":"+typeStr(args[i].t), expr);
|
||||
return ret;
|
||||
case TDynamic:
|
||||
for( p in params ) typeExpr(p,Value);
|
||||
return TDynamic;
|
||||
return makeMono();
|
||||
default:
|
||||
error(typeStr(ft)+" cannot be called", e);
|
||||
return makeMono();
|
||||
}
|
||||
case EField(o, f):
|
||||
var ot = typeExpr(o, Value);
|
||||
var ft = getField(ot, f, expr);
|
||||
if( ft == null )
|
||||
error(typeStr(ot)+" has no field "+f, expr);
|
||||
return ft;
|
||||
return typeField(o,f,expr,false);
|
||||
case ECheckType(v, t):
|
||||
var ct = makeType(t, expr);
|
||||
var vt = typeExpr(v, WithType(ct));
|
||||
unify(vt, ct, v);
|
||||
return ct;
|
||||
case EMeta(_, _, e):
|
||||
case EMeta(m, _, e):
|
||||
if( m == ":untyped" && allowUntypedMeta )
|
||||
return makeMono();
|
||||
return typeExpr(e, withType);
|
||||
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
|
||||
typeExprWith(cond, TBool);
|
||||
@@ -764,8 +921,11 @@ class Checker {
|
||||
var map = [for( f in tfields ) f.name => f];
|
||||
return TAnon([for( f in fl ) {
|
||||
var ft = map.get(f.name);
|
||||
if( ft == null ) error("Extra field "+f.name, f.e);
|
||||
{ t : typeExprWith(f.e, ft.t), opt : false, name : f.name }
|
||||
var ft = if( ft == null ) {
|
||||
error("Extra field "+f.name, f.e);
|
||||
TDynamic;
|
||||
} else ft.t;
|
||||
{ t : typeExprWith(f.e, ft), opt : false, name : f.name }
|
||||
}]);
|
||||
default:
|
||||
return TAnon([for( f in fl ) { t : typeExpr(f.e, Value), opt : false, name : f.name }]);
|
||||
@@ -778,7 +938,7 @@ class Checker {
|
||||
error("Return not allowed here", expr);
|
||||
else
|
||||
unify(et, allowReturn, v == null ? expr : v);
|
||||
return TDynamic;
|
||||
return makeMono();
|
||||
case EArrayDecl(el):
|
||||
var et = null;
|
||||
for( v in el ) {
|
||||
@@ -787,7 +947,7 @@ class Checker {
|
||||
if( tryUnify(et,t) ) et = t else unify(t,et,v);
|
||||
}
|
||||
}
|
||||
if( et == null ) et = TDynamic;
|
||||
if( et == null ) et = makeMono();
|
||||
return types.getType("Array",[et]);
|
||||
case EArray(a, index):
|
||||
typeExprWith(index, TInt);
|
||||
@@ -798,12 +958,26 @@ class Checker {
|
||||
}
|
||||
case EThrow(e):
|
||||
typeExpr(e, Value);
|
||||
return TDynamic;
|
||||
return makeMono();
|
||||
case EFunction(args, body, name, ret):
|
||||
var tret = ret == null ? makeMono() : makeType(ret, expr);
|
||||
var ft = null, tret = null, targs = null;
|
||||
if( currentFunType != null ) {
|
||||
switch( currentFunType ) {
|
||||
case TFun(args,ret):
|
||||
ft = currentFunType;
|
||||
tret = ret; targs = args;
|
||||
default:
|
||||
throw "assert";
|
||||
}
|
||||
currentFunType = null;
|
||||
} else {
|
||||
tret = ret == null ? makeMono() : makeType(ret, expr);
|
||||
}
|
||||
var locals = saveLocals();
|
||||
var oldRet = allowReturn;
|
||||
var oldGDef = allowDefine;
|
||||
allowReturn = tret;
|
||||
allowDefine = false;
|
||||
var withArgs = null;
|
||||
if( name != null && !withType.match(WithType(follow(_) => TFun(_))) ) {
|
||||
var ev = events.get(name);
|
||||
@@ -813,26 +987,33 @@ class Checker {
|
||||
case WithType(follow(_) => TFun(args,ret)): withArgs = args; unify(tret,ret,expr);
|
||||
default:
|
||||
}
|
||||
var targs = [for( i in 0...args.length ) {
|
||||
var a = args[i];
|
||||
var at = a.t == null ? makeMono() : makeType(a.t, expr);
|
||||
if( withArgs != null && withArgs.length > i )
|
||||
unify(withArgs[i].t, at, expr);
|
||||
this.locals.set(a.name, at);
|
||||
{ name : a.name, opt : a.opt, t : at };
|
||||
}];
|
||||
|
||||
if( targs == null )
|
||||
targs = typeArgs(args,expr);
|
||||
for( i in 0...targs.length ) {
|
||||
var a = targs[i];
|
||||
if( withArgs != null ) {
|
||||
if( i < withArgs.length )
|
||||
unify(withArgs[i].t, a.t, expr);
|
||||
else
|
||||
error("Extra argument "+a.name, expr);
|
||||
}
|
||||
this.locals.set(a.name, a.t);
|
||||
}
|
||||
if( withArgs != null && targs.length < withArgs.length )
|
||||
error("Missing "+(withArgs.length - targs.length)+" arguments ("+[for( i in targs.length...withArgs.length ) typeStr(withArgs[i].t)].join(",")+")", expr);
|
||||
typeExpr(body,NoValue);
|
||||
allowDefine = oldGDef;
|
||||
allowReturn = oldRet;
|
||||
this.locals = locals;
|
||||
var ft = TFun(targs,tret);
|
||||
if( name != null )
|
||||
if( ft == null ) {
|
||||
ft = TFun(targs, tret);
|
||||
locals.set(name, ft);
|
||||
}
|
||||
return ft;
|
||||
case EUnop(op, _, e):
|
||||
var et = typeExpr(e, Value);
|
||||
switch( op ) {
|
||||
case "++", "--":
|
||||
case "++", "--", "-":
|
||||
unify(et,TInt,e);
|
||||
return et;
|
||||
case "!":
|
||||
@@ -847,13 +1028,27 @@ class Checker {
|
||||
case TInst({name:"Array"},[t]):
|
||||
t;
|
||||
default:
|
||||
var ft = getField(itt,"iterator", it);
|
||||
if( ft == null )
|
||||
switch( itt ) {
|
||||
case TAbstract(a, args):
|
||||
// special case : we allow unconditional access
|
||||
// to an abstract iterator() underlying value (eg: ArrayProxy)
|
||||
ft = getField(apply(a.t,a.params,args),"iterator",it);
|
||||
default:
|
||||
}
|
||||
if( ft != null )
|
||||
switch( ft ) {
|
||||
case TFun([],ret): ft = ret;
|
||||
default: ft = null;
|
||||
}
|
||||
var t = makeMono();
|
||||
var iter = makeIterator(t);
|
||||
unify(itt,iter,it);
|
||||
unify(ft != null ? ft : itt,iter,it);
|
||||
t;
|
||||
}
|
||||
this.locals.set(v, vt);
|
||||
typeExpr(e, Value);
|
||||
typeExpr(e, NoValue);
|
||||
this.locals = locals;
|
||||
return TVoid;
|
||||
case EBinop(op, e1, e2):
|
||||
@@ -863,7 +1058,19 @@ class Checker {
|
||||
typeExprWith(e2,TInt);
|
||||
return TInt;
|
||||
case "=":
|
||||
var vt = typeExpr(e1, Value);
|
||||
if( allowDefine ) {
|
||||
switch( edef(e1) ) {
|
||||
case EIdent(i) if( !locals.exists(i) && !globals.exists(i) ):
|
||||
var vt = typeExpr(e2,Value);
|
||||
locals.set(i, vt);
|
||||
return vt;
|
||||
default:
|
||||
}
|
||||
}
|
||||
var vt = switch( edef(e1) ) {
|
||||
case EField(o,f): typeField(o, f, e1, true);
|
||||
default: typeExpr(e1,Value);
|
||||
}
|
||||
typeExprWith(e2,vt);
|
||||
return vt;
|
||||
case "+":
|
||||
@@ -930,11 +1137,48 @@ class Checker {
|
||||
}
|
||||
error("Unsupported operation "+op, expr);
|
||||
}
|
||||
case ETry(etry, v, et, ecatch):
|
||||
var vt = typeExpr(etry, withType);
|
||||
|
||||
var old = locals.get(v);
|
||||
locals.set(v, makeType(et, ecatch));
|
||||
var ct = typeExpr(ecatch, withType);
|
||||
if( old != null ) locals.set(v,old) else locals.remove(v);
|
||||
|
||||
if( withType == NoValue )
|
||||
return TVoid;
|
||||
if( tryUnify(vt,ct) )
|
||||
return ct;
|
||||
unify(ct,vt,ecatch);
|
||||
return vt;
|
||||
case ESwitch(value, cases, defaultExpr):
|
||||
var tmin = null;
|
||||
var vt = typeExpr(value, Value);
|
||||
inline function mergeType(t,p) {
|
||||
if( withType != NoValue ) {
|
||||
if( tmin == null )
|
||||
tmin = t;
|
||||
else if( !tryUnify(t,tmin) ) {
|
||||
unify(tmin,t, p);
|
||||
tmin = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
for( c in cases ) {
|
||||
for( v in c.values ) {
|
||||
var ct = typeExpr(v, WithType(vt));
|
||||
unify(ct, vt, v);
|
||||
}
|
||||
var et = typeExpr(c.expr, withType);
|
||||
mergeType(et, c.expr);
|
||||
}
|
||||
if( defaultExpr != null )
|
||||
mergeType( typeExpr(defaultExpr, withType), defaultExpr);
|
||||
return withType == NoValue ? TVoid : tmin == null ? makeMono() : tmin;
|
||||
case ENew(cl, params):
|
||||
case ETry(e, v, t, ecatch):
|
||||
case ESwitch(e, cases, defaultExpr):
|
||||
}
|
||||
error("Don't know how to type "+edef(expr).getName(), expr);
|
||||
return TDynamic;
|
||||
}
|
||||
|
||||
}
|
||||
+13
-5
@@ -52,13 +52,22 @@ class Interp {
|
||||
|
||||
public function new() {
|
||||
#if haxe3
|
||||
variables = new Map<String,Dynamic>();
|
||||
locals = new Map();
|
||||
#else
|
||||
variables = new Hash();
|
||||
locals = new Hash();
|
||||
#end
|
||||
declared = new Array();
|
||||
resetVariables();
|
||||
initOps();
|
||||
}
|
||||
|
||||
private function resetVariables(){
|
||||
#if haxe3
|
||||
variables = new Map<String,Dynamic>();
|
||||
#else
|
||||
variables = new Hash();
|
||||
#end
|
||||
|
||||
variables.set("null",null);
|
||||
variables.set("true",true);
|
||||
variables.set("false",false);
|
||||
@@ -68,7 +77,6 @@ class Interp {
|
||||
if( el.length > 0 ) inf.customParams = el;
|
||||
haxe.Log.trace(Std.string(v), inf);
|
||||
}));
|
||||
initOps();
|
||||
}
|
||||
|
||||
public function posInfos(): PosInfos {
|
||||
@@ -400,11 +408,11 @@ class Interp {
|
||||
else
|
||||
minParams++;
|
||||
var f = function(args:Array<Dynamic>) {
|
||||
if( args.length != params.length ) {
|
||||
if( ( (args == null) ? 0 : args.length ) != params.length ) {
|
||||
if( args.length < minParams ) {
|
||||
var str = "Invalid number of parameters. Got " + args.length + ", required " + minParams;
|
||||
if( name != null ) str += " for function '" + name+"'";
|
||||
throw str;
|
||||
error(ECustom(str));
|
||||
}
|
||||
// make sure mandatory args are forced
|
||||
var args2 = [];
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ class Macro {
|
||||
opt : false,
|
||||
value : null,
|
||||
});
|
||||
EFunction(name, {
|
||||
EFunction(#if haxe4 FNamed(name,false) #else name #end, {
|
||||
params : [],
|
||||
args : targs,
|
||||
expr : convert(e),
|
||||
|
||||
+98
-92
@@ -78,8 +78,15 @@ class Parser {
|
||||
**/
|
||||
public var allowMetadata : Bool;
|
||||
|
||||
/**
|
||||
resume from parsing errors (when parsing incomplete code, during completion for example)
|
||||
**/
|
||||
public var resumeErrors : Bool;
|
||||
|
||||
// implementation
|
||||
var input : haxe.io.Input;
|
||||
var input : String;
|
||||
var readPos : Int;
|
||||
|
||||
var char : Int;
|
||||
var ops : Array<Bool>;
|
||||
var idents : Array<Bool>;
|
||||
@@ -87,7 +94,6 @@ class Parser {
|
||||
|
||||
#if hscriptPos
|
||||
var origin : String;
|
||||
var readPos : Int;
|
||||
var tokenMin : Int;
|
||||
var tokenMax : Int;
|
||||
var oldTokenMin : Int;
|
||||
@@ -95,7 +101,6 @@ class Parser {
|
||||
var tokens : List<{ min : Int, max : Int, t : Token }>;
|
||||
#else
|
||||
static inline var p1 = 0;
|
||||
static inline var readPos = 0;
|
||||
static inline var tokenMin = 0;
|
||||
static inline var tokenMax = 0;
|
||||
#if haxe3
|
||||
@@ -142,6 +147,7 @@ class Parser {
|
||||
}
|
||||
|
||||
public inline function error( err, pmin, pmax ) {
|
||||
if( !resumeErrors )
|
||||
#if hscriptPos
|
||||
throw new Error(err, pmin, pmax, origin, line);
|
||||
#else
|
||||
@@ -178,12 +184,9 @@ class Parser {
|
||||
}
|
||||
|
||||
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;
|
||||
readPos = 0;
|
||||
var a = new Array();
|
||||
while( true ) {
|
||||
var tk = token();
|
||||
@@ -247,7 +250,7 @@ class Parser {
|
||||
|
||||
inline function pmin(e:Expr) {
|
||||
#if hscriptPos
|
||||
return e.pmin;
|
||||
return e == null ? 0 : e.pmin;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
@@ -255,7 +258,7 @@ class Parser {
|
||||
|
||||
inline function pmax(e:Expr) {
|
||||
#if hscriptPos
|
||||
return e.pmax;
|
||||
return e == null ? 0 : e.pmax;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
@@ -273,6 +276,7 @@ class Parser {
|
||||
}
|
||||
|
||||
function isBlock(e) {
|
||||
if( e == null ) return false;
|
||||
return switch( expr(e) ) {
|
||||
case EBlock(_), EObject(_), ESwitch(_): true;
|
||||
case EFunction(_,e,_,_): isBlock(e);
|
||||
@@ -296,7 +300,7 @@ class Parser {
|
||||
|
||||
var tk = token();
|
||||
// this is a hack to support var a,b,c; with a single EVar
|
||||
while( tk == TComma && expr(e).match(EVar(_)) ) {
|
||||
while( tk == TComma && e != null && expr(e).match(EVar(_)) ) {
|
||||
e = parseStructure("var"); // next variable
|
||||
exprs.push(e);
|
||||
tk = token();
|
||||
@@ -329,6 +333,7 @@ class Parser {
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
ensure(TDoubleDot);
|
||||
fl.push({ name : id, e : parseExpr() });
|
||||
@@ -422,7 +427,7 @@ class Parser {
|
||||
while( true ) {
|
||||
parseFullExpr(a);
|
||||
tk = token();
|
||||
if( tk == TBrClose )
|
||||
if( tk == TBrClose || (resumeErrors && tk == TEof) )
|
||||
break;
|
||||
push(tk);
|
||||
}
|
||||
@@ -431,7 +436,7 @@ class Parser {
|
||||
if( unops.exists(op) ) {
|
||||
var start = tokenMin;
|
||||
var e = parseExpr();
|
||||
if( op == "-" )
|
||||
if( op == "-" && e != null )
|
||||
switch( expr(e) ) {
|
||||
case EConst(CInt(i)):
|
||||
return mk(EConst(CInt(-i)), start, pmax(e));
|
||||
@@ -445,14 +450,14 @@ class Parser {
|
||||
case TBkOpen:
|
||||
var a = new Array();
|
||||
tk = token();
|
||||
while( tk != TBkClose ) {
|
||||
while( tk != TBkClose && (!resumeErrors || tk != TEof) ) {
|
||||
push(tk);
|
||||
a.push(parseExpr());
|
||||
tk = token();
|
||||
if( tk == TComma )
|
||||
tk = token();
|
||||
}
|
||||
if( a.length == 1 )
|
||||
if( a.length == 1 && a[0] != null )
|
||||
switch( expr(a[0]) ) {
|
||||
case EFor(_), EWhile(_), EDoWhile(_):
|
||||
var tmp = "__a_" + (uid++);
|
||||
@@ -485,6 +490,7 @@ class Parser {
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ensureToken(TOp("->"));
|
||||
@@ -517,6 +523,7 @@ class Parser {
|
||||
}
|
||||
|
||||
function mapCompr( tmp : String, e : Expr ) {
|
||||
if( e == null ) return null;
|
||||
var edef = switch( expr(e) ) {
|
||||
case EFor(v, it, e2):
|
||||
EFor(v, it, mapCompr(tmp, e2));
|
||||
@@ -537,6 +544,8 @@ class Parser {
|
||||
}
|
||||
|
||||
function makeUnop( op, e ) {
|
||||
if( e == null && resumeErrors )
|
||||
return null;
|
||||
return switch( expr(e) ) {
|
||||
case EBinop(bop, e1, e2): mk(EBinop(bop, makeUnop(op, e1), e2), pmin(e1), pmax(e2));
|
||||
case ETernary(e1, e2, e3): mk(ETernary(makeUnop(op, e1), e2, e3), pmin(e1), pmax(e3));
|
||||
@@ -545,6 +554,8 @@ class Parser {
|
||||
}
|
||||
|
||||
function makeBinop( op, e1, e ) {
|
||||
if( e == null && resumeErrors )
|
||||
return mk(EBinop(op,e1,e),pmin(e1),pmax(e1));
|
||||
return switch( expr(e) ) {
|
||||
case EBinop(op2,e2,e3):
|
||||
if( opPriority.get(op) <= opPriority.get(op2) && !opRightAssoc.exists(op) )
|
||||
@@ -644,16 +655,16 @@ class Parser {
|
||||
case "new":
|
||||
var a = new Array();
|
||||
a.push(getIdent());
|
||||
var next = true;
|
||||
while( next ) {
|
||||
while( true ) {
|
||||
var tk = token();
|
||||
switch( tk ) {
|
||||
case TDot:
|
||||
a.push(getIdent());
|
||||
case TPOpen:
|
||||
next = false;
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var args = parseExprList(TPClose);
|
||||
@@ -696,6 +707,7 @@ class Parser {
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var exprs = [];
|
||||
@@ -705,6 +717,8 @@ class Parser {
|
||||
switch( tk ) {
|
||||
case TId("case"), TId("default"), TBrClose:
|
||||
break;
|
||||
case TEof if( resumeErrors ):
|
||||
break;
|
||||
default:
|
||||
parseFullExpr(exprs);
|
||||
}
|
||||
@@ -725,6 +739,8 @@ class Parser {
|
||||
switch( tk ) {
|
||||
case TId("case"), TId("default"), TBrClose:
|
||||
break;
|
||||
case TEof if( resumeErrors ):
|
||||
break;
|
||||
default:
|
||||
parseFullExpr(exprs);
|
||||
}
|
||||
@@ -739,6 +755,7 @@ class Parser {
|
||||
break;
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mk(ESwitch(e, cases, def), p1, tokenMax);
|
||||
@@ -809,7 +826,9 @@ class Parser {
|
||||
}
|
||||
switch( tk ) {
|
||||
case TId(id): name = id;
|
||||
default: unexpected(tk);
|
||||
default:
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
var arg : Argument = { name : name };
|
||||
args.push(arg);
|
||||
@@ -830,8 +849,8 @@ class Parser {
|
||||
unexpected(tk);
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function parseFunctionDecl() {
|
||||
@@ -891,6 +910,7 @@ class Parser {
|
||||
default:
|
||||
}
|
||||
unexpected(t);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
push(t);
|
||||
@@ -901,13 +921,13 @@ class Parser {
|
||||
case TPOpen:
|
||||
var a = token(),
|
||||
b = token();
|
||||
|
||||
push(b);
|
||||
|
||||
push(b);
|
||||
push(a);
|
||||
|
||||
function withReturn(args) {
|
||||
switch token() { // I think it wouldn't hurt if ensure used enumEq
|
||||
case TOp('->'):
|
||||
case TOp('->'):
|
||||
case t: unexpected(t);
|
||||
}
|
||||
|
||||
@@ -916,7 +936,7 @@ class Parser {
|
||||
|
||||
switch [a, b] {
|
||||
case [TPClose, _] | [TId(_), TDoubleDot]:
|
||||
|
||||
|
||||
var args = [for (arg in parseFunctionArgs()) {
|
||||
switch arg.value {
|
||||
case null:
|
||||
@@ -929,12 +949,12 @@ class Parser {
|
||||
|
||||
return withReturn(args);
|
||||
default:
|
||||
|
||||
|
||||
var t = parseType();
|
||||
return switch token() {
|
||||
case TComma:
|
||||
var args = [t];
|
||||
|
||||
|
||||
while (true) {
|
||||
args.push(parseType());
|
||||
if (!maybe(TComma)) break;
|
||||
@@ -973,6 +993,7 @@ class Parser {
|
||||
meta.push({ name : name, params : parseMetaArgs() });
|
||||
default:
|
||||
unexpected(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return parseTypeNext(CTAnon(fields));
|
||||
@@ -1017,6 +1038,7 @@ class Parser {
|
||||
default:
|
||||
if( tk == etk ) break;
|
||||
unexpected(tk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return args;
|
||||
@@ -1026,7 +1048,8 @@ class Parser {
|
||||
|
||||
public function parseModule( content : String, ?origin : String = "hscript" ) {
|
||||
initParser(origin);
|
||||
this.input = new haxe.io.StringInput(content);
|
||||
input = content;
|
||||
readPos = 0;
|
||||
allowTypes = true;
|
||||
allowMetadata = true;
|
||||
var decls = [];
|
||||
@@ -1219,6 +1242,7 @@ class Parser {
|
||||
};
|
||||
default:
|
||||
unexpected(TId(id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -1226,20 +1250,13 @@ class Parser {
|
||||
|
||||
// ------------------------ lexing -------------------------------
|
||||
|
||||
inline function incPos() {
|
||||
#if hscriptPos
|
||||
readPos++;
|
||||
#end
|
||||
}
|
||||
|
||||
function readChar() {
|
||||
incPos();
|
||||
return try input.readByte() catch( e : Dynamic ) 0;
|
||||
inline function readChar() {
|
||||
return StringTools.fastCodeAt(input, readPos++);
|
||||
}
|
||||
|
||||
function readString( until ) {
|
||||
var c = 0;
|
||||
var b = new haxe.io.BytesOutput();
|
||||
var b = new StringBuf();
|
||||
var esc = false;
|
||||
var old = line;
|
||||
var s = input;
|
||||
@@ -1247,38 +1264,26 @@ class Parser {
|
||||
var p1 = readPos - 1;
|
||||
#end
|
||||
while( true ) {
|
||||
try {
|
||||
incPos();
|
||||
c = s.readByte();
|
||||
} catch( e : Dynamic ) {
|
||||
var c = readChar();
|
||||
if( StringTools.isEof(c) ) {
|
||||
line = old;
|
||||
error(EUnterminatedString, p1, p1);
|
||||
break;
|
||||
}
|
||||
if( esc ) {
|
||||
esc = false;
|
||||
switch( c ) {
|
||||
case 'n'.code: b.writeByte(10);
|
||||
case 'r'.code: b.writeByte(13);
|
||||
case 't'.code: b.writeByte(9);
|
||||
case "'".code, '"'.code, '\\'.code: b.writeByte(c);
|
||||
case '/'.code: if( allowJSON ) b.writeByte(c) else invalidChar(c);
|
||||
case 'n'.code: b.addChar('\n'.code);
|
||||
case 'r'.code: b.addChar('\r'.code);
|
||||
case 't'.code: b.addChar('\t'.code);
|
||||
case "'".code, '"'.code, '\\'.code: b.addChar(c);
|
||||
case '/'.code: if( allowJSON ) b.addChar(c) else invalidChar(c);
|
||||
case "u".code:
|
||||
if( !allowJSON ) invalidChar(c);
|
||||
var code = null;
|
||||
try {
|
||||
incPos();
|
||||
incPos();
|
||||
incPos();
|
||||
incPos();
|
||||
code = s.readString(4);
|
||||
} catch( e : Dynamic ) {
|
||||
line = old;
|
||||
error(EUnterminatedString, p1, p1);
|
||||
}
|
||||
var k = 0;
|
||||
for( i in 0...4 ) {
|
||||
k <<= 4;
|
||||
var char = code.charCodeAt(i);
|
||||
var char = readChar();
|
||||
switch( char ) {
|
||||
case 48,49,50,51,52,53,54,55,56,57: // 0-9
|
||||
k += char - 48;
|
||||
@@ -1287,20 +1292,14 @@ class Parser {
|
||||
case 97,98,99,100,101,102: // a-f
|
||||
k += char - 87;
|
||||
default:
|
||||
if( StringTools.isEof(char) ) {
|
||||
line = old;
|
||||
error(EUnterminatedString, p1, p1);
|
||||
}
|
||||
invalidChar(char);
|
||||
}
|
||||
}
|
||||
// encode k in UTF8
|
||||
if( k <= 0x7F )
|
||||
b.writeByte(k);
|
||||
else if( k <= 0x7FF ) {
|
||||
b.writeByte( 0xC0 | (k >> 6));
|
||||
b.writeByte( 0x80 | (k & 63));
|
||||
} else {
|
||||
b.writeByte( 0xE0 | (k >> 12) );
|
||||
b.writeByte( 0x80 | ((k >> 6) & 63) );
|
||||
b.writeByte( 0x80 | (k & 63) );
|
||||
}
|
||||
b.addChar(k);
|
||||
default: invalidChar(c);
|
||||
}
|
||||
} else if( c == 92 )
|
||||
@@ -1309,10 +1308,10 @@ class Parser {
|
||||
break;
|
||||
else {
|
||||
if( c == 10 ) line++;
|
||||
b.writeByte(c);
|
||||
b.addChar(c);
|
||||
}
|
||||
}
|
||||
return b.getBytes().toString();
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
function token() {
|
||||
@@ -1344,8 +1343,13 @@ class Parser {
|
||||
this.char = -1;
|
||||
}
|
||||
while( true ) {
|
||||
if( StringTools.isEof(char) ) {
|
||||
this.char = char;
|
||||
return TEof;
|
||||
}
|
||||
switch( char ) {
|
||||
case 0: return TEof;
|
||||
case 0:
|
||||
return TEof;
|
||||
case 32,9,13: // space, tab, CR
|
||||
#if hscriptPos
|
||||
tokenMin++;
|
||||
@@ -1517,6 +1521,7 @@ class Parser {
|
||||
var prev = -1;
|
||||
while( true ) {
|
||||
char = readChar();
|
||||
if( StringTools.isEof(char) ) char = 0;
|
||||
if( !ops[char] || prev == '='.code ) {
|
||||
if( op.charCodeAt(0) == '/'.code )
|
||||
return tokenComment(op,char);
|
||||
@@ -1531,6 +1536,7 @@ class Parser {
|
||||
var id = String.fromCharCode(char);
|
||||
while( true ) {
|
||||
char = readChar();
|
||||
if( StringTools.isEof(char) ) char = 0;
|
||||
if( !idents[char] ) {
|
||||
this.char = char;
|
||||
return TId(id);
|
||||
@@ -1636,14 +1642,11 @@ class Parser {
|
||||
var c = op.charCodeAt(1);
|
||||
var s = input;
|
||||
if( c == '/'.code ) { // comment
|
||||
try {
|
||||
while( char != '\r'.code && char != '\n'.code ) {
|
||||
incPos();
|
||||
char = s.readByte();
|
||||
}
|
||||
this.char = char;
|
||||
} catch( e : Dynamic ) {
|
||||
while( char != '\r'.code && char != '\n'.code ) {
|
||||
char = readChar();
|
||||
if( StringTools.isEof(char) ) break;
|
||||
}
|
||||
this.char = char;
|
||||
return token();
|
||||
}
|
||||
if( c == '*'.code ) { /* comment */
|
||||
@@ -1652,21 +1655,24 @@ class Parser {
|
||||
this.char = char;
|
||||
return token();
|
||||
}
|
||||
try {
|
||||
while( true ) {
|
||||
while( char != '*'.code ) {
|
||||
if( char == '\n'.code ) line++;
|
||||
incPos();
|
||||
char = s.readByte();
|
||||
}
|
||||
incPos();
|
||||
char = s.readByte();
|
||||
if( char == '/'.code )
|
||||
while( true ) {
|
||||
while( char != '*'.code ) {
|
||||
if( char == '\n'.code ) line++;
|
||||
char = readChar();
|
||||
if( StringTools.isEof(char) ) {
|
||||
line = old;
|
||||
error(EUnterminatedComment, tokenMin, tokenMin);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch( e : Dynamic ) {
|
||||
line = old;
|
||||
error(EUnterminatedComment, tokenMin, tokenMin);
|
||||
char = readChar();
|
||||
if( StringTools.isEof(char) ) {
|
||||
line = old;
|
||||
error(EUnterminatedComment, tokenMin, tokenMin);
|
||||
break;
|
||||
}
|
||||
if( char == '/'.code )
|
||||
break;
|
||||
}
|
||||
return token();
|
||||
}
|
||||
|
||||
+2
-2
@@ -48,7 +48,7 @@ class Printer {
|
||||
|
||||
function type( t : CType ) {
|
||||
switch( t ) {
|
||||
case CTOpt(t):
|
||||
case CTOpt(t):
|
||||
add('?');
|
||||
type(t);
|
||||
case CTPath(path, params):
|
||||
@@ -331,7 +331,7 @@ class Printer {
|
||||
|
||||
public static function errorToString( e : Expr.Error ) {
|
||||
var message = switch( #if hscriptPos e.e #else e #end ) {
|
||||
case EInvalidChar(c): "Invalid character: '"+String.fromCharCode(c)+"' ("+c+")";
|
||||
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF" : String.fromCharCode(c))+"' ("+c+")";
|
||||
case EUnexpected(s): "Unexpected token: \""+s+"\"";
|
||||
case EUnterminatedString: "Unterminated string";
|
||||
case EUnterminatedComment: "Unterminated comment";
|
||||
|
||||
Reference in New Issue
Block a user