Compare commits

..
35 Commits
Author SHA1 Message Date
Nicolas Cannasse b32dde3c91 2.4.0 2020-04-10 10:09:34 +02:00
Nicolas Cannasse b9a566350b fixed missing enums constructor parsing 2020-04-08 15:27:03 +02:00
Nicolas Cannasse ce9ce674ea added missing unary - 2019-12-18 14:33:35 +01:00
Nicolas Cannasse b8641779bd added error on missing event args 2019-12-16 13:28:40 +01:00
Nicolas Cannasse 5e620d784e added canWrite check on fields 2019-12-13 13:18:14 +01:00
Nicolas Cannasse 013bcfaffb fixed unicode parsing in strings 2019-12-09 17:34:13 +01:00
Andy Li 2504c96927 [TravisCI] use php from apt 2019-12-04 03:48:09 +08:00
Andy Li e2f4d1481e [TravisCI] some fixes
- use bionic
 - remove flash test
 - add Haxe 4.0.3 test
 - only test C# for Haxe 4+
 - add -D php7 for Haxe 3
2019-12-04 01:01:52 +08:00
Andy Li ea3d68573b [AppVeyor] remove php and flash tests 2019-12-04 00:33:14 +08:00
Andy Li c6cb594ef1 [AppVeyor] use Haxe 4.0.3 2019-12-04 00:24:27 +08:00
Andy Li 948123bc7a keep haxe 3 support 2019-12-04 00:13:41 +08:00
Nicolas Cannasse 68f5b26af9 bugfixes for eval (close #90) 2019-12-03 14:27:07 +01:00
Nicolas Cannasse 5d493630b1 make apply public 2019-11-29 18:11:46 +01:00
Nicolas Cannasse f4db6faa80 fixed follow wrt recursion 2019-11-28 14:20:55 +01:00
Nicolas Cannasse e340f7f5ad ignored custom metadata in async toCps 2019-11-27 14:37:42 +01:00
Nicolas Cannasse b4d923b74c fixed without hscriptPos 2019-11-26 18:11:10 +01:00
Nicolas Cannasse 965f334d59 various fixes in type checker 2019-11-26 14:34:15 +01:00
Nicolas Cannasse 54463b78d2 more resumeErrors parser support, switch to parseString only (fix position with unicode) 2019-11-26 14:32:56 +01:00
Nicolas Cannasse 600aec1f50 stronger parser wrt resumeErrors 2019-11-25 20:32:08 +01:00
Nicolas Cannasse bcfbe81979 fixed pmin/pmax for resumeErrors 2019-11-20 18:14:02 +01:00
ee-tprandNicolas Cannasse 31e8851bcc Add a resetVariables into the interp so it can be called when extending interp (#89) 2019-10-30 11:51:38 +01:00
Nicolas Cannasse f464767ee7 added Parser.resumeError, added Checker.getCompletion 2019-10-20 11:16:23 +02:00
Christopher GeorgeandNicolas Cannasse 22eb58b3de Fix calling with 0 args targeting Java (#88) 2019-10-08 09:29:14 +02:00
Nicolas Cannasse 0daee20545 allow customization of top level async end point 2019-10-07 10:57:53 +02:00
Nicolas Cannasse d98073dc33 added support for mutually recursive functions 2019-10-02 16:27:35 +02:00
Nicolas Cannasse fb199e7dea added removeGlobal() 2019-10-01 18:09:41 +02:00
Christopher GeorgeandNicolas Cannasse 6cea0d97c2 Call error() on invalid number of arguments instead of just throwing … (#86) 2019-09-25 21:09:36 +02:00
Nicolas Cannasse 058dd2e01e fix for haxe 4.0-rc4 2019-09-05 10:41:37 +02:00
Nicolas Cannasse 975d122491 make unify public 2019-08-29 17:29:11 +02:00
Nicolas Cannasse eef4c66374 disable flash testing (404) 2019-06-16 10:52:29 +02:00
ncannasse 5ce4fbd74d added @sync function name(...) support, improved variable lookup 2019-04-24 15:27:20 +02:00
ncannasse f4d8cff26b disable cs tests for now (see https://github.com/HaxeFoundation/haxe/issues/4639) 2019-03-07 12:25:26 +01:00
ncannasse 46b6b3c151 always valid 2018-10-25 15:13:36 +02:00
ncannasse 796b217dec compile Checker (travis) 2018-10-15 09:42:01 +02:00
ncannasse 1ccc35f464 added try/switch type checking, and some other fixes 2018-10-15 09:40:41 +02:00
16 changed files with 484 additions and 355 deletions
+11 -16
View File
@@ -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
+14 -2
View File
@@ -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)
+1
View File
@@ -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
View File
@@ -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
-5
View File
@@ -1,5 +0,0 @@
*.exe
*.app
flashplayerdebugger
LGPL/
license.pdf
-91
View File
@@ -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';
}
}
}
}
-54
View File
@@ -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);
}
}
-2
View File
@@ -1,2 +0,0 @@
-cp flash
--run Install
-2
View File
@@ -1,2 +0,0 @@
-cp flash
--run Run
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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";