Compare commits

..
28 Commits
Author SHA1 Message Date
ncannasse 2ed6f15fe1 2.3.0 2018-10-08 09:46:15 +02:00
Juraj KirchheimandNicolas Cannasse 575040be65 Fix Haxe 3. (#75) 2018-09-26 18:38:29 +02:00
Juraj KirchheimandNicolas Cannasse 206959adbd Support new function type syntax. (#74) 2018-09-26 14:06:16 +02:00
ncannasse dd1c97b6af more type checking 2018-09-25 17:01:09 +02:00
ncannasse 85463dcff0 implemented apply type parameters 2018-09-25 12:04:35 +02:00
ncannasse 7ff816417e fixes 2018-09-24 15:54:40 +02:00
ncannasse 3fa2f3898a fix 2018-09-24 15:54:24 +02:00
ncannasse 514ce9eec2 more type checks 2018-09-21 10:47:30 +02:00
ncannasse 5bc357b4b3 hscriptPos changes 2018-09-21 10:47:20 +02:00
ncannasse 56ddc3c24e more typing 2018-09-20 18:21:17 +02:00
ncannasse e407b2a307 optimize the ast (eliminate (function(_) e)(null) calls)
treat function literals as synchronized by default
2018-09-19 18:45:07 +02:00
ncannasse 1ac194ed1d more typing 2018-09-19 18:42:06 +02:00
ncannasse 62bf5d4cc9 more typing, added top down inference 2018-09-19 15:11:29 +02:00
ncannasse 27093064a9 added ECheckType, added short lambda parsing 2018-09-18 16:15:15 +02:00
ncannasse 860a3018a7 don't test on haxe 3.2.x anymore 2018-09-18 15:07:07 +02:00
ncannasse eb3992dbdb separate Checker / CheckerTypes 2018-09-18 14:35:36 +02:00
ncannasse 8c5f9cc1d3 more type checking 2018-09-17 11:52:29 +02:00
ncannasse ed78703090 added type checker (partial) 2018-09-12 18:00:34 +02:00
ncannasse 29b61bcee9 fixed invalid char position 2018-09-12 18:00:34 +02:00
Andy Li 0ff49845a7 fix hscript.Bytes with -D hscriptPos (close #64) 2018-05-11 17:58:04 +08:00
Andy Li c17366b2b1 [AppVeyor] update flash install script 2018-05-11 16:39:39 +08:00
Andy Li 5da267fb31 [TravisCI] recompile hxcpp ndll for haxe 3.2.1 2018-05-11 16:18:20 +08:00
Andy Li dffa3345d0 [TravisCI] turn off php deprecated function warning 2018-05-11 16:01:16 +08:00
Andy Li a23c767fb0 [AppVeyor] enable windows update service 2018-05-11 14:05:39 +08:00
Andy Li 5d6ec51302 [CI] testing 3.4.7 2018-05-11 14:01:38 +08:00
Andy Li 585f277bf5 [TravisCI] fix homebrew php and python 2018-05-11 12:55:32 +08:00
Andy Li aae5f431cb use Bytes#getInt32 and BytesBuffer#addInt32
fix test for python and php targets
2018-05-11 11:00:40 +08:00
Andy Li 4eb84e5c32 better pos info in test 2018-05-11 10:48:14 +08:00
19 changed files with 1231 additions and 76 deletions
+8 -7
View File
@@ -8,8 +8,7 @@ sudo: false
dist: trusty
haxe:
- "3.2.1"
- "3.4.2"
- "3.4.7"
- development
addons:
@@ -36,21 +35,22 @@ install:
elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
brew update;
brew install mono;
brew install python3;
brew tap homebrew/homebrew-php;
brew install php71;
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;
else
haxelib install hxcpp;
fi
- if [ "${TRAVIS_HAXE_VERSION}" = "development" ] || [ "${TRAVIS_HAXE_VERSION}" = "3.2.1" ]; then
pushd $(haxelib path hxcpp | head -1);
pushd tools/hxcpp; haxe compile.hxml; popd;
pushd project; neko build.n; popd;
popd;
else
haxelib install hxcpp;
fi
- haxelib install hxjava
- haxelib install hxcs
@@ -63,6 +63,7 @@ script:
haxe build.hxml &&
popd
- haxe bin/build-interp.hxml
- haxe bin/build-interp.hxml -D hscriptPos
- 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
+16
View File
@@ -0,0 +1,16 @@
{
// Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
// Pointez pour afficher la description des attributs existants.
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "HashLink (launch)",
"request": "launch",
"type": "hl",
"hxml": "hscript.hxml",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build"
}
]
}
+16
View File
@@ -0,0 +1,16 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "hxml",
"file": "hscript.hxml",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
+19 -2
View File
@@ -7,7 +7,7 @@ import hscript.Printer;
import haxe.unit.*;
class Test extends TestCase {
function assertScript(x,v:Dynamic,?vars : Dynamic,allowTypes=false) {
function assertScript(x,v:Dynamic,?vars : Dynamic, allowTypes=false, ?pos:haxe.PosInfos) {
var p = new hscript.Parser();
p.allowTypes = allowTypes;
var program = p.parseString(x);
@@ -18,7 +18,7 @@ class Test extends TestCase {
for( v in Reflect.fields(vars) )
interp.variables.set(v,Reflect.field(vars,v));
var ret : Dynamic = interp.execute(program);
assertEquals(v, ret);
assertEquals(v, ret, pos);
}
function test():Void {
@@ -99,6 +99,16 @@ class Test extends TestCase {
assertScript("var a:Array<Dynamic>=[1,2,4]; a[2]", 4, null, true);
assertScript("/**/0", 0);
assertScript("x=1;x*=-2", -2);
assertScript("var f = x -> x + 1; f(3)", 4);
assertScript("var f = (x) -> x + 1; f(3)", 4);
assertScript("var f = (x:Int) -> x + 1; f(3)", 4);
assertScript("var f = (x,y) -> x + y; f(3,1)", 4);
assertScript("var f = (x,y:Int) -> x + y; f(3,1)", 4);
assertScript("var f = (x:Int,y:Int) -> x + y; f(3,1)", 4);
assertScript("var f:Int->Int->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
assertScript("var f:(x:Int, y:Int)->Int = (x:Int,y:Int) -> x + y; f(3,1)", 4, null, true);
assertScript("var f:(x:Int)->(y:Int, z:Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
assertScript("var f:(x:Int)->(Int, Int)->Int = (x:Int) -> (y:Int, z:Int) -> x + y + z; f(3)(1, 2)", 6, null, true);
}
function testMap():Void {
@@ -152,9 +162,16 @@ class Test extends TestCase {
}
static function main() {
#if ((haxe_ver < 4) && php)
// uncaught exception: The each() function is deprecated. This message will be suppressed on further calls (errno: 8192)
// in file: /Users/travis/build/andyli/hscript/bin/lib/Type.class.php line 178
untyped __php__("error_reporting(E_ALL ^ E_DEPRECATED);");
#end
var runner = new TestRunner();
runner.add(new Test());
var succeed = runner.run();
#if sys
Sys.exit(succeed ? 0 : 1);
#elseif flash
+3 -1
View File
@@ -5,8 +5,10 @@ environment:
HAXELIB_ROOT: C:\projects\haxelib
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.2 -y
- cinst haxe -version 3.4.7 -y
- RefreshEnv
# Setup haxelib
- mkdir "%HAXELIB_ROOT%"
+3 -1
View File
@@ -1,3 +1,5 @@
*.exe
*.app
flashplayerdebugger
flashplayerdebugger
LGPL/
license.pdf
+6 -9
View File
@@ -1,4 +1,5 @@
import Sys.*;
import sys.FileSystem.*;
import sys.io.File.*;
import haxe.*;
import haxe.io.*;
@@ -7,11 +8,11 @@ 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/25/flash_player_sa_linux_debug.x86_64.tar.gz";
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flash_player_sa_linux_debug.x86_64.tar.gz";
case "Mac":
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flashplayer_25_sa_debug.dmg";
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.dmg";
case "Windows":
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flashplayer_25_sa_debug.exe";
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/26/flashplayer_26_sa_debug.exe";
case _:
throw "unsupported system";
}
@@ -33,7 +34,7 @@ class Install {
case "Mac":
"/Library/Application Support/Macromedia/FlashPlayerTrust";
case "Windows":
Path.join([getEnv("SYSTEMROOT"), "system32", "Macromed", "Flash", "FlashPlayerTrust"]);
Path.join([getEnv("APPDATA"), "Macromedia", "Flash Player", "#Security", "FlashPlayerTrust"]);
case _:
throw "unsupported system";
}
@@ -45,12 +46,8 @@ class Install {
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":
// https://github.com/caskroom/homebrew-cask/pull/15381
if (command("brew", ["uninstall", "--force", "brew-cask"]) != 0)
throw "failed to brew uninstall --force brew-cask";
if (command("brew", ["tap", "caskroom/versions"]) != 0)
throw "failed to brew tap caskroom/versions";
if (command("brew", ["cask", "install", "flash-player-debugger"]) != 0)
throw "failed to install flash-player-debugger";
case "Windows":
+1 -1
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.2.0",
"version": "2.3.0",
"releasenote": "",
"contributors": ["ncannasse"]
}
+2 -1
View File
@@ -1 +1,2 @@
bin/build-flash.hxml
bin/build-each.hxml
-hl bin/Test.hl
+28 -18
View File
@@ -79,8 +79,10 @@ class Async {
function buildSync( e : Expr, exit : Expr ) : Expr {
switch( expr(e) ) {
case EFunction(_):
return toCps(e, null, null);
case EFunction(_,_,name,_):
if( name != null )
return toCps(e, null, null);
return e;
case EBlock(el):
var v = saveVars();
for( e in el )
@@ -96,7 +98,7 @@ class Async {
case EBreak if( currentBreak != null ):
return currentBreak(e);
case EContinue if( currentLoop != null ):
return block([call(currentLoop, [nullId], e), mk(EReturn(),e)],e);
return block([retNull(currentLoop, e), mk(EReturn(),e)],e);
case EFor(_), EWhile(_):
var oldLoop = currentLoop, oldBreak = currentBreak;
currentLoop = null;
@@ -106,7 +108,7 @@ class Async {
currentBreak = oldBreak;
return e;
case EReturn(eret) if( exit != null ):
return block([call(exit,[eret == null ? nullId : eret], e), mk(EReturn(),e)], e);
return block([eret == null ? retNull(exit, e) : call(exit,[eret], e), mk(EReturn(),e)], e);
default:
return Tools.map(e, buildSync.bind(_, exit));
}
@@ -133,7 +135,9 @@ class Async {
return mk(EFunction([for( a in arg ) { name : a, t : null }], e, name), e);
}
inline function block(arr, e) {
inline function block(arr:Array<Expr>, e) {
if( arr.length == 1 && expr(arr[0]).match(EBlock(_)) )
return arr[0];
return mk(EBlock(arr), e);
}
@@ -149,8 +153,12 @@ class Async {
return mk(ECall(e, args), inf);
}
function retNull(e) : Expr {
return call(e, [nullId],e);
function retNull(e:Expr,?pos) : Expr {
switch( expr(e) ) {
case EFunction([{name:"_"}], e, _, _): return e;
default:
}
return call(e, [nullId], pos == null ? e : pos);
}
function makeCall( ecall, args : Array<Expr>, rest : Expr, exit, sync = false ) {
@@ -179,7 +187,9 @@ class Async {
if( !syncFlag )
return;
switch( expr(e) ) {
case ECall(_), EFunction(_):
case ECall(_):
syncFlag = false;
case EFunction(_,_,name,_) if( name != null ):
syncFlag = false;
case EMeta("sync" | "async", _, _):
// isolated from the sync part
@@ -206,7 +216,7 @@ class Async {
// local recursion
for( e in el )
switch( expr(e) ) {
case EFunction(_, _, name, null): varNames.push(name);
case EFunction(_, _, name, null): if( name != null ) varNames.push(name);
default:
}
while( el.length > 0 ) {
@@ -250,7 +260,7 @@ class Async {
var oldLoop = currentLoop, oldBreak = currentBreak;
var loop = ident("_loop" + id,e);
currentLoop = loop;
currentBreak = function(inf) return block([call(rest, [nullId], inf), mk(EReturn(),inf)], inf);
currentBreak = function(inf) return block([retNull(rest, inf), mk(EReturn(),inf)], inf);
var efor = block([
mk(EVar("_i" + id, call(ident("makeIterator",eit),[eit],eit)),eit),
fun("_", block([
@@ -258,7 +268,7 @@ class Async {
mk(EVar(v, call(field(it, "next",it), [], it)), it),
toCps(eloop, loop, exit),
], it),"_loop" + id),
call(loop, [nullId], e),
retNull(loop, e),
], e);
currentLoop = oldLoop;
currentBreak = oldBreak;
@@ -306,18 +316,18 @@ class Async {
var loop = ident("_loop" + id, cond);
var oldLoop = currentLoop, oldBreak = currentBreak;
currentLoop = loop;
currentBreak = function(e) return block([call(rest, [nullId], e), mk(EReturn(),e)],e);
currentBreak = function(e) return block([retNull(rest,e), mk(EReturn(),e)],e);
var ewhile = block([
fun("_r",
toCps(cond, fun("_c", mk(EIf(ident("_c", cond), toCps(ewh, loop, exit), call(rest, [nullId], cond)),cond)), exit)
toCps(cond, fun("_c", mk(EIf(ident("_c", cond), toCps(ewh, loop, exit), retNull(rest,cond)),cond)), exit)
, "_loop"+id),
call(loop, [nullId], cond),
retNull(loop, cond),
],e);
currentLoop = oldLoop;
currentBreak = oldBreak;
return ewhile;
case EReturn(eret):
return eret == null ? call(exit, [nullId], e) : toCps(eret, exit, exit);
return eret == null ? retNull(exit, e) : toCps(eret, exit, exit);
case EObject(fields):
var id = "_o" + uid++;
var rest = call(rest, [ident(id,e)], e);
@@ -353,10 +363,10 @@ class Async {
return toCps(earr, fun(id1, toCps(eindex, fun(id2, call(rest, [mk(EArray(ident(id1,e), ident(id2,e)),e)], e)), exit)), exit);
case EVar(v, t, ev):
if( ev == null )
return block([e, call(rest, [nullId],e)], e);
return block([e, retNull(rest, e)], e);
return block([
mk(EVar(v, t),e),
toCps(ev, fun("_r", block([binop("=", ident(v,e), ident("_r",e), e), call(rest,[nullId],e)], e)), exit),
toCps(ev, fun("_r", block([binop("=", ident(v,e), ident("_r",e), e), retNull(rest,e)], e)), exit),
],e);
case EConst(_), EIdent(_), EUnop(_), EField(_):
return call(rest, [e], e);
@@ -375,7 +385,7 @@ class Async {
return currentBreak(e);
case EContinue:
if( currentLoop == null ) throw "Continue outside loop";
return block([call(currentLoop, [nullId], e), mk(EReturn(),e)], e);
return block([retNull(currentLoop, e), mk(EReturn(),e)], e);
case ESwitch(v, cases, def):
var cases = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
return toCps(v, mk(EFunction([ { name : "_c", t : null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
+10 -5
View File
@@ -72,10 +72,7 @@ class Bytes {
}
function doEncodeInt(v: Int) {
bout.addByte(v & 0xFF);
bout.addByte((v >> 8) & 0xFF);
bout.addByte((v >> 16) & 0xFF);
bout.addByte(v >>> 24);
bout.addInt32(v);
}
function doEncodeConst( c : Const ) {
@@ -107,7 +104,7 @@ class Bytes {
}
function doDecodeInt() {
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16) | (bin.get(pin+3) << 24);
var i = bin.getInt32(pin);
pin += 4;
return i;
}
@@ -249,11 +246,17 @@ class Bytes {
bout.addByte(args == null ? 0 : args.length + 1);
if( args != null ) for( e in args ) doEncode(e);
doEncode(e);
case ECheckType(e,_):
doEncode(e);
}
}
function doDecode() : Expr {
#if hscriptPos
if (bin.get(pin) == 255) {
pin++;
return null;
}
var origin = doDecodeString();
var line = doDecodeInt();
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
@@ -373,6 +376,8 @@ class Bytes {
var count = bin.get(pin++);
var args = count == 0 ? null : [for( i in 0...count - 1 ) doDecode()];
EMeta(name, args, doDecode());
case 26:
ECheckType(doDecode(), CTPath(["Void"]));
case 255:
null;
default:
+940
View File
@@ -0,0 +1,940 @@
package hscript;
import hscript.Expr;
/**
This is a special type that can be used in API.
It will be type-checked as `Script` but will compile/execute as `Real`
**/
typedef TypeCheck<Real,Script> = Real;
enum TType {
TMono( r : { r : TType } );
TVoid;
TInt;
TFloat;
TBool;
TDynamic;
TParam( name : String );
TUnresolved( name : String );
TNull( t : TType );
TInst( c : CClass, args : Array<TType> );
TEnum( e : CEnum, args : Array<TType> );
TType( t : CTypedef, args : Array<TType> );
TAbstract( a : CAbstract, args : Array<TType> );
TFun( args : Array<{ name : String, opt : Bool, t : TType }>, ret : TType );
TAnon( fields : Array<{ name : String, opt : Bool, t : TType }> );
}
private enum WithType {
NoValue;
Value;
WithType( t : TType );
}
enum CTypedecl {
CTClass( c : CClass );
CTEnum( e : CEnum );
CTTypedef( t : CTypedef );
CTAlias( t : TType );
CTAbstract( a : CAbstract );
}
typedef CNamedType = {
var name : String;
var params : Array<TType>;
}
typedef CClass = {> CNamedType,
@:optional var superClass : TType;
@:optional var constructor : CField;
var fields : Map<String,CField>;
var statics : Map<String,CField>;
}
typedef CField = {
var isPublic : Bool;
var params : Array<TType>;
var name : String;
var t : TType;
}
typedef CEnum = {> CNamedType,
var constructors : Map<String,TType>;
}
typedef CTypedef = {> CNamedType,
var t : TType;
}
typedef CAbstract = {> CNamedType,
}
@:allow(hscript.Checker)
class CheckerTypes {
var types : Map<String,CTypedecl> = new Map();
var t_string : TType;
var localParams : Map<String,TType>;
public function new() {
types = new Map();
types.set("Void",CTAlias(TVoid));
types.set("Int",CTAlias(TInt));
types.set("Float",CTAlias(TFloat));
types.set("Bool",CTAlias(TBool));
types.set("Dynamic",CTAlias(TDynamic));
}
public function addXmlApi( api : Xml ) {
var types = new haxe.rtti.XmlParser();
types.process(api, "");
var todo = [];
for( v in types.root )
addXmlType(v,todo);
for( f in todo )
f();
t_string = getType("String");
}
function addXmlType(x:haxe.rtti.CType.TypeTree,todo:Array<Void->Void>) {
switch (x) {
case TPackage(name, full, subs):
for( s in subs ) addXmlType(s,todo);
case TClassdecl(c):
if( types.exists(c.path) ) return;
var cl : CClass = {
name : c.path,
params : [],
fields : new Map(),
statics : new Map(),
};
for( p in c.params )
cl.params.push(TParam(p));
todo.push(function() {
localParams = [for( t in cl.params ) c.path+"."+Checker.typeStr(t) => t];
if( c.superClass != null )
cl.superClass = getType(c.superClass.path, [for( t in c.superClass.params ) makeXmlType(t)]);
var pkeys = [];
for( f in c.fields ) {
if( f.isOverride || f.name.substr(0,4) == "get_" || f.name.substr(0,4) == "set_" ) continue;
var skip = false;
for( m in f.meta )
if( m.name == ":noScript" ) {
skip = true;
break;
}
if( skip ) continue;
var fl : CField = { isPublic : f.isPublic, params : [], name : f.name, t : null };
for( p in f.params ) {
var pt = TParam(p);
var key = f.name+"."+p;
pkeys.push(key);
fl.params.push(pt);
localParams.set(key, pt);
}
fl.t = makeXmlType(f.type);
while( pkeys.length > 0 )
localParams.remove(pkeys.pop());
if( fl.name == "new" )
cl.constructor = fl;
else
cl.fields.set(f.name, fl);
}
localParams = null;
});
types.set(cl.name, CTClass(cl));
case TEnumdecl(e):
if( types.exists(e.path) ) return;
var en : CEnum = {
name : e.path,
params : [],
constructors: new Map(),
};
for( p in e.params )
en.params.push(TParam(p));
todo.push(function() {
localParams = [for( t in en.params ) e.path+"."+Checker.typeStr(t) => t];
localParams = null;
});
types.set(en.name, CTEnum(en));
case TTypedecl(t):
if( types.exists(t.path) ) return;
var td : CTypedef = {
name : t.path,
params : [],
t : null,
};
for( p in t.params )
td.params.push(TParam(p));
if( t.path == "hscript.TypeCheck" )
td.params.reverse();
todo.push(function() {
localParams = [for( pt in td.params ) t.path+"."+Checker.typeStr(pt) => pt];
td.t = makeXmlType(t.type);
localParams = null;
});
types.set(t.path, CTTypedef(td));
case TAbstractdecl(a):
if( types.exists(a.path) ) return;
var ta : CAbstract = {
name : a.path,
params : [],
};
for( p in a.params )
ta.params.push(TParam(p));
todo.push(function() {
localParams = [for( t in ta.params ) a.path+"."+Checker.typeStr(t) => t];
localParams = null;
});
types.set(a.path, CTAbstract(ta));
}
}
function makeXmlType( t : haxe.rtti.CType.CType ) : TType {
return switch (t) {
case CUnknown: TUnresolved("Unknown");
case CEnum(name, params): getType(name,[for( t in params ) makeXmlType(t)]);
case CClass(name, params): getType(name,[for( t in params ) makeXmlType(t)]);
case CTypedef(name, params): getType(name,[for( t in params ) makeXmlType(t)]);
case CFunction(args, ret): TFun([for( a in args ) { name : a.name, opt : a.opt, t : makeXmlType(a.t) }], makeXmlType(ret));
case CAnonymous(fields):
inline function isOpt(m:haxe.rtti.CType.MetaData) {
if( m == null ) return false;
var b = false;
for( m in m ) if( m.name == ":optional" ) { b = true; break; }
return b;
}
TAnon([for( f in fields ) { name : f.name, t : makeXmlType(f.type), opt : isOpt(f.meta) }]);
case CDynamic(t): TDynamic;
case CAbstract(name, params):
switch( name ) {
default:
getType(name,[for( t in params ) makeXmlType(t)]);
}
}
}
function getType( name : String, ?args : Array<TType> ) : TType {
if( localParams != null ) {
var t = localParams.get(name);
if( t != null ) return t;
}
var t = resolve(name,args);
if( t == null )
return TUnresolved(name); // most likely private class
return t;
}
public function resolve( name : String, ?args : Array<TType> ) : TType {
if( name == "Null" ) {
if( args == null || args.length != 1 ) throw "Missing Null<T> parameter";
return TNull(args[0]);
}
var t = types.get(name);
if( t == null ) return null;
if( args == null ) args = [];
return switch( t ) {
case CTClass(c): TInst(c,args);
case CTEnum(e): TEnum(e,args);
case CTTypedef(t): TType(t,args);
case CTAbstract(a): TAbstract(a, args);
case CTAlias(t): t;
}
}
}
class Checker {
public var types : CheckerTypes;
var locals : Map<String,TType>;
var globals : Map<String,TType> = new Map();
var events : Map<String,TType> = new Map();
public var allowAsync : Bool;
public var allowReturn : Null<TType>;
public function new( ?types ) {
if( types == null ) types = new CheckerTypes();
this.types = types;
}
public function setGlobal( name : String, type : TType ) {
globals.set(name, type);
}
public function setEvent( name : String, type : TType ) {
events.set(name, type);
}
public function getGlobals() {
return globals;
}
public function check( expr : Expr, ?withType : WithType ) {
if( withType == null ) withType = NoValue;
locals = new Map();
return typeExpr(expr,withType);
}
inline function edef( e : Expr ) {
#if hscriptPos
return e.e;
#else
return e;
#end
}
inline function error( msg : String, curExpr : Expr ) {
var e = ECustom(msg);
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
throw e;
}
function saveLocals() {
return [for( k in locals.keys() ) k => locals.get(k)];
}
function makeType( t : CType, e : Expr ) : TType {
return switch (t) {
case CTPath(path, params):
var ct = types.resolve(path.join("."),params == null ? [] : [for( p in params ) makeType(t,e)]);
if( ct == null ) error("Unknown type "+path, e);
return ct;
case CTFun(args, ret):
var i = 0;
return TFun([for( a in args ) { name : "p"+(i++), opt : false, t : makeType(a,e) }], makeType(ret,e));
case CTAnon(fields):
return TAnon([for( f in fields ) { name : f.name, opt : false, t : makeType(f.t,e) }]);
case CTParent(t):
return makeType(t,e);
}
}
public static function typeStr( t : TType ) {
inline function makeArgs(args:Array<TType>) return args.length==0 ? "" : "<"+[for( t in args ) typeStr(t)].join(",")+">";
return switch (t) {
case TMono(r): r.r == null ? "Unknown" : typeStr(r.r);
case TInst(c, args): c.name + makeArgs(args);
case TEnum(e, args): e.name + makeArgs(args);
case TType(t, args): t.name + makeArgs(args);
case TAbstract(a, args): a.name + makeArgs(args);
case TFun(args, ret): "(" + [for( a in args ) (a.opt?"?":"")+(a.name == "" ? "" : a.name+":")+typeStr(a.t)].join(", ")+") -> "+typeStr(ret);
case TAnon(fields): "{" + [for( f in fields ) (f.opt?"?":"")+f.name+":"+typeStr(f.t)].join(", ")+"}";
case TParam(name): name;
case TNull(t): "Null<"+typeStr(t)+">";
case TUnresolved(name): "?"+name;
default: t.getName().substr(1);
}
}
function linkLoop( a : TType, t : TType ) {
if( t == a ) return true;
switch( t ) {
case TMono(r):
if( r.r == null ) return false;
return linkLoop(a,r.r);
case TEnum(_,tl), TInst(_,tl), TType(_,tl), TAbstract(_,tl):
for( t in tl )
if( linkLoop(a,t) )
return true;
return false;
case TFun(args,ret):
for( arg in args )
if( linkLoop(a,arg.t) )
return true;
return linkLoop(a,ret);
case TDynamic:
if( t == TDynamic )
return false;
return linkLoop(a,TDynamic);
case TAnon(fl):
for( f in fl )
if( linkLoop(a, f.t) )
return true;
return false;
default:
return false;
}
}
function link( a : TType, b : TType, r : { r : TType } ) {
if( linkLoop(a,b) )
return follow(b) == a;
if( b == TDynamic )
return true;
r.r = b;
return true;
}
function typeEq( t1 : TType, t2 : TType ) {
if( t1 == t2 )
return true;
switch( [t1,t2] ) {
case [TMono(r), _]:
if( r.r == null ) {
if( !link(t1,t2,r) )
return false;
r.r = t2;
return true;
}
return typeEq(r.r, t2);
case [_, TMono(r)]:
if( r.r == null ) {
if( !link(t2,t1,r) )
return false;
r.r = t1;
return true;
}
return typeEq(t1, r.r);
case [TType(t1,pl1),TType(t2,pl2)] if( t1 == t2 ):
for( i in 0...pl1.length )
if( !typeEq(pl1[i],pl2[i]) )
return false;
return true;
case [TType(t1,pl1), _]:
return typeEq(apply(t1.t, t1.params, pl1), t2);
case [_,TType(t2,pl2)]:
return typeEq(t1, apply(t2.t, t2.params, pl2));
case [TInst(cl1,pl1), TInst(cl2,pl2)] if( cl1 == cl2 ):
for( i in 0...pl1.length )
if( !typeEq(pl1[i],pl2[i]) )
return false;
return true;
case [TEnum(e1,pl1), TEnum(e2,pl2)] if( e1 == e2 ):
for( i in 0...pl1.length )
if( !typeEq(pl1[i],pl2[i]) )
return false;
return true;
case [TAbstract(a1,pl1), TAbstract(a2,pl2)] if( a1 == a2 ):
for( i in 0...pl1.length )
if( !typeEq(pl1[i],pl2[i]) )
return false;
return true;
case [TNull(t1), TNull(t2)]:
return typeEq(t1,t2);
case [TNull(t1), _]:
return typeEq(t1,t2);
case [_, TNull(t2)]:
return typeEq(t1,t2);
case [TFun(args1,r1), TFun(args2,r2)] if( args1.length == args2.length ):
for( i in 0...args1.length )
if( !typeEq(args1[i].t, args2[i].t) )
return false;
return typeEq(r1, r2);
case [TAnon(a1),TAnon(a2)] if( a1.length == a2.length ):
var m = new Map();
for( f in a2 )
m.set(f.name, f);
for( f1 in a1 ) {
var f2 = m.get(f1.name);
if( f2 == null ) return false;
if( !typeEq(f1.t,f2.t) )
return false;
}
return true;
default:
}
return false;
}
function tryUnify( t1 : TType, t2 : TType ) {
if( t1 == t2 )
return true;
switch( [t1,t2] ) {
case [TMono(r), _]:
if( r.r == null ) {
if( !link(t1,t2,r) )
return false;
r.r = t2;
return true;
}
return tryUnify(r.r, t2);
case [_, TMono(r)]:
if( r.r == null ) {
if( !link(t2,t1,r) )
return false;
r.r = t1;
return true;
}
return tryUnify(t1, r.r);
case [TType(t1,pl1), _]:
return tryUnify(apply(t1.t, t1.params, pl1), t2);
case [_,TType(t2,pl2)]:
return tryUnify(t1, apply(t2.t, t2.params, pl2));
case [TNull(t1), _]:
return tryUnify(t1,t2);
case [_, TNull(t2)]:
return tryUnify(t1,t2);
case [TFun(args1,r1),TFun(args2,r2)] if( args1.length == args2.length ):
for( i in 0...args1.length ) {
var a1 = args1[i];
var a2 = args2[i];
if( a2.opt && !a1.opt ) return false;
if( !tryUnify(a2.t, a1.t) ) return false;
}
return tryUnify(r1,r2);
case [_, TDynamic]:
return true;
case [TDynamic, _]:
return true;
case [TAnon(a1),TAnon(a2)]:
var m = new Map();
for( f in a1 )
m.set(f.name, f);
for( f2 in a2 ) {
var f1 = m.get(f2.name);
if( f1 == null ) {
if( f2.opt ) continue;
return false;
}
if( !typeEq(f1.t,f2.t) )
return false;
}
return true;
case [TInst(cl1,pl1), TInst(cl2,pl2)]:
while( cl1 != cl2 ) {
switch( cl1.superClass ) {
case null: return false;
case TInst(c, args):
pl1 = [for( a in args ) apply(a,cl1.params,pl1)];
cl1 = c;
default: throw "assert";
}
}
for( i in 0...pl1.length )
if( !typeEq(pl1[i],pl2[i]) )
return false;
return true;
case [TInt, TFloat]:
return true;
case [TFun(_), TAbstract({ name : "haxe.Function" },_)]:
return true;
default:
}
return typeEq(t1,t2);
}
function unify( t1 : TType, t2 : TType, e : Expr ) {
if( !tryUnify(t1,t2) )
error(typeStr(t1)+" should be "+typeStr(t2),e);
}
function apply( t : TType, params : Array<TType>, args : Array<TType> ) {
if( args.length != params.length ) throw "Invalid number of type parameters";
if( args.length == 0 )
return t;
var subst = new Map();
for( i in 0...params.length )
subst.set(params[i], args[i]);
function map(t:TType) {
var st = subst.get(t);
if( st != null ) return st;
return mapType(t,map);
}
return map(t);
}
public function mapType( t : TType, f : TType -> TType ) {
switch (t) {
case TMono(r):
if( r.r == null ) return t;
return f(t);
case TVoid, TInt, TFloat,TBool,TDynamic,TParam(_), TUnresolved(_):
return t;
case TEnum(_,[]), TInst(_,[]), TAbstract(_,[]), TType(_,[]):
return t;
case TNull(t):
return TNull(f(t));
case TInst(c, args):
return TInst(c, [for( t in args ) f(t)]);
case TEnum(e, args):
return TEnum(e, [for( t in args ) f(t)]);
case TType(t, args):
return TType(t, [for( t in args ) f(t)]);
case TAbstract(a, args):
return TAbstract(a, [for( t in args ) f(t)]);
case TFun(args, ret):
return TFun([for( a in args ) { name : a.name, opt : a.opt, t : f(a.t) }], f(ret));
case TAnon(fields):
return TAnon([for( af in fields ) { name : af.name, opt : af.opt, t : f(af.t) }]);
}
}
public function follow( t : TType ) {
return switch( t ) {
case TMono(r): if( r.r != null ) follow(r.r) else t;
case TType(t,args): apply(t.t, t.params, args);
case TNull(t): follow(t);
default: t;
}
}
public function getFields( t : TType ) {
var fields = [];
while( t != null ) {
t = follow(t);
switch( t ) {
case TInst(c, args):
for( fname in c.fields.keys() ) {
var f = c.fields.get(fname);
fields.push({ name : f.name, t : f.t });
}
t = c.superClass;
case TAnon(fl):
for( f in fl )
fields.push({ name : f.name, t : f.t });
break;
default:
}
}
return fields;
}
function getField( t : TType, f : String, e : Expr ) {
switch( follow(t) ) {
case TInst(c, args):
var cf = c.fields.get(f);
if( cf == null && allowAsync ) {
cf = c.fields.get("a_"+f);
if( cf != null ) {
var isPublic = true; // consider a_ prefixed as script specific
cf = { isPublic : isPublic, params : cf.params, name : cf.name, t : unasync(cf.t) };
if( cf.t == null ) cf = null;
}
}
if( cf == null ) {
if( c.superClass == null ) return null;
var ft = getField(c.superClass, f, e);
if( ft != null ) ft = apply(ft, c.params, args);
return ft;
}
if( !cf.isPublic )
error("Can't access private field "+f+" on "+c.name, e);
return apply(cf.t, c.params, args);
case TDynamic:
return makeMono();
case TAnon(fields):
for( af in fields )
if( af.name == f )
return af.t;
return null;
default:
return null;
}
}
public function unasync( t : TType ) : TType {
switch( follow(t) ) {
case TFun(args, ret) if( args.length > 0 ):
var rargs = args.copy();
switch( follow(rargs.shift().t) ) {
case TFun([r],_): return TFun(rargs,r.t);
default:
}
default:
}
return null;
}
function typeExprWith( expr : Expr, t : TType ) {
var et = typeExpr(expr, WithType(t));
unify(et, t, expr);
return t;
}
function makeMono() {
return TMono({r:null});
}
function makeIterator(t) : TType {
return TAnon([{ name : "next", opt : false, t : TFun([],t) }, { name : "hasNext", opt : false, t : TFun([],TBool) }]);
}
function mk(e,p) : Expr {
#if hscriptPos
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
#else
return e;
#end
}
function isString( t : TType ) {
t = follow(t);
return t.match(TInst({name:"String"},_));
}
function typeExpr( expr : Expr, withType : WithType ) : TType {
switch( edef(expr) ) {
case EConst(c):
return switch (c) {
case CInt(_): TInt;
case CFloat(_): TFloat;
case CString(_): types.t_string;
}
case EIdent(v):
var l = locals.get(v);
if( l != null ) return l;
var g = globals.get(v);
if( g != null ) return g;
if( allowAsync ) {
g = globals.get("a_"+v);
if( g != null ) g = unasync(g);
if( g != null ) return g;
}
switch( v ) {
case "null":
return makeMono();
case "true", "false":
return TBool;
case "trace":
return TDynamic;
default:
error("Unknown identifier "+v, expr);
}
case EBlock(el):
var t = TVoid;
var locals = saveLocals();
for( e in el )
t = typeExpr(e, e == el[el.length-1] ? withType : NoValue);
this.locals = locals;
return t;
case EVar(n, t, init):
var vt = t == null ? makeMono() : makeType(t, expr);
if( init != null ) {
var et = typeExpr(init, t == null ? Value : WithType(vt));
if( t == null ) vt = et else unify(et,vt, init);
}
locals.set(n, vt);
return TVoid;
case EParent(e):
return typeExpr(e,withType);
case ECall(e, params):
var ft = typeExpr(e, Value);
switch( follow(ft) ) {
case TFun(args, ret):
for( i in 0...params.length ) {
var a = args[i];
if( a == null ) error("Too many arguments", params[i]);
var t = typeExpr(params[i], WithType(a.t));
unify(t, a.t, params[i]);
}
for( i in params.length...args.length )
if( !args[i].opt )
error("Missing argument '"+args[i].name+"'", expr);
return ret;
case TDynamic:
for( p in params ) typeExpr(p,Value);
return TDynamic;
default:
error(typeStr(ft)+" cannot be called", e);
}
case EField(o, f):
var ot = typeExpr(o, Value);
var ft = getField(ot, f, expr);
if( ft == null )
error(typeStr(ot)+" has no field "+f, expr);
return ft;
case ECheckType(v, t):
var ct = makeType(t, expr);
var vt = typeExpr(v, WithType(ct));
unify(vt, ct, v);
return ct;
case EMeta(_, _, e):
return typeExpr(e, withType);
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
typeExprWith(cond, TBool);
var t1 = typeExpr(e1, withType);
if( e2 == null )
return t1;
var t2 = typeExpr(e2, withType);
if( withType == NoValue )
return TVoid;
if( tryUnify(t2,t1) )
return t1;
if( tryUnify(t1,t2) )
return t2;
unify(t2,t1,e2); // error
case EWhile(cond, e), EDoWhile(cond, e):
typeExprWith(cond,TBool);
typeExpr(e, NoValue);
return TVoid;
case EObject(fl):
switch( withType ) {
case WithType(follow(_) => TAnon(tfields)):
var map = [for( f in tfields ) f.name => f];
return TAnon([for( f in fl ) {
var ft = map.get(f.name);
if( ft == null ) error("Extra field "+f.name, f.e);
{ t : typeExprWith(f.e, ft.t), opt : false, name : f.name }
}]);
default:
return TAnon([for( f in fl ) { t : typeExpr(f.e, Value), opt : false, name : f.name }]);
}
case EBreak, EContinue:
return TVoid;
case EReturn(v):
var et = v == null ? TVoid : typeExpr(v, allowReturn == null ? Value : WithType(allowReturn));
if( allowReturn == null )
error("Return not allowed here", expr);
else
unify(et, allowReturn, v == null ? expr : v);
return TDynamic;
case EArrayDecl(el):
var et = null;
for( v in el ) {
var t = typeExpr(v, et == null ? Value : WithType(et));
if( et == null ) et = t else if( !tryUnify(t,et) ) {
if( tryUnify(et,t) ) et = t else unify(t,et,v);
}
}
if( et == null ) et = TDynamic;
return types.getType("Array",[et]);
case EArray(a, index):
typeExprWith(index, TInt);
var at = typeExpr(a, Value);
switch( follow(at) ) {
case TInst({ name : "Array"},[et]): return et;
default: error(typeStr(at)+" is not an Array", a);
}
case EThrow(e):
typeExpr(e, Value);
return TDynamic;
case EFunction(args, body, name, ret):
var tret = ret == null ? makeMono() : makeType(ret, expr);
var locals = saveLocals();
var oldRet = allowReturn;
allowReturn = tret;
var withArgs = null;
if( name != null && !withType.match(WithType(follow(_) => TFun(_))) ) {
var ev = events.get(name);
if( ev != null ) withType = WithType(ev);
}
switch( withType ) {
case WithType(follow(_) => TFun(args,ret)): withArgs = args; unify(tret,ret,expr);
default:
}
var targs = [for( i in 0...args.length ) {
var a = args[i];
var at = a.t == null ? makeMono() : makeType(a.t, expr);
if( withArgs != null && withArgs.length > i )
unify(withArgs[i].t, at, expr);
this.locals.set(a.name, at);
{ name : a.name, opt : a.opt, t : at };
}];
typeExpr(body,NoValue);
allowReturn = oldRet;
this.locals = locals;
var ft = TFun(targs,tret);
if( name != null )
locals.set(name, ft);
return ft;
case EUnop(op, _, e):
var et = typeExpr(e, Value);
switch( op ) {
case "++", "--":
unify(et,TInt,e);
return et;
case "!":
unify(et,TBool,e);
return et;
default:
}
case EFor(v, it, e):
var locals = saveLocals();
var itt = typeExpr(it, Value);
var vt = switch( follow(itt) ) {
case TInst({name:"Array"},[t]):
t;
default:
var t = makeMono();
var iter = makeIterator(t);
unify(itt,iter,it);
t;
}
this.locals.set(v, vt);
typeExpr(e, Value);
this.locals = locals;
return TVoid;
case EBinop(op, e1, e2):
switch( op ) {
case "&", "|", "^", ">>", ">>>", "<<":
typeExprWith(e1,TInt);
typeExprWith(e2,TInt);
return TInt;
case "=":
var vt = typeExpr(e1, Value);
typeExprWith(e2,vt);
return vt;
case "+":
var t1 = typeExpr(e1,WithType(TInt));
var t2 = typeExpr(e2,WithType(t1));
tryUnify(t1,t2);
switch( [follow(t1), follow(t2)]) {
case [TInt, TInt]:
return TInt;
case [TFloat, TInt], [TInt, TFloat], [TFloat, TFloat]:
return TFloat;
case [TDynamic, _], [_, TDynamic]:
return TDynamic;
case [t1,t2]:
if( isString(t1) || isString(t2) )
return types.t_string;
unify(t1, TFloat, e1);
unify(t2, TFloat, e2);
}
case "-", "*", "/", "%":
var t1 = typeExpr(e1,WithType(TInt));
var t2 = typeExpr(e2,WithType(t1));
if( !tryUnify(t1,t2) )
unify(t2,t1,e2);
switch( [follow(t1), follow(t2)]) {
case [TInt, TInt]:
if( op == "/" ) return TFloat;
return TInt;
case [TFloat|TDynamic, TInt|TDynamic], [TInt|TDynamic, TFloat|TDynamic], [TFloat, TFloat]:
return TFloat;
default:
unify(t1, TFloat, e1);
unify(t2, TFloat, e2);
}
case "&&", "||":
typeExprWith(e1,TBool);
typeExprWith(e2,TBool);
return TBool;
case "...":
typeExprWith(e1,TInt);
typeExprWith(e2,TInt);
return makeIterator(TInt);
case "==", "!=":
var t1 = typeExpr(e1,Value);
var t2 = typeExpr(e2,WithType(t1));
if( !tryUnify(t1,t2) )
unify(t2,t1,e2);
return TBool;
case ">", "<", ">=", "<=":
var t1 = typeExpr(e1,Value);
var t2 = typeExpr(e2,WithType(t1));
if( !tryUnify(t1,t2) )
unify(t2,t1,e2);
switch( follow(t1) ) {
case TInt, TFloat, TBool, TInst({name:"String"},_):
default:
error("Cannot compare "+typeStr(t1), expr);
}
return TBool;
default:
if( op.charCodeAt(op.length-1) == "=".code ) {
var t = typeExpr(mk(EBinop(op.substr(0,op.length-1),e1,e2),expr),withType);
return typeExpr(mk(EBinop("=",e1,e2),expr), withType);
}
error("Unsupported operation "+op, expr);
}
case ENew(cl, params):
case ETry(e, v, t, ecatch):
case ESwitch(e, cases, defaultExpr):
}
error("Don't know how to type "+edef(expr).getName(), expr);
}
}
+4
View File
@@ -40,6 +40,7 @@ typedef Expr = {
}
enum ExprDef {
#else
typedef ExprDef = Expr;
enum Expr {
#end
EConst( c : Const );
@@ -68,6 +69,7 @@ enum Expr {
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr);
EDoWhile( cond : Expr, e : Expr);
EMeta( name : String, args : Array<Expr>, e : Expr );
ECheckType( e : Expr, t : CType );
}
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
@@ -79,6 +81,8 @@ enum CType {
CTFun( args : Array<CType>, ret : CType );
CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
CTParent( t : CType );
CTOpt( t : CType );
CTNamed( n : String, t : CType );
}
#if hscriptPos
+7 -13
View File
@@ -122,7 +122,7 @@ class Interp {
function assign( e1 : Expr, e2 : Expr ) : Dynamic {
var v = expr(e2);
switch( edef(e1) ) {
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
if( l == null )
@@ -154,7 +154,7 @@ class Interp {
function evalAssignOp(op,fop,e1,e2) : Dynamic {
var v;
switch( edef(e1) ) {
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
v = fop(expr(e1),expr(e2));
@@ -280,14 +280,6 @@ class Interp {
}
}
inline function edef( e : Expr ) {
#if hscriptPos
return e.e;
#else
return e;
#end
}
inline function error(e : #if hscriptPos ErrorDef #else Error #end, rethrow=false ) : Dynamic {
#if hscriptPos var e = new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line); #end
if( rethrow ) this.rethrow(e) else throw e;
@@ -372,7 +364,7 @@ class Interp {
for( p in params )
args.push(expr(p));
switch( edef(e) ) {
switch( Tools.expr(e) ) {
case EField(e,f):
var obj = expr(e);
if( obj == null ) error(EInvalidAccess(f));
@@ -468,7 +460,7 @@ class Interp {
}
return f;
case EArrayDecl(arr):
if (arr.length > 0 && edef(arr[0]).match(EBinop("=>", _))) {
if (arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _))) {
var isAllString:Bool = true;
var isAllInt:Bool = true;
var isAllObject:Bool = true;
@@ -476,7 +468,7 @@ class Interp {
var keys:Array<Dynamic> = [];
var values:Array<Dynamic> = [];
for (e in arr) {
switch(edef(e)) {
switch(Tools.expr(e)) {
case EBinop("=>", eKey, eValue): {
var key:Dynamic = expr(eKey);
var value:Dynamic = expr(eValue);
@@ -574,6 +566,8 @@ class Interp {
return val;
case EMeta(_, _, e):
return expr(e);
case ECheckType(e,_):
return expr(e);
}
return null;
}
+9
View File
@@ -113,6 +113,7 @@ class Macro {
function convertType( t : Expr.CType ) : ComplexType {
return switch( t ) {
case CTOpt(t): TOptional(convertType(t));
case CTPath(pack, args):
var params = [];
if( args != null )
@@ -127,6 +128,12 @@ class Macro {
case CTParent(t): TParent(convertType(t));
case CTFun(args, ret):
TFunction(map(args,convertType), convertType(ret));
case CTNamed(name, convertType(_) => ct):
#if (haxe_ver >= 4)
TNamed(name, ct);
#else
ct;
#end
case CTAnon(fields):
var tf = [];
for( f in fields ) {
@@ -238,6 +245,8 @@ class Macro {
case EMeta(m, params, esub):
var mpos = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
case ECheckType(e, t):
ECheckType(convert(e), convertType(t));
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
}
+116 -10
View File
@@ -150,7 +150,7 @@ class Parser {
}
public function invalidChar(c) {
error(EInvalidChar(c), readPos, readPos);
error(EInvalidChar(c), readPos-1, readPos-1);
}
function initParser( origin ) {
@@ -359,8 +359,31 @@ class Parser {
return parseExprNext(mk(EConst(c)));
case TPOpen:
var e = parseExpr();
ensure(TPClose);
return parseExprNext(mk(EParent(e),p1,tokenMax));
tk = token();
switch( tk ) {
case TPClose:
return parseExprNext(mk(EParent(e),p1,tokenMax));
case TDoubleDot:
var t = parseType();
tk = token();
switch( tk ) {
case TPClose:
return parseExprNext(mk(ECheckType(e,t),p1,tokenMax));
case TComma:
switch( expr(e) ) {
case EIdent(v): return parseLambda([{ name : v, t : t }], pmin(e));
default:
}
default:
}
case TComma:
switch( expr(e) ) {
case EIdent(v): return parseLambda([{name:v}], pmin(e));
default:
}
default:
}
return unexpected(tk);
case TBrOpen:
tk = token();
switch( tk ) {
@@ -450,6 +473,25 @@ class Parser {
}
}
function parseLambda( args : Array<Argument>, pmin ) {
while( true ) {
var id = getIdent();
var t = maybe(TDoubleDot) ? parseType() : null;
args.push({ name : id, t : t });
var tk = token();
switch( tk ) {
case TComma:
case TPClose:
break;
default:
unexpected(tk);
}
}
ensureToken(TOp("->"));
var eret = parseExpr();
return mk(EFunction(args, mk(EReturn(eret),pmin)), pmin);
}
function parseMetaArgs() {
var tk = token();
if( tk != TPOpen ) {
@@ -709,6 +751,21 @@ class Parser {
var tk = token();
switch( tk ) {
case TOp(op):
if( op == "->" ) {
// single arg reinterpretation of `f -> e` , `(f) -> e` and `(f:T) -> e`
switch( expr(e1) ) {
case EIdent(i), EParent(expr(_) => EIdent(i)):
var eret = parseExpr();
return mk(EFunction([{ name : i }], mk(EReturn(eret),pmin(eret))), pmin(e1));
case ECheckType(expr(_) => EIdent(i), t):
var eret = parseExpr();
return mk(EFunction([{ name : i, t : t }], mk(EReturn(eret),pmin(eret))), pmin(e1));
default:
}
unexpected(tk);
}
if( unops.get(op) ) {
if( isBlock(e1) || switch(expr(e1)) { case EParent(_): true; default: false; } ) {
push(tk);
@@ -737,8 +794,7 @@ class Parser {
}
}
function parseFunctionDecl() {
ensure(TPOpen);
function parseFunctionArgs() {
var args = new Array();
var tk = token();
if( tk != TPClose ) {
@@ -774,10 +830,16 @@ class Parser {
unexpected(tk);
}
}
}
}
return args;
}
function parseFunctionDecl() {
ensure(TPOpen);
var args = parseFunctionArgs();
var ret = null;
if( allowTypes ) {
tk = token();
var tk = token();
if( tk != TDoubleDot )
push(tk);
else
@@ -837,9 +899,53 @@ class Parser {
}
return parseTypeNext(CTPath(path, params));
case TPOpen:
var t = parseType();
ensure(TPClose);
return parseTypeNext(CTParent(t));
var a = token(),
b = token();
push(b);
push(a);
function withReturn(args) {
switch token() { // I think it wouldn't hurt if ensure used enumEq
case TOp('->'):
case t: unexpected(t);
}
return CTFun(args, parseType());
}
switch [a, b] {
case [TPClose, _] | [TId(_), TDoubleDot]:
var args = [for (arg in parseFunctionArgs()) {
switch arg.value {
case null:
case v:
error(ECustom('Default values not allowed in function types'), #if hscriptPos v.pmin, v.pmax #else 0, 0 #end);
}
CTNamed(arg.name, if (arg.opt) CTOpt(arg.t) else arg.t);
}];
return withReturn(args);
default:
var t = parseType();
return switch token() {
case TComma:
var args = [t];
while (true) {
args.push(parseType());
if (!maybe(TComma)) break;
}
ensure(TPClose);
withReturn(args);
case TPClose:
parseTypeNext(CTParent(t));
case t: unexpected(t);
}
}
case TBrOpen:
var fields = [];
var meta = null;
+22
View File
@@ -48,6 +48,9 @@ class Printer {
function type( t : CType ) {
switch( t ) {
case CTOpt(t):
add('?');
type(t);
case CTPath(path, params):
add(path.join("."));
if( params != null ) {
@@ -59,6 +62,19 @@ class Printer {
}
add(">");
}
case CTNamed(name, t):
add(name);
add(':');
type(t);
case CTFun(args, ret) if (Lambda.exists(args, function (a) return a.match(CTNamed(_, _)))):
add('(');
for (a in args)
switch a {
case CTNamed(_, _): type(a);
default: type(CTNamed('_', a));
}
add(')->');
type(ret);
case CTFun(args, ret):
if( args.length == 0 )
add("Void -> ");
@@ -300,6 +316,12 @@ class Printer {
}
add(" ");
expr(e);
case ECheckType(e, t):
add("(");
expr(e);
add(" : ");
addType(t);
add(")");
}
}
+20 -8
View File
@@ -25,7 +25,7 @@ import hscript.Expr;
class Tools {
public static function iter( e : Expr, f : Expr -> Void ) {
switch( #if hscriptPos e.e #else e #end ) {
switch( expr(e) ) {
case EConst(_), EIdent(_):
case EVar(_, _, e): if( e != null ) f(e);
case EParent(e): f(e);
@@ -56,16 +56,13 @@ class Tools {
}
if( def != null ) f(def);
case EMeta(name, args, e): if( args != null ) for( a in args ) f(a); f(e);
case ECheckType(e,_): f(e);
}
}
public static function map( e : Expr, f : Expr -> Expr ) {
#if hscriptPos
var edef = switch( e.e ) {
#else
return switch( e ) {
#end
case EConst(_), EIdent(_), EBreak, EContinue: #if hscriptPos e.e #else e #end;
var edef = switch( expr(e) ) {
case EConst(_), EIdent(_), EBreak, EContinue: expr(e);
case EVar(n, t, e): EVar(n, t, if( e != null ) f(e) else null);
case EParent(e): EParent(f(e));
case EBlock(el): EBlock([for( e in el ) f(e)]);
@@ -88,9 +85,24 @@ class Tools {
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
case ESwitch(e, cases, def): ESwitch(f(e), [for( c in cases ) { values : [for( v in c.values ) f(v)], expr : f(c.expr) } ], def == null ? null : f(def));
case EMeta(name, args, e): EMeta(name, args == null ? null : [for( a in args ) f(a)], f(e));
case ECheckType(e,t): ECheckType(f(e), t);
}
return mk(edef, e);
}
public static inline function expr( e : Expr ) : ExprDef {
#if hscriptPos
return { e : edef, pmin : e.pmin, pmax : e.pmax, origin : e.origin, line : e.line };
return e.e;
#else
return e;
#end
}
public static inline function mk( e : ExprDef, p : Expr ) {
#if hscriptPos
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
#else
return e;
#end
}
+1
View File
@@ -1,4 +1,5 @@
@echo off
set PATH="C:\Program Files\7-Zip";%PATH%
rm -rf release
mkdir release
cp haxelib.json README.md extraParams.hxml release