Compare commits

...
16 Commits
Author SHA1 Message Date
Nicolas Cannasse 09923651d8 2.1.0 2017-07-18 18:47:06 +02:00
Nicolas Cannasse 7ca5b85051 added optional metadata support in toplevel expr and anon types 2017-07-18 17:28:15 +02:00
Andy Li a34faf8a26 [TravisCI] use newer versions of osx and flashplayer 2017-06-27 17:24:01 +08:00
Andy Li 7708159e92 [TravisCI] haxe development tmp fix
https://github.com/HaxeFoundation/haxe/pull/6253#issuecomment-300717995
2017-05-14 00:11:15 +08:00
Andy Li 7851d48e82 [CI] test with Haxe 3.4.2 2017-05-13 23:30:51 +08:00
Andy Li f320b4fa9b [AppVeyor] fix php install 2017-05-11 09:54:17 +08:00
Andy Li b3ea81f4e8 [TravisCI] use Trusty 2017-05-11 09:38:27 +08:00
Nicolas Cannasse ace61e589d moved build scripts in /bin 2017-05-06 11:14:45 +02:00
Tom CoxonandNicolas Cannasse 51506e0400 Add filename / line number error reporting with -D hscriptPos (#57) 2017-01-02 17:48:48 +01:00
Andy Li 4a1432b01d [TravisCI] flash-player-debuger has been moved to caskroom/versions 2016-10-16 23:35:42 +08:00
Pavel AlexandrovandNicolas Cannasse aa16393c1b Add do ... while support. (#51)
Add `do ... while` support.
2016-10-03 12:24:53 +02:00
Andy Li 55ed2c0d5c Avoid using Array#sort in test.
There is some problem with the php target. Let's workaround it for now.
2016-10-01 15:14:58 +08:00
Andy Li 6c588733d9 [CI] updated flash player install 2016-10-01 12:14:25 +08:00
Andy LiandGitHub d72411632d [TravisCI] use OSX 10.10 2016-10-01 10:59:02 +08:00
romamikandNicolas Cannasse a1af1ec29e Added Map support (#49)
Map support
2016-09-28 13:04:18 +02:00
Andy Li bdd55483f9 add a line in release.bat to remind Nicolas to create a git tag 2016-08-21 22:33:13 +08:00
33 changed files with 487 additions and 159 deletions
+15 -17
View File
@@ -4,8 +4,12 @@ os:
- linux - linux
- osx - osx
sudo: false
dist: trusty
haxe: haxe:
- "3.2.1" - "3.2.1"
- "3.4.2"
- development - development
addons: addons:
@@ -22,14 +26,8 @@ addons:
- mono-devel - mono-devel
- mono-mcs - mono-mcs
# Flash # Flash
- libcurl3:i386 - libglib2.0
- libglib2.0-0:i386 - libfreetype6
- libx11-6:i386
- libxext6:i386
- libxt6:i386
- libxcursor1:i386
- libnss3:i386
- libgtk2.0-0:i386
install: install:
# os-specific config # os-specific config
@@ -62,12 +60,12 @@ script:
- pushd script && - pushd script &&
haxe build.hxml && haxe build.hxml &&
popd popd
- haxe build-interp.hxml - haxe bin/build-interp.hxml
- haxe build-neko.hxml && neko bin/Test.n - haxe bin/build-neko.hxml && neko bin/Test.n
- haxe build-js.hxml && node bin/Test.js - haxe bin/build-js.hxml && node bin/Test.js
- haxe build-java.hxml && java -jar bin/Test.jar - haxe bin/build-java.hxml && java -jar bin/Test.jar
- haxe build-cs.hxml && mono bin/bin/Test.exe - haxe bin/build-cs.hxml && mono bin/bin/Test.exe
- haxe build-cpp.hxml && ./bin/Test - haxe bin/build-cpp.hxml && ./bin/Test
- haxe build-flash.hxml -D fdb && haxe flash/run.hxml bin/Test.swf - haxe bin/build-flash.hxml -D fdb && haxe flash/run.hxml bin/Test.swf
- haxe build-php.hxml && php bin/index.php - haxe bin/build-php.hxml && php bin/index.php
- haxe build-python.hxml && python3 bin/Test.py - haxe bin/build-python.hxml && python3 bin/Test.py
+53
View File
@@ -1,3 +1,5 @@
import haxe.ds.EnumValueMap;
import haxe.ds.Option;
import hscript.Macro; import hscript.Macro;
import haxe.unit.*; import haxe.unit.*;
@@ -73,6 +75,7 @@ class Test extends TestCase {
assertScript("(function(a,b) return a + b)(4,5)",9); assertScript("(function(a,b) return a + b)(4,5)",9);
assertScript("var y = 0; var add = function(a) y += a; add(5); add(3); y", 8); assertScript("var y = 0; var add = function(a) y += a; add(5); add(3); y", 8);
assertScript("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10); assertScript("var a = [1,[2,[3,[4,null]]]]; var t = 0; while( a != null ) { t += a[0]; a = a[1]; }; t",10);
assertScript("var a = false; do { a = true; } while (!a); a;",true);
assertScript("var t = 0; for( x in 1...10 ) t += x; t", 45); assertScript("var t = 0; for( x in 1...10 ) t += x; t", 45);
#if haxe3 #if haxe3
assertScript("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45); assertScript("var t = 0; for( x in new IntIterator(1,10) ) t +=x; t", 45);
@@ -95,6 +98,56 @@ class Test extends TestCase {
assertScript("x=1;x*=-2", -2); assertScript("x=1;x*=-2", -2);
} }
function testMap():Void {
var objKey = { ok:true };
var vars = {
stringMap: ["foo" => "Foo", "bar" => "Bar"],
intMap:[100 => "one hundred"],
objKey: objKey,
objMap:[objKey => "ok"],
enumKey:Option.Some("some"),
enumMap:new EnumValueMap<Option<String>, String>(),
stringIntMap: ["foo" => 100]
}
vars.enumMap.set(vars.enumKey, "ok");
assertScript('stringMap["foo"]', "Foo", vars);
assertScript('intMap[100]', "one hundred", vars);
assertScript('objMap[objKey]', "ok", vars);
assertScript('enumMap[enumKey]', "ok", vars);
assertScript('stringMap["a"] = "A"; stringMap["a"]', "A", vars);
assertScript('intMap[200] = objMap[{foo:false}] = enumMap[enumKey] = "A"', "A", vars);
assertEquals('A', vars.intMap[200]);
assertEquals('A', vars.enumMap.get(vars.enumKey));
for (key in vars.objMap.keys()) {
if (key != objKey) {
assertEquals(false, (key:Dynamic).foo);
assertEquals('A', vars.objMap[key]);
}
}
assertScript('
var keys = [];
for (key in stringMap.keys()) keys.push(key);
keys.join("_");
', {
var keys = [];
for (key in vars.stringMap.keys()) keys.push(key);
keys.join("_");
}, vars);
assertScript('stringMap.remove("foo"); stringMap.exists("foo");', false, vars);
assertScript('stringMap["foo"] = "a"; stringMap["foo"] += "b"', 'ab', vars);
assertEquals('ab', vars.stringMap['foo']);
assertScript('stringIntMap["foo"]++', 100, vars);
assertEquals(101, vars.stringIntMap['foo']);
assertScript('++stringIntMap["foo"]', 102, vars);
assertScript('var newMap = ["foo"=>"foo"]; newMap["foo"];', 'foo', vars);
#if (!php || (haxe_ver >= 3.3))
assertScript('var newMap = [enumKey=>"foo"]; newMap[enumKey];', 'foo', vars);
#end
assertScript('var newMap = [{a:"a"}=>"foo", objKey=>"bar"]; newMap[objKey];', 'bar', vars);
}
static function main() { static function main() {
var runner = new TestRunner(); var runner = new TestRunner();
runner.add(new Test()); runner.add(new Test());
+12 -10
View File
@@ -6,13 +6,15 @@ environment:
install: install:
# Install the haxe chocolatey package (https://chocolatey.org/packages/haxe) # Install the haxe chocolatey package (https://chocolatey.org/packages/haxe)
- cinst haxe -version 3.2.1 -y - cinst haxe -version 3.4.2 -y
- RefreshEnv - RefreshEnv
# Setup haxelib # Setup haxelib
- mkdir "%HAXELIB_ROOT%" - mkdir "%HAXELIB_ROOT%"
- haxelib setup "%HAXELIB_ROOT%" - haxelib setup "%HAXELIB_ROOT%"
# Install test dependencies # Install test dependencies
- ps: Set-Service wuauserv -StartupType Manual
- cinst php -y - cinst php -y
- RefreshEnv
# Download and setup a standalone flash player debugger # Download and setup a standalone flash player debugger
- haxe flash/install.hxml - haxe flash/install.hxml
# Install project dependencies # Install project dependencies
@@ -28,12 +30,12 @@ test_script:
- pushd script && - pushd script &&
haxe build.hxml && haxe build.hxml &&
popd popd
- haxe build-interp.hxml - haxe bin/build-interp.hxml
- haxe build-neko.hxml && neko bin\Test.n - haxe bin/build-neko.hxml && neko bin\Test.n
- haxe build-js.hxml && node bin\Test.js - haxe bin/build-js.hxml && node bin\Test.js
- haxe build-java.hxml && java -jar bin\Test.jar - haxe bin/build-java.hxml && java -jar bin\Test.jar
- haxe build-cs.hxml && bin\bin\Test.exe - haxe bin/build-cs.hxml && bin\bin\Test.exe
- haxe build-cpp.hxml && bin\Test.exe - haxe bin/build-cpp.hxml && bin\Test.exe
- haxe build-flash.hxml -D fdb && haxe flash\run.hxml bin\Test.swf - haxe bin/build-flash.hxml -D fdb && haxe flash\run.hxml bin\Test.swf
- haxe build-php.hxml && C:\tools\php\php.exe bin\index.php - haxe bin/build-php.hxml && php bin\index.php
- haxe build-python.hxml && C:\Python34-x64\python.exe bin\Test.py - haxe bin/build-python.hxml && C:\Python34-x64\python.exe bin\Test.py
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
-cpp bin
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
-cs bin
+1 -1
View File
@@ -1,4 +1,4 @@
build-each.hxml bin/build-each.hxml
-swf bin/Test.swf -swf bin/Test.swf
-swf-header 800:600:30:FFFFFF -swf-header 800:600:30:FFFFFF
--flash-strict --flash-strict
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
--interp
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
-java bin
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
-js bin/Test.js
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
-neko bin/Test.n
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
-php bin
+2
View File
@@ -0,0 +1,2 @@
bin/build-each.hxml
-python bin/Test.py
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
-cpp bin
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
-cs bin
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
--interp
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
-java bin
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
-js bin/Test.js
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
-neko bin/Test.n
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
-php bin
-2
View File
@@ -1,2 +0,0 @@
build-each.hxml
-python bin/Test.py
+24 -7
View File
@@ -1,5 +1,4 @@
import Sys.*; import Sys.*;
import sys.FileSystem.*;
import sys.io.File.*; import sys.io.File.*;
import haxe.*; import haxe.*;
import haxe.io.*; import haxe.io.*;
@@ -8,11 +7,11 @@ class Install {
// https://www.adobe.com/support/flashplayer/downloads.html // https://www.adobe.com/support/flashplayer/downloads.html
static var fpDownload(default, never) = switch (systemName()) { static var fpDownload(default, never) = switch (systemName()) {
case "Linux": case "Linux":
"https://fpdownload.macromedia.com/pub/flashplayer/updaters/11/flashplayer_11_sa_debug.i386.tar.gz"; "http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flash_player_sa_linux_debug.x86_64.tar.gz";
case "Mac": case "Mac":
"https://fpdownload.macromedia.com/pub/flashplayer/updaters/19/flashplayer_19_sa_debug.dmg"; "http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flashplayer_25_sa_debug.dmg";
case "Windows": case "Windows":
"http://fpdownload.macromedia.com/pub/flashplayer/updaters/19/flashplayer_19_sa_debug.exe"; "http://fpdownload.macromedia.com/pub/flashplayer/updaters/25/flashplayer_25_sa_debug.exe";
case _: case _:
throw "unsupported system"; throw "unsupported system";
} }
@@ -47,9 +46,12 @@ class Install {
if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0) if (command("tar", ["-xf", Path.withoutDirectory(fpDownload), "-C", "flash"]) != 0)
throw "failed to extract flash player"; throw "failed to extract flash player";
case "Mac": case "Mac":
if (command("brew", ["install", "caskroom/cask/brew-cask"]) != 0) // https://github.com/caskroom/homebrew-cask/pull/15381
throw "failed to brew install caskroom/cask/brew-cask"; if (command("brew", ["uninstall", "--force", "brew-cask"]) != 0)
if (command("brew", ["cask", "install", "flash-player-debugger", "--appdir=flash"]) != 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"; throw "failed to install flash-player-debugger";
case "Windows": case "Windows":
// Download flash player // Download flash player
@@ -74,4 +76,19 @@ class Install {
}; };
http.customRequest(false, write(saveAs)); 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';
}
}
}
} }
+17 -5
View File
@@ -21,22 +21,34 @@ class Run {
var exitCode = switch (systemName()) { var exitCode = switch (systemName()) {
case "Linux": case "Linux":
// The flash player has some issues with unexplained crashes, // The flash player has some issues with unexplained crashes,
// but if it runs about 8 times, it should succeed one of those... // but if it runs about 16 times, it should succeed one of those...
var c = -1; var c = -1;
for (i in 0...8) { for (i in 0...16) {
if ((c = command("xvfb-run", ["flash/flashplayerdebugger", swf])) == 0) if ((c = command("xvfb-run", ["-a", "flash/flashplayerdebugger", swf])) == 0)
break; break;
println('retry... (${i+1})'); println('retry... (${i+1})');
sleep(1.5);
} }
c; c;
case "Mac": case "Mac":
command("flash/Flash Player Debugger.app/Contents/MacOS/Flash Player Debugger", [fullPath(swf)]); command("/Applications/Flash Player Debugger.app/Contents/MacOS/Flash Player Debugger", [fullPath(swf)]);
case "Windows": case "Windows":
command("flash\\flashplayer.exe", [fullPath(swf)]); command("flash\\flashplayer.exe", [fullPath(swf)]);
case _: case _:
throw "unsupported platform"; throw "unsupported platform";
} }
println(getContent(flashlog)); 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); exit(exitCode);
} }
} }
+2 -2
View File
@@ -3,7 +3,7 @@
"url": "https://github.com/HaxeFoundation/hscript", "url": "https://github.com/HaxeFoundation/hscript",
"license": "MIT", "license": "MIT",
"description": "Haxe Script is a scripting engine for a subset of the Haxe language", "description": "Haxe Script is a scripting engine for a subset of the Haxe language",
"version": "2.0.7", "version": "2.1.0",
"releasenote": "fixes for haxe 3.3, use property access by default", "releasenote": "added metadata support",
"contributors": ["ncannasse"] "contributors": ["ncannasse"]
} }
+1 -1
View File
@@ -1 +1 @@
build-flash.hxml bin/build-flash.hxml
+26 -7
View File
@@ -74,6 +74,13 @@ class Bytes {
return strings[id]; return strings[id];
} }
function doEncodeInt(v: Int) {
bout.addByte(v & 0xFF);
bout.addByte((v >> 8) & 0xFF);
bout.addByte((v >> 16) & 0xFF);
bout.addByte(v >>> 24);
}
function doEncodeConst( c : Const ) { function doEncodeConst( c : Const ) {
switch( c ) { switch( c ) {
case CInt(v): case CInt(v):
@@ -82,10 +89,7 @@ class Bytes {
bout.addByte(v); bout.addByte(v);
} else { } else {
bout.addByte(1); bout.addByte(1);
bout.addByte(v & 0xFF); doEncodeInt(v);
bout.addByte((v >> 8) & 0xFF);
bout.addByte((v >> 16) & 0xFF);
bout.addByte(v >>> 24);
} }
#if !haxe3 #if !haxe3
case CInt32(v): case CInt32(v):
@@ -105,13 +109,18 @@ class Bytes {
} }
} }
function doDecodeInt() {
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16) | (bin.get(pin+3) << 24);
pin += 4;
return i;
}
function doDecodeConst() { function doDecodeConst() {
return switch( bin.get(pin++) ) { return switch( bin.get(pin++) ) {
case 0: case 0:
CInt(bin.get(pin++)); CInt(bin.get(pin++));
case 1: case 1:
var i = bin.get(pin) | (bin.get(pin+1) << 8) | (bin.get(pin+2) << 16) | (bin.get(pin+3) << 24); var i = doDecodeInt();
pin += 4;
CInt(i); CInt(i);
case 2: case 2:
CFloat( Std.parseFloat(doDecodeString()) ); CFloat( Std.parseFloat(doDecodeString()) );
@@ -131,6 +140,8 @@ class Bytes {
function doEncode( e : Expr ) { function doEncode( e : Expr ) {
#if hscriptPos #if hscriptPos
doEncodeString(e.origin);
doEncodeInt(e.line);
var e = e.e; var e = e.e;
#end #end
bout.addByte(Type.enumIndex(e)); bout.addByte(Type.enumIndex(e));
@@ -177,6 +188,9 @@ class Bytes {
case EWhile(cond,e): case EWhile(cond,e):
doEncode(cond); doEncode(cond);
doEncode(e); doEncode(e);
case EDoWhile(cond,e):
doEncode(cond);
doEncode(e);
case EFor(v,it,e): case EFor(v,it,e):
doEncodeString(v); doEncodeString(v);
doEncode(it); doEncode(it);
@@ -238,7 +252,9 @@ class Bytes {
function doDecode() : Expr { function doDecode() : Expr {
#if hscriptPos #if hscriptPos
return { e : _doDecode(), pmin : 0, pmax : 0 }; var origin = doDecodeString();
var line = doDecodeInt();
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
} }
function _doDecode() : ExprDef { function _doDecode() : ExprDef {
#end #end
@@ -347,6 +363,9 @@ class Bytes {
} }
var def = doDecode(); var def = doDecode();
ESwitch(e, cases, def); ESwitch(e, cases, def);
case 24:
var cond = doDecode();
EDoWhile(cond,doDecode());
case 255: case 255:
null; null;
default: default:
+46 -2
View File
@@ -38,6 +38,8 @@ typedef Expr = {
var e : ExprDef; var e : ExprDef;
var pmin : Int; var pmin : Int;
var pmax : Int; var pmax : Int;
var origin : String;
var line : Int;
} }
enum ExprDef { enum ExprDef {
#else #else
@@ -67,14 +69,18 @@ enum Expr {
EObject( fl : Array<{ name : String, e : Expr }> ); EObject( fl : Array<{ name : String, e : Expr }> );
ETernary( cond : Expr, e1 : Expr, e2 : Expr ); ETernary( cond : Expr, e1 : Expr, e2 : Expr );
ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr); ESwitch( e : Expr, cases : Array<{ values : Array<Expr>, expr : Expr }>, ?defaultExpr : Expr);
EDoWhile( cond : Expr, e : Expr);
EMeta( name : String, args : Array<Expr>, e : Expr );
} }
typedef Argument = { name : String, ?t : CType, ?opt : Bool }; typedef Argument = { name : String, ?t : CType, ?opt : Bool };
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
enum CType { enum CType {
CTPath( path : Array<String>, ?params : Array<CType> ); CTPath( path : Array<String>, ?params : Array<CType> );
CTFun( args : Array<CType>, ret : CType ); CTFun( args : Array<CType>, ret : CType );
CTAnon( fields : Array<{ name : String, t : CType }> ); CTAnon( fields : Array<{ name : String, t : CType, ?meta : Metadata }> );
CTParent( t : CType ); CTParent( t : CType );
} }
@@ -83,10 +89,48 @@ class Error {
public var e : ErrorDef; public var e : ErrorDef;
public var pmin : Int; public var pmin : Int;
public var pmax : Int; public var pmax : Int;
public function new(e, pmin, pmax) { public var origin : String;
public var line : Int;
public function new(e, pmin, pmax, origin, line) {
this.e = e; this.e = e;
this.pmin = pmin; this.pmin = pmin;
this.pmax = pmax; this.pmax = pmax;
this.origin = origin;
this.line = line;
}
private function errorDefToString(): String {
switch (e) {
case EInvalidChar(c):
return "Invalid character: '"+String.fromCharCode(c)+"' ("+c+")";
case EUnexpected(s):
return "Unexpected token: \""+s+"\"";
case EUnterminatedString:
return "Unterminated string";
case EUnterminatedComment:
return "Unterminated comment";
case EUnknownVariable(v):
return "Unknown variable: "+v;
case EInvalidIterator(v):
return "Invalid iterator: "+v;
case EInvalidOp(op):
return "Invalid operator: "+op;
case EInvalidAccess(f):
return "Invalid access to field "+f;
}
}
public function toString(): String {
var message = errorDefToString();
message = origin + ":" + line + ": " + message;
return message;
} }
} }
enum ErrorDef { enum ErrorDef {
+153 -37
View File
@@ -23,6 +23,7 @@
* DAMAGE. * DAMAGE.
*/ */
package hscript; package hscript;
import haxe.PosInfos;
import hscript.Expr; import hscript.Expr;
private enum Stop { private enum Stop {
@@ -63,10 +64,18 @@ class Interp {
variables.set("null",null); variables.set("null",null);
variables.set("true",true); variables.set("true",true);
variables.set("false",false); variables.set("false",false);
variables.set("trace",function(e) haxe.Log.trace(Std.string(e),cast { fileName : "hscript", lineNumber : 0 })); variables.set("trace",function(e) haxe.Log.trace(Std.string(e), posInfos()));
initOps(); initOps();
} }
public function posInfos(): PosInfos {
#if hscriptPos
if (curExpr != null)
return cast { fileName : curExpr.origin, lineNumber : curExpr.line };
#end
return cast { fileName : "hscript", lineNumber : 0 };
}
function initOps() { function initOps() {
var me = this; var me = this;
#if haxe3 #if haxe3
@@ -119,8 +128,16 @@ class Interp {
l.r = v; l.r = v;
case EField(e,f): case EField(e,f):
v = set(expr(e),f,v); v = set(expr(e),f,v);
case EArray(e,index): case EArray(e, index):
expr(e)[expr(index)] = v; var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
setMapValue(arr, index, v);
}
else {
arr[index] = v;
}
default: default:
error(EInvalidOp("=")); error(EInvalidOp("="));
} }
@@ -146,11 +163,17 @@ class Interp {
var obj = expr(e); var obj = expr(e);
v = fop(get(obj,f),expr(e2)); v = fop(get(obj,f),expr(e2));
v = set(obj,f,v); v = set(obj,f,v);
case EArray(e,index): case EArray(e, index):
var arr = expr(e); var arr:Dynamic = expr(e);
var index = expr(index); var index:Dynamic = expr(index);
v = fop(arr[index],expr(e2)); if (isMap(arr)) {
arr[index] = v; v = fop(getMapValue(arr, index), expr(e2));
setMapValue(arr, index, v);
}
else {
v = fop(arr[index],expr(e2));
arr[index] = v;
}
default: default:
return error(EInvalidOp(op)); return error(EInvalidOp(op));
} }
@@ -181,16 +204,29 @@ class Interp {
} else } else
set(obj,f,v + delta); set(obj,f,v + delta);
return v; return v;
case EArray(e,index): case EArray(e, index):
var arr = expr(e); var arr:Dynamic = expr(e);
var index = expr(index); var index:Dynamic = expr(index);
var v = arr[index]; if (isMap(arr)) {
if( prefix ) { var v = getMapValue(arr, index);
v += delta; if (prefix) {
arr[index] = v; v += delta;
} else setMapValue(arr, index, v);
arr[index] = v + delta; }
return v; else {
setMapValue(arr, index, v + delta);
}
return v;
}
else {
var v = arr[index];
if( prefix ) {
v += delta;
arr[index] = v;
} else
arr[index] = v + delta;
return v;
}
default: default:
return error(EInvalidOp((delta > 0)?"++":"--")); return error(EInvalidOp((delta > 0)?"++":"--"));
} }
@@ -248,7 +284,7 @@ class Interp {
inline function error(e : #if hscriptPos ErrorDef #else Error #end ) : Dynamic { inline function error(e : #if hscriptPos ErrorDef #else Error #end ) : Dynamic {
#if hscriptPos #if hscriptPos
throw new Error(e, curExpr.pmin, curExpr.pmax); throw new Error(e, curExpr.pmin, curExpr.pmax, curExpr.origin, curExpr.line);
#else #else
throw e; throw e;
#end #end
@@ -338,6 +374,9 @@ class Interp {
case EWhile(econd,e): case EWhile(econd,e):
whileLoop(econd,e); whileLoop(econd,e);
return null; return null;
case EDoWhile(econd,e):
doWhileLoop(econd,e);
return null;
case EFor(v,it,e): case EFor(v,it,e):
forLoop(v,it,e); forLoop(v,it,e);
return null; return null;
@@ -417,12 +456,56 @@ class Interp {
} }
return f; return f;
case EArrayDecl(arr): case EArrayDecl(arr):
var a = new Array(); if (arr.length > 0 && edef(arr[0]).match(EBinop("=>", _))) {
for( e in arr ) var isAllString:Bool = true;
a.push(expr(e)); var isAllInt:Bool = true;
return a; var isAllObject:Bool = true;
case EArray(e,index): var isAllEnum:Bool = true;
return expr(e)[expr(index)]; var keys:Array<Dynamic> = [];
var values:Array<Dynamic> = [];
for (e in arr) {
switch(edef(e)) {
case EBinop("=>", eKey, eValue): {
var key:Dynamic = expr(eKey);
var value:Dynamic = expr(eValue);
isAllString = isAllString && Std.is(key, String);
isAllInt = isAllInt && Std.is(key, Int);
isAllObject = isAllObject && Reflect.isObject(key);
isAllEnum = isAllEnum && Reflect.isEnumValue(key);
keys.push(key);
values.push(value);
}
default: throw("=> expected");
}
}
var map:Dynamic = {
if (isAllInt) new haxe.ds.IntMap<Dynamic>();
else if (isAllString) new haxe.ds.StringMap<Dynamic>();
else if (isAllEnum) new haxe.ds.EnumValueMap<Dynamic, Dynamic>();
else if (isAllObject) new haxe.ds.ObjectMap<Dynamic, Dynamic>();
else throw 'Inconsistent key types';
}
for (n in 0...keys.length) {
setMapValue(map, keys[n], values[n]);
}
return map;
}
else {
var a = new Array();
for ( e in arr ) {
a.push(expr(e));
}
return a;
}
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
return getMapValue(arr, index);
}
else {
return arr[index];
}
case ENew(cl,params): case ENew(cl,params):
var a = new Array(); var a = new Array();
for( e in params ) for( e in params )
@@ -477,10 +560,29 @@ class Interp {
if( !match ) if( !match )
val = def == null ? null : expr(def); val = def == null ? null : expr(def);
return val; return val;
case EMeta(_, _, e):
return expr(e);
} }
return null; return null;
} }
function doWhileLoop(econd,e) {
var old = declared.length;
do {
try {
expr(e);
} catch( err : Stop ) {
switch(err) {
case SContinue:
case SBreak: break;
case SReturn(_): throw err;
}
}
}
while( expr(econd) == true );
restore(old);
}
function whileLoop(econd,e) { function whileLoop(econd,e) {
var old = declared.length; var old = declared.length;
while( expr(econd) == true ) { while( expr(econd) == true ) {
@@ -498,8 +600,8 @@ class Interp {
} }
function makeIterator( v : Dynamic ) : Iterator<Dynamic> { function makeIterator( v : Dynamic ) : Iterator<Dynamic> {
#if (flash && !flash9) #if ((flash && !flash9) || php)
if( v.iterator != null ) v = v.iterator(); if ( v.iterator != null ) v = v.iterator();
#else #else
try v = v.iterator() catch( e : Dynamic ) {}; try v = v.iterator() catch( e : Dynamic ) {};
#end #end
@@ -526,18 +628,32 @@ class Interp {
restore(old); restore(old);
} }
inline function isMap(o:Dynamic):Bool {
return Std.is(o, haxe.Constraints.IMap);
}
inline function getMapValue(map:Dynamic, key:Dynamic):Dynamic {
return cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).get(key);
}
inline function setMapValue(map:Dynamic, key:Dynamic, value:Dynamic):Void {
cast(map, haxe.Constraints.IMap<Dynamic, Dynamic>).set(key, value);
}
function get( o : Dynamic, f : String ) : Dynamic { function get( o : Dynamic, f : String ) : Dynamic {
if( o == null ) error(EInvalidAccess(f)); if ( o == null ) error(EInvalidAccess(f));
#if php return {
// https://github.com/HaxeFoundation/haxe/issues/4915 #if php
return try { // https://github.com/HaxeFoundation/haxe/issues/4915
try {
Reflect.getProperty(o, f);
} catch (e:Dynamic) {
Reflect.field(o, f);
}
#else
Reflect.getProperty(o, f); Reflect.getProperty(o, f);
} catch (e:Dynamic) { #end
Reflect.field(o, f); }
}
#else
return Reflect.getProperty(o, f);
#end
} }
function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic { function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic {
+5
View File
@@ -180,6 +180,8 @@ class Macro {
EIf(convert(c), convert(e1), e2 == null ? null : convert(e2)); EIf(convert(c), convert(e1), e2 == null ? null : convert(e2));
case EWhile(c, e): case EWhile(c, e):
EWhile(convert(c), convert(e), true); EWhile(convert(c), convert(e), true);
case EDoWhile(c, e):
EWhile(convert(c), convert(e), false);
#if (haxe_211 || haxe3) #if (haxe_211 || haxe3)
case EFor(v, it, efor): case EFor(v, it, efor):
var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end; var p = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
@@ -229,6 +231,9 @@ class Macro {
ETernary(convert(cond), convert(e1), convert(e2)); ETernary(convert(cond), convert(e1), convert(e2));
case ESwitch(e, cases, edef): case ESwitch(e, cases, edef):
ESwitch(convert(e), [for( c in cases ) { values : [for( v in c.values ) convert(v)], expr : convert(c.expr) } ], edef == null ? null : convert(edef)); ESwitch(convert(e), [for( c in cases ) { values : [for( v in c.values ) convert(v)], expr : convert(c.expr) } ], edef == null ? null : convert(edef));
case EMeta(m, params, esub):
var mpos = #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EMeta({ name : m, params : params == null ? [] : [for( p in params ) convert(p)], pos : mpos }, convert(esub));
}, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end } }, pos : #if hscriptPos { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
} }
+104 -51
View File
@@ -41,6 +41,7 @@ enum Token {
TBkClose; TBkClose;
TQuestion; TQuestion;
TDoubleDot; TDoubleDot;
TMeta( s : String );
} }
class Parser { class Parser {
@@ -69,6 +70,11 @@ class Parser {
**/ **/
public var allowTypes : Bool; public var allowTypes : Bool;
/**
allow haxe metadata declarations
**/
public var allowMetadata : Bool;
// implementation // implementation
var input : haxe.io.Input; var input : haxe.io.Input;
var char : Int; var char : Int;
@@ -77,6 +83,7 @@ class Parser {
var uid : Int = 0; var uid : Int = 0;
#if hscriptPos #if hscriptPos
var origin : String;
var readPos : Int; var readPos : Int;
var tokenMin : Int; var tokenMin : Int;
var tokenMax : Int; var tokenMax : Int;
@@ -111,7 +118,7 @@ class Parser {
["..."], ["..."],
["&&"], ["&&"],
["||"], ["||"],
["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^="], ["=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","|=","&=","^=","=>"],
]; ];
#if haxe3 #if haxe3
opPriority = new Map(); opPriority = new Map();
@@ -133,7 +140,7 @@ class Parser {
public inline function error( err, pmin, pmax ) { public inline function error( err, pmin, pmax ) {
#if hscriptPos #if hscriptPos
throw new Error(err, pmin, pmax); throw new Error(err, pmin, pmax, origin, line);
#else #else
throw err; throw err;
#end #end
@@ -143,14 +150,15 @@ class Parser {
error(EInvalidChar(c), readPos, readPos); error(EInvalidChar(c), readPos, readPos);
} }
public function parseString( s : String ) { public function parseString( s : String, ?origin : String = "hscript" ) {
line = 1;
uid = 0; uid = 0;
return parse( new haxe.io.StringInput(s) ); return parse( new haxe.io.StringInput(s), origin );
} }
public function parse( s : haxe.io.Input ) { public function parse( s : haxe.io.Input, ?origin : String = "hscript" ) {
line = 1;
#if hscriptPos #if hscriptPos
this.origin = origin;
readPos = 0; readPos = 0;
tokenMin = oldTokenMin = 0; tokenMin = oldTokenMin = 0;
tokenMax = oldTokenMax = 0; tokenMax = oldTokenMax = 0;
@@ -198,6 +206,16 @@ class Parser {
if( t != tk ) unexpected(t); if( t != tk ) unexpected(t);
} }
function getIdent() {
var tk = token();
switch( tk ) {
case TId(id): return id;
default:
unexpected(tk);
return null;
}
}
inline function expr(e:Expr) { inline function expr(e:Expr) {
#if hscriptPos #if hscriptPos
return e.e; return e.e;
@@ -224,9 +242,10 @@ class Parser {
inline function mk(e,?pmin,?pmax) : Expr { inline function mk(e,?pmin,?pmax) : Expr {
#if hscriptPos #if hscriptPos
if( e == null ) return null;
if( pmin == null ) pmin = tokenMin; if( pmin == null ) pmin = tokenMin;
if( pmax == null ) pmax = tokenMax; if( pmax == null ) pmax = tokenMax;
return { e : e, pmin : pmin, pmax : pmax }; return { e : e, pmin : pmin, pmax : pmax, origin : origin, line : line };
#else #else
return e; return e;
#end #end
@@ -241,6 +260,7 @@ class Parser {
case EBinop(_,_,e): isBlock(e); case EBinop(_,_,e): isBlock(e);
case EUnop(_,prefix,e): !prefix && isBlock(e); case EUnop(_,prefix,e): !prefix && isBlock(e);
case EWhile(_,e): isBlock(e); case EWhile(_,e): isBlock(e);
case EDoWhile(_,e): isBlock(e);
case EFor(_,_,e): isBlock(e); case EFor(_,_,e): isBlock(e);
case EReturn(e): e != null && isBlock(e); case EReturn(e): e != null && isBlock(e);
case ETry(_, _, _, e): isBlock(e); case ETry(_, _, _, e): isBlock(e);
@@ -370,7 +390,7 @@ class Parser {
} }
if( a.length == 1 ) if( a.length == 1 )
switch( expr(a[0]) ) { switch( expr(a[0]) ) {
case EFor(_), EWhile(_): case EFor(_), EWhile(_), EDoWhile(_):
var tmp = "__a_" + (uid++); var tmp = "__a_" + (uid++);
var e = mk(EBlock([ var e = mk(EBlock([
mk(EVar(tmp, null, mk(EArrayDecl([]), p1)), p1), mk(EVar(tmp, null, mk(EArrayDecl([]), p1)), p1),
@@ -380,18 +400,47 @@ class Parser {
return parseExprNext(e); return parseExprNext(e);
default: default:
} }
return parseExprNext(mk(EArrayDecl(a),p1)); return parseExprNext(mk(EArrayDecl(a), p1));
case TMeta(id) if( allowMetadata ):
var args = parseMetaArgs();
return mk(EMeta(id, args, parseExpr()),p1);
default: default:
return unexpected(tk); return unexpected(tk);
} }
} }
function parseMetaArgs() {
var tk = token();
if( tk != TPOpen ) {
push(tk);
return null;
}
var args = [];
tk = token();
if( tk != TPClose ) {
push(tk);
while( true ) {
args.push(parseExpr());
switch( token() ) {
case TComma:
case TPClose:
break;
case tk:
unexpected(tk);
}
}
}
return args;
}
function mapCompr( tmp : String, e : Expr ) { function mapCompr( tmp : String, e : Expr ) {
var edef = switch( expr(e) ) { var edef = switch( expr(e) ) {
case EFor(v, it, e2): case EFor(v, it, e2):
EFor(v, it, mapCompr(tmp, e2)); EFor(v, it, mapCompr(tmp, e2));
case EWhile(cond, e2): case EWhile(cond, e2):
EWhile(cond, mapCompr(tmp, e2)); EWhile(cond, mapCompr(tmp, e2));
case EDoWhile(cond, e2):
EDoWhile(cond, mapCompr(tmp, e2));
case EIf(cond, e1, e2) if( e2 == null ): case EIf(cond, e1, e2) if( e2 == null ):
EIf(cond, mapCompr(tmp, e1), null); EIf(cond, mapCompr(tmp, e1), null);
case EBlock([e]): case EBlock([e]):
@@ -454,13 +503,8 @@ class Parser {
} }
mk(EIf(cond,e1,e2),p1,(e2 == null) ? tokenMax : pmax(e2)); mk(EIf(cond,e1,e2),p1,(e2 == null) ? tokenMax : pmax(e2));
case "var": case "var":
var ident = getIdent();
var tk = token(); var tk = token();
var ident = null;
switch(tk) {
case TId(id): ident = id;
default: unexpected(tk);
}
tk = token();
var t = null; var t = null;
if( tk == TDoubleDot && allowTypes ) { if( tk == TDoubleDot && allowTypes ) {
t = parseType(); t = parseType();
@@ -476,15 +520,20 @@ class Parser {
var econd = parseExpr(); var econd = parseExpr();
var e = parseExpr(); var e = parseExpr();
mk(EWhile(econd,e),p1,pmax(e)); mk(EWhile(econd,e),p1,pmax(e));
case "do":
var e = parseExpr();
var tk = token();
switch(tk)
{
case TId("while"): // Valid
default: unexpected(tk);
}
var econd = parseExpr();
mk(EDoWhile(econd,e),p1,pmax(econd));
case "for": case "for":
ensure(TPOpen); ensure(TPOpen);
var vname = getIdent();
var tk = token(); var tk = token();
var vname = null;
switch( tk ) {
case TId(id): vname = id;
default: unexpected(tk);
}
tk = token();
if( !Type.enumEq(tk,TId("in")) ) unexpected(tk); if( !Type.enumEq(tk,TId("in")) ) unexpected(tk);
var eiter = parseExpr(); var eiter = parseExpr();
ensure(TPClose); ensure(TPClose);
@@ -552,21 +601,13 @@ class Parser {
mk(EReturn(e),p1,if( e == null ) tokenMax else pmax(e)); mk(EReturn(e),p1,if( e == null ) tokenMax else pmax(e));
case "new": case "new":
var a = new Array(); var a = new Array();
var tk = token(); a.push(getIdent());
switch( tk ) {
case TId(id): a.push(id);
default: unexpected(tk);
}
var next = true; var next = true;
while( next ) { while( next ) {
tk = token(); var tk = token();
switch( tk ) { switch( tk ) {
case TDot: case TDot:
tk = token(); a.push(getIdent());
switch(tk) {
case TId(id): a.push(id);
default: unexpected(tk);
}
case TPOpen: case TPOpen:
next = false; next = false;
default: default:
@@ -583,11 +624,7 @@ class Parser {
var tk = token(); var tk = token();
if( !Type.enumEq(tk, TId("catch")) ) unexpected(tk); if( !Type.enumEq(tk, TId("catch")) ) unexpected(tk);
ensure(TPOpen); ensure(TPOpen);
tk = token(); var vname = getIdent();
var vname = switch( tk ) {
case TId(id): id;
default: unexpected(tk);
}
ensure(TDoubleDot); ensure(TDoubleDot);
var t = null; var t = null;
if( allowTypes ) if( allowTypes )
@@ -684,12 +721,7 @@ class Parser {
} }
return makeBinop(op,e1,parseExpr()); return makeBinop(op,e1,parseExpr());
case TDot: case TDot:
tk = token(); var field = getIdent();
var field = null;
switch(tk) {
case TId(id): field = id;
default: unexpected(tk);
}
return parseExprNext(mk(EField(e1,field),pmin(e1))); return parseExprNext(mk(EField(e1,field),pmin(e1)));
case TPOpen: case TPOpen:
return parseExprNext(mk(ECall(e1,parseExprList(TPClose)),pmin(e1))); return parseExprNext(mk(ECall(e1,parseExprList(TPClose)),pmin(e1)));
@@ -717,13 +749,7 @@ class Parser {
t = token(); t = token();
if( t != TDot ) if( t != TDot )
break; break;
t = token(); path.push(getIdent());
switch( t ) {
case TId(v):
path.push(v);
default:
unexpected(t);
}
} }
var params = null; var params = null;
switch( t ) { switch( t ) {
@@ -761,19 +787,29 @@ class Parser {
return parseTypeNext(CTParent(t)); return parseTypeNext(CTParent(t));
case TBrOpen: case TBrOpen:
var fields = []; var fields = [];
var meta = null;
while( true ) { while( true ) {
t = token(); t = token();
switch( t ) { switch( t ) {
case TBrClose: break; case TBrClose: break;
case TId("var"):
var name = getIdent();
ensure(TDoubleDot);
fields.push( { name : name, t : parseType(), meta : meta } );
meta = null;
ensure(TSemicolon);
case TId(name): case TId(name):
ensure(TDoubleDot); ensure(TDoubleDot);
fields.push( { name : name, t : parseType() } ); fields.push( { name : name, t : parseType(), meta : meta } );
t = token(); t = token();
switch( t ) { switch( t ) {
case TComma: case TComma:
case TBrClose: break; case TBrClose: break;
default: unexpected(t); default: unexpected(t);
} }
case TMeta(name):
if( meta == null ) meta = [];
meta.push({ name : name, params : parseMetaArgs() });
default: default:
unexpected(t); unexpected(t);
} }
@@ -1062,8 +1098,24 @@ class Parser {
char = readChar(); char = readChar();
if( char == '='.code ) if( char == '='.code )
return TOp("=="); return TOp("==");
else if ( char == '>'.code )
return TOp("=>");
this.char = char; this.char = char;
return TOp("="); return TOp("=");
case '@'.code:
char = readChar();
if( idents[char] || char == ':'.code ) {
var id = String.fromCharCode(char);
while( true ) {
char = readChar();
if( !idents[char] ) {
this.char = char;
return TMeta(id);
}
id += String.fromCharCode(char);
}
}
invalidChar(char);
default: default:
if( ops[char] ) { if( ops[char] ) {
var op = String.fromCharCode(char); var op = String.fromCharCode(char);
@@ -1168,6 +1220,7 @@ class Parser {
case TBkClose: "]"; case TBkClose: "]";
case TQuestion: "?"; case TQuestion: "?";
case TDoubleDot: ":"; case TDoubleDot: ":";
case TMeta(id): "@" + id;
} }
} }
+6
View File
@@ -148,6 +148,12 @@ class Printer {
expr(cond); expr(cond);
add(" ) "); add(" ) ");
expr(e); expr(e);
case EDoWhile(cond,e):
add("do ");
expr(e);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e): case EFor(v, it, e):
add("for( "+v+" in "); add("for( "+v+" in ");
expr(it); expr(it);
+2
View File
@@ -15,6 +15,7 @@ class Tools {
case ECall(e, args): f(e); for( a in args ) f(a); case ECall(e, args): f(e); for( a in args ) f(a);
case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2); case EIf(c, e1, e2): f(c); f(e1); if( e2 != null ) f(e2);
case EWhile(c, e): f(c); f(e); case EWhile(c, e): f(c); f(e);
case EDoWhile(c, e): f(c); f(e);
case EFor(_, it, e): f(it); f(e); case EFor(_, it, e): f(it); f(e);
case EBreak,EContinue: case EBreak,EContinue:
case EFunction(_, e, _, _): f(e); case EFunction(_, e, _, _): f(e);
@@ -48,6 +49,7 @@ class Tools {
case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]); case ECall(e, args): ECall(f(e),[for( a in args ) f(a)]);
case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null); case EIf(c, e1, e2): EIf(f(c),f(e1),if( e2 != null ) f(e2) else null);
case EWhile(c, e): EWhile(f(c),f(e)); case EWhile(c, e): EWhile(f(c),f(e));
case EDoWhile(c, e): EDoWhile(f(c),f(e));
case EFor(v, it, e): EFor(v, f(it), f(e)); case EFor(v, it, e): EFor(v, f(it), f(e));
case EBreak, EContinue: e; case EBreak, EContinue: e;
case EFunction(args, e, name, t): EFunction(args, f(e), name, t); case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
+1
View File
@@ -15,4 +15,5 @@ haxe -xml release/haxedoc.xml hscript.Interp hscript.Parser hscript.Bytes hscrip
7z a -tzip release.zip release 7z a -tzip release.zip release
rm -rf release rm -rf release
haxelib submit release.zip haxelib submit release.zip
echo Remember to "git tag vX.Y.Z && git push --tags"
pause pause