Repe [T3DU] Update - 31f26d171bba0355ce2a77031e3aad4c64dbc7e9

This commit is contained in:
2026-09-04 10:46:13 -07:00
parent c915312901
commit 0460b9d587
482 changed files with 27797 additions and 3226 deletions

43
lib/hscript/.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: CI
on:
push:
# This should disable running the workflow on tags
branches:
- "**"
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: krdlab/setup-haxe@v1
with:
haxe-version: 4.3.4
- name: Install haxelib dependencies
run: |
haxelib install hx3compat
haxelib dev hscript .
- name: Test interp
run: |
haxe bin/build-interp.hxml
haxe bin/build-interp.hxml -D hscriptPos
- name: Test neko
run: |
haxe bin/build-neko.hxml && neko bin/Test.n
haxe bin/build-neko.hxml -D hscriptPos && neko bin/Test.n
- name: Test js
run: |
haxe bin/build-js.hxml && node bin/Test.js
haxe bin/build-js.hxml -D hscriptPos && node bin/Test.js
- name: Build hashlink from source
run: |
git clone https://github.com/HaxeFoundation/hashlink.git
cd hashlink
make hl
cd ..
- name: Test hl
run: |
haxe bin/build-hl.hxml && ./hashlink/hl bin/Test.hl
haxe bin/build-hl.hxml -D hscriptPos && ./hashlink/hl bin/Test.hl

2
lib/hscript/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/hscript.swf
/release.zip

16
lib/hscript/.vscode/launch.json vendored Normal file
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
lib/hscript/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "hxml",
"file": "hscript.hxml",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

22
lib/hscript/LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License
Copyright (C)2008-2017 Haxe Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

100
lib/hscript/README.md Normal file
View File

@ -0,0 +1,100 @@
hscript
=======
Parse and evalutate Haxe expressions.
In some projects it's sometimes useful to be able to interpret some code dynamically, without recompilation.
Haxe script is a complete subset of the Haxe language.
It is dynamically typed but allows all Haxe expressions apart from type (class,enum,typedef) declarations.
Usage
-----
```haxe
var expr = "var x = 4; 1 + 2 * x";
var parser = new hscript.Parser();
var ast = parser.parseString(expr);
var interp = new hscript.Interp();
trace(interp.execute(ast));
```
In case of a parsing error an `hscript.Expr.Error` is thrown. You can use `parser.line` to check the line number.
You can set some globaly accessible identifiers by using `interp.variables.set("name",value)`
Example
-------
Here's a small example of Haxe Script usage :
```haxe
var script = "
var sum = 0;
for( a in angles )
sum += Math.cos(a);
sum;
";
var parser = new hscript.Parser();
var program = parser.parseString(script);
var interp = new hscript.Interp();
interp.variables.set("Math",Math); // share the Math class
interp.variables.set("angles",[0,1,2,3]); // set the angles list
trace( interp.execute(program) );
```
This will calculate the sum of the cosines of the angles given as input.
Haxe Script has not been really optimized, and it's not meant to be very fast. But it's entirely crossplatform since it's pure Haxe code (it doesn't use any platform-specific API).
Advanced Usage
--------------
When compiled with `-D hscriptPos` you will get fine error reporting at parsing time.
You can subclass `hscript.Interp` to override behaviors for `get`, `set`, `call`, `fcall` and `cnew`.
You can add more binary and unary operations to the parser by setting `opPriority`, `opRightAssoc` and `unops` content.
You can use `parser.allowJSON` to allow JSON data.
You can use `parser.allowTypes` to parse types for local vars, exceptions, function args and return types. Types are ignored by the interpreter.
You can use `parser.allowMetadata` to parse metadata before expressions on in anonymous types. Metadata are ignored by the interpreter.
You can use `new hscript.Macro(pos).convert(ast)` to convert an hscript AST to a Haxe macros one.
You can use `hscript.Checker` in order to type check and even get completion, using `haxe -xml` output for type information.
Limitations
-----------
Compared to Haxe, limitations are :
- `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`.
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)

263
lib/hscript/TestHScript.hx Normal file
View File

@ -0,0 +1,263 @@
import haxe.ds.EnumValueMap;
import haxe.ds.Option;
import hscript.Macro;
import hscript.Tools;
import hscript.Async;
import hscript.Printer;
import hscript.Checker;
import haxe.unit.*;
class TestHScript extends TestCase {
var optimize : Bool;
function new(optimize=false) {
this.optimize = optimize;
super();
}
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);
var bytes = hscript.Bytes.encode(program);
program = hscript.Bytes.decode(bytes);
var interp = new hscript.Interp();
#if js
if( optimize ) interp = new hscript.JsInterp();
#end
if( vars != null )
for( v in Reflect.fields(vars) )
interp.variables.set(v,Reflect.field(vars,v));
var ret : Dynamic = interp.execute(program);
assertEquals(v, ret, pos);
}
function test():Void {
assertScript("0",0);
assertScript("0xFF", 255);
#if !(php || python)
#if haxe3
assertScript("0xBFFFFFFF", 0xBFFFFFFF);
assertScript("0x7FFFFFFF", 0x7FFFFFFF);
#elseif !neko
assertScript("n(0xBFFFFFFF)", 0xBFFFFFFF, { n : haxe.Int32.toNativeInt });
assertScript("n(0x7FFFFFFF)", 0x7FFFFFFF, { n : haxe.Int32.toNativeInt } );
#end
#end
assertScript("-123",-123);
assertScript("- 123",-123);
assertScript("1.546",1.546);
assertScript(".545",.545);
assertScript("1e5",100000);
assertScript("1.2e2",120);
assertScript("100e-2",1);
assertScript("1.2e-1",0.12);
assertScript("'bla'","bla");
assertScript("null",null);
assertScript("true",true);
assertScript("false",false);
assertScript("1 == 2",false);
assertScript("1.3 == 1.3",true);
assertScript("5 > 3",true);
assertScript("0 < 0",false);
assertScript("-1 <= -1",true);
assertScript("1 + 2",3);
assertScript("~545",-546);
assertScript("'abc' + 55","abc55");
assertScript("'abc' + 'de'","abcde");
assertScript("-1 + 2",1);
assertScript("1 / 5",0.2);
assertScript("3 * 2 + 5",11);
assertScript("3 * (2 + 5)",21);
assertScript("3 * 2 // + 5 \n + 6",12);
assertScript("3 /* 2\n */ + 5",8);
assertScript("[55,66,77][1]",66);
assertScript("[11,22,33,][2]", 33);
assertScript("var a = [55]; a[0] *= 2; a[0]",110);
assertScript("x",55,{ x : 55 });
assertScript("var y = 33; y",33);
assertScript("{ 1; 2; 3; }",3);
assertScript("{ var x = 0; } x",55,{ x : 55 });
assertScript("o.val",55,{ o : { val : 55 } });
assertScript("o.val",null,{ o : {} });
assertScript("var a = 1; a++",1);
assertScript("var a = 1; a++; a",2);
assertScript("var a = 1; ++a",2);
assertScript("var a = 1; a *= 3",3);
assertScript("a = b = 3; a + b",6);
assertScript("add(1,2)",3,{ add : function(x,y) return x + y });
assertScript("a.push(5); a.pop() + a.pop()",8,{ a : [3] });
assertScript("if( true ) 1 else 2",1);
assertScript("if( false ) 1 else 2",2);
assertScript("var t = 0; for( x in [1,2,3] ) t += x; t",6);
assertScript("var a = new Array(); for( x in 0...5 ) a[x] = x; a.join('-')","0-1-2-3-4");
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 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 new IntIterator(1,10) ) t +=x; t", 45);
assertScript("var x = 1; try { var x = 66; throw 789; } catch( e : Dynamic ) e + x",790);
assertScript("var x = 1; var f = function(x) throw x; try f(55) catch( e : Dynamic ) e + x",56);
assertScript("var i=2; if( true ) --i; i",1);
assertScript("var i=0; if( i++ > 0 ) i=3; i",1);
assertScript("var a = 5/2; a",2.5);
assertScript("{ x = 3; x; }", 3);
assertScript("{ x : 3, y : {} }.x", 3);
assertScript("function bug() return { \n }\nbug().x", null);
assertScript("1 + 2 == 3", true);
assertScript("-2 == 3 - 5", true);
assertScript("var x=-3; x", -3);
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 = () -> 55; f()", 55);
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);
assertScript("var a = 10; var b = 5; a - -b", 15);
assertScript("var a = 10; var b = 5; a - b / 2", 7.5);
assertScript("var a; a", null);
assertScript("var a = 1, b = 5; a + b;", 6);
assertScript("var a, b = 5; if (a == null) a = 2; a + b;", 7);
assertScript("var a:Int; a", null, null, true);
assertScript("var a:Int = 1, b:Int = 5; a + b;", 6, null, true);
assertScript("var a:Int, b:Int = 5; if (a == null) a = 2; a + b;", 7, null, true);
assertScript("false && xxx", false);
assertScript("true || xxx", true);
assertScript("[for( x in arr ) switch( x ) { case 1: 55; case 3: 66; default: 0; }].join(':')",'55:0:66',{ arr : [1,2,3] });
assertScript("switch( x ) { case 1: 55; case 3: 66; default: 0; }",66 ,{ x : 3 });
assertScript("var a = 1; switch( b ) { default: a = 2; }; a", 2, { b : 2 });
assertScript("var a = 1; switch( b ) { case 2: a = 100; default: a = 2; }; a", 100, { b : 2 });
assertScript("var a = 3; switch( b ) { case 2: if (a == 1) { a = 100; } else { a = 99; }; default: a = 2; }; a", 99, { b : 2 });
}
function testNullFieldAccess():Void {
var pt = {x : 10};
var vars = {
ptnull : null,
pt: pt,
pt2null : {pt : null},
pt2: {pt : pt}
}
assertScript("ptnull?.x", null, vars);
assertScript("pt?.x", 10, vars);
assertScript("pt2null?.pt", null, vars);
assertScript("pt2null?.pt?.x", null, vars);
assertScript("pt2?.pt", pt, vars);
assertScript("pt2?.pt?.x", 10, vars);
}
function testIsOperator():Void {
var vars = {
String: String,
Bool: Bool,
Int: Int,
Float: Float,
Dynamic: Dynamic
}
assertScript("10 is Int", true, vars);
assertScript("10.0 is Int", true, vars);
assertScript("10.1 is Int", false, vars);
assertScript("10 is Float", true, vars);
assertScript("10.0 is Float", true, vars);
assertScript("10.1 is Float", true, vars);
assertScript("10 is String", false, vars);
assertScript('"hscript" is String', true, vars);
assertScript('"" is String', true, vars);
assertScript('true is Bool', true, vars);
assertScript('false is Bool', true, vars);
assertScript('0 is Bool', false, vars);
assertScript('1 is Bool', false, vars);
assertScript('1 is Bool', false, vars);
assertScript("10 is Dynamic", true, vars);
assertScript("10.1 is Dynamic", true, vars);
assertScript('"hscript" is Dynamic', true, vars);
assertScript('null is Int', false, vars);
assertScript('null is Float', false, vars);
assertScript('null is String', false, vars);
assertScript('null is Bool', false, vars);
assertScript('null is Dynamic', false, vars);
}
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() {
#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 TestHScript());
runner.add(new TestHScript(true));
var succeed = runner.run();
#if sys
Sys.exit(succeed ? 0 : 1);
#elseif flash
flash.system.System.exit(succeed ? 0 : 1);
#else
if (!succeed)
throw "failed";
#end
}
}

2
lib/hscript/bin/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1 @@
--macro keep('IntIterator')

9
lib/hscript/haxelib.json Normal file
View File

@ -0,0 +1,9 @@
{
"name": "hscript",
"url": "https://github.com/HaxeFoundation/hscript",
"license": "MIT",
"description": "Haxe Script is a scripting engine for a subset of the Haxe language",
"version": "2.6.0",
"releasenote": "More Haxe 4/5 features",
"contributors": ["ncannasse","HaxeFoundation"]
}

2
lib/hscript/hscript.hxml Normal file
View File

@ -0,0 +1,2 @@
bin/build-each.hxml
-hl bin/Test.hl

View File

@ -0,0 +1,536 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
enum VarMode {
Defined;
ForceSync;
}
class Async {
var definedVars : Array<{ n : String, prev : Null<VarMode> }>;
var vars : Map<String,VarMode>;
var currentFun : String;
var currentLoop : Expr;
var currentBreak : Expr -> Expr;
var uid = 0;
public var asyncIdents : Map<String,Bool>;
static var nullExpr : Expr = #if hscriptPos { e : null, pmin : 0, pmax : 0, origin : "<null>", line : 0 } #else null #end;
static var nullId = mk(EIdent("null"), nullExpr);
inline static function expr( e : Expr ) {
return #if hscriptPos e.e #else e #end;
}
inline static function mk( e, inf : Expr ) : Expr {
return #if hscriptPos { e : e, pmin : inf.pmin, pmax : inf.pmax, origin : inf.origin, line : inf.line } #else e #end;
}
/**
Convert a script into asynchronous one.
- calls such as foo(a,b,c) are translated to a_foo(function(r) ...rest, a,b,c) where r is the result value
- object access such obj.bar(a,b,c) are translated to obj.a_bar(function(r) ...rest, a, b, c)
- @async expr will execute the expression but continue without waiting for it to finish
- @split [ e1, e2, e3 ] is transformed to split(function(_) ...rest, [e1, e2, e3]) which
should execute asynchronously all expressions - until they return - before continuing the execution
- for(i in v) block; loops are translated to the following:
var _i = makeIterator(v);
function _loop() {
if( !_i.hasNext() ) return;
var v = _i.next();
block(function(_) _loop());
}
_loop()
- while loops are translated similar to for loops
- break and continue are correctly handled
- you can use @sync <expr> to disable async transformation in some code parts (for performance reason)
- a few expressions are still not supported (complex calls, try/catch, and a few others)
In these examples ...rest represents the continuation of execution of the script after the expression
**/
public static function toAsync( e : Expr, topLevelSync = false ) {
var a = new Async();
return a.build(e, topLevelSync);
}
public dynamic function getTopLevelEnd() {
return ignore();
}
public function build( e : Expr, topLevelSync = false ) {
if( topLevelSync ) {
return buildSync(e,null);
} else {
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,_):
if( name != null )
return toCps(e, null, null);
return e;
case EBlock(el):
var v = saveVars();
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 ):
return block([retNull(currentLoop, e), mk(EReturn(),e)],e);
case EFor(_), EWhile(_):
var oldLoop = currentLoop, oldBreak = currentBreak;
currentLoop = null;
currentBreak = null;
e = Tools.map(e, buildSync.bind(_, exit));
currentLoop = oldLoop;
currentBreak = oldBreak;
return e;
case EReturn(eret) if( exit != null ):
return block([eret == null ? retNull(exit, e) : call(exit,[eret], e), mk(EReturn(),e)], e);
default:
return Tools.map(e, buildSync.bind(_, exit));
}
}
public function new() {
vars = new Map();
definedVars = [];
}
function ignore(?e) : Expr {
var inf = e == null ? nullExpr : e;
return fun("_", block(e == null ? [] : [e],inf));
}
inline function ident(str, e) {
return mk(EIdent(str), e);
}
inline function fun(arg:String, e, ?name) {
return mk(EFunction([{ name : arg, t : null }], e, name), e);
}
inline function funs(arg:Array<String>, e, ?name) {
return mk(EFunction([for( a in arg ) { name : a, t : null }], e, name), e);
}
inline function block(arr:Array<Expr>, e) {
if( arr.length == 1 && expr(arr[0]).match(EBlock(_)) )
return arr[0];
return mk(EBlock(arr), e);
}
inline function field(e, f, inf) {
return mk(EField(e, f), inf);
}
inline function binop(op, e1, e2, inf) {
return mk(EBinop(op, e1, e2), inf);
}
inline function call(e, args, inf) {
return mk(ECall(e, args), inf);
}
function retNull(e:Expr,?pos) : Expr {
switch( expr(e) ) {
case EFunction([{name:"_"}], e, _, _): return e;
default:
}
return call(e, [nullId], pos == null ? e : pos);
}
function makeCall( ecall, args : Array<Expr>, rest : Expr, exit, sync = false ) {
var names = [for( i in 0...args.length ) "_a"+uid++];
var rargs = [for( i in 0...args.length ) ident(names[i],ecall)];
if( !sync )
rargs.unshift(rest);
var rest = mk(sync ? ECall(rest,[call(ecall, rargs,ecall)]) : ECall(ecall, rargs), ecall);
var i = args.length - 1;
while( i >= 0 ) {
rest = toCps(args[i], fun(names[i], rest), exit);
i--;
}
return rest;
}
var syncFlag : Bool;
function isSync( e : Expr ) {
syncFlag = true;
checkSync(e);
return syncFlag;
}
inline function isAsyncIdent( id : String ) {
return asyncIdents == null || asyncIdents.exists(id);
}
function checkSync( e : Expr ) {
if( !syncFlag )
return;
switch( expr(e) ) {
case ECall(expr(_) => EIdent(i),_) if( isAsyncIdent(i) || vars.get(i) == Defined ):
syncFlag = false;
case ECall(expr(_) => EField(_,i),_) if( isAsyncIdent(i) ):
syncFlag = false;
case EFunction(_,_,name,_) if( name != null ):
syncFlag = false;
case EMeta("sync" | "async", _, _):
// isolated from the sync part
default:
Tools.iter(e, checkSync);
}
}
function saveVars() {
return definedVars.length;
}
function restoreVars(k) {
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 {
if( isSync(e) )
return call(rest, [buildSync(e, exit)],e);
switch( expr(e) ) {
case EBlock(el):
var el = el.copy();
var vold = saveVars();
lookupFunctions(el);
while( el.length > 0 ) {
var e = toCps(el.pop(), rest, exit);
rest = ignore(e);
}
restoreVars(vold);
return retNull(rest);
case EFunction(args, body, name, t):
var vold = saveVars();
if( name != null )
defineVar(name, Defined);
for( a in args )
defineVar(a.name, Defined);
args.unshift( { name : "_onEnd", t : null } );
var frest = ident("_onEnd",e);
var oldFun = currentFun;
currentFun = name;
var body = toCps(body, frest, frest);
var f = mk(EFunction(args, body, name, t),e);
restoreVars(vold);
return rest == null ? f : call(rest, [f],e);
case EParent(e):
return mk(EParent(toCps(e, rest, exit)),e);
case EMeta("sync", _, e):
return call(rest,[buildSync(e,exit)],e);
case EMeta("async", _, e):
var nothing = ignore();
return block([toCps(e,nothing,nothing),retNull(rest)],e);
case EMeta("split", _, e):
var args = switch( expr(e) ) { case EArrayDecl(el): el; default: throw "@split expression should be an array"; };
var args = [for( a in args ) fun("_rest", toCps(block([a],a), ident("_rest",a), exit))];
return call(ident("split",e), [rest, mk(EArrayDecl(args),e)],e);
case ECall(expr(_) => EIdent(i), args):
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):
var id = ++uid;
var it = ident("_i" + id,e);
var oldLoop = currentLoop, oldBreak = currentBreak;
var loop = ident("_loop" + id,e);
currentLoop = loop;
currentBreak = function(inf) return block([retNull(rest, inf), mk(EReturn(),inf)], inf);
var efor = block([
mk(EVar("_i" + id, call(ident("makeIterator",eit),[eit],eit)),eit),
fun("_", block([
mk(EIf(mk(EUnop("!", true, call( field(it, "hasNext", it), [], it)),it), currentBreak(it)),it),
mk(EVar(v, call(field(it, "next",it), [], it)), it),
toCps(eloop, loop, exit),
], it),"_loop" + id),
retNull(loop, e),
], e);
currentLoop = oldLoop;
currentBreak = oldBreak;
return efor;
case EUnop(op = "!", prefix, eop):
return toCps(eop, fun("_r",call(rest, [mk(EUnop(op, prefix, ident("_r",e)),e)], e)), exit);
case EBinop(op, e1, e2):
switch( op ) {
case "=", "+=", "-=", "/=", "*=", "%=", "&=", "|=", "^=":
switch( expr(e1) ) {
case EIdent(_):
var id = "_r" + uid++;
return toCps(e2, fun(id, call(rest, [binop(op, e1, ident(id,e1),e1)], e1)), exit);
case EField(ef1, f):
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(ef1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, field(ident(id1, e1), f, ef1), ident(id2, e2), e)], e)), exit)), exit);
case EArray(earr, eindex):
var idArr = "_r" + uid++;
var idIndex = "_r" + uid++;
var idVal = "_r" + uid++;
return toCps(earr,fun(idArr, toCps(eindex, fun(idIndex, toCps(e2,
fun(idVal, call(rest, [binop(op, mk(EArray(ident(idArr,earr), ident(idIndex,eindex)),e1), ident(idVal,e1), e)], e))
, exit)), exit)),exit);
default:
throw "assert " + e1;
}
case "||":
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(e1, fun(id1, mk(EIf(binop("==", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("true",e1)],e1),toCps(e2, rest, exit)),e)), exit);
case "&&":
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(e1, fun(id1, mk(EIf(binop("!=", ident(id1,e1), ident("true",e1), e1),call(rest,[ident("false",e1)],e1),toCps(e2, rest, exit)),e)), exit);
default:
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(e1, fun(id1, toCps(e2, fun(id2, call(rest, [binop(op, ident(id1,e1), ident(id2,e2), e)], e)), exit)), exit);
}
case EIf(cond, e1, e2), ETernary(cond, e1, e2):
return toCps(cond, fun("_c", mk(EIf(ident("_c",cond), toCps(e1, rest, exit), e2 == null ? retNull(rest) : toCps(e2, rest, exit)),e)), exit);
case EWhile(cond, ewh):
var id = ++uid;
var loop = ident("_loop" + id, cond);
var oldLoop = currentLoop, oldBreak = currentBreak;
currentLoop = loop;
currentBreak = function(e) return block([retNull(rest,e), mk(EReturn(),e)],e);
var ewhile = block([
fun("_r",
toCps(cond, fun("_c", mk(EIf(ident("_c", cond), toCps(ewh, loop, exit), retNull(rest,cond)),cond)), exit)
, "_loop"+id),
retNull(loop, cond),
],e);
currentLoop = oldLoop;
currentBreak = oldBreak;
return ewhile;
case EReturn(eret):
return eret == null ? retNull(exit, e) : toCps(eret, exit, exit);
case EObject(fields):
var id = "_o" + uid++;
var rest = call(rest, [ident(id,e)], e);
fields.reverse();
for( f in fields )
rest = toCps(f.e, fun("_r", block([
binop("=", mk(EField(ident(id,f.e), f.name),f.e), ident("_r",f.e), f.e),
rest,
],f.e)),exit);
return block([
mk(EVar(id, mk(EObject([]),e)),e),
rest,
],e);
case EArrayDecl(el):
var id = "_a" + uid++;
var rest = call(rest, [ident(id,e)], e);
var i = el.length - 1;
while( i >= 0 ) {
var e = el[i];
rest = toCps(e, fun("_r", block([
binop("=", mk(EArray(ident(id,e), mk(EConst(CInt(i)),e)),e), ident("_r",e), e),
rest,
],e)), exit);
i--;
}
return block([
mk(EVar(id, mk(EArrayDecl([]),e)),e),
rest,
],e);
case EArray(earr, eindex):
var id1 = "_r" + uid++;
var id2 = "_r" + uid++;
return toCps(earr, fun(id1, toCps(eindex, fun(id2, call(rest, [mk(EArray(ident(id1,e), ident(id2,e)),e)], e)), exit)), exit);
case EVar(v, t, ev):
if( ev == null )
return block([e, retNull(rest, e)], e);
return block([
mk(EVar(v, t),e),
toCps(ev, fun("_r", block([binop("=", ident(v,e), ident("_r",e), e), retNull(rest,e)], e)), exit),
],e);
case EConst(_), EIdent(_), EUnop(_), EField(_):
return call(rest, [e], e);
case ENew(cl, args):
var names = [for( i in 0...args.length ) "_a"+uid++];
var rargs = [for( i in 0...args.length ) ident(names[i], args[i])];
var rest = call(rest,[mk(ENew(cl, rargs),e)],e);
var i = args.length - 1;
while( i >= 0 ) {
rest = toCps(args[i], fun(names[i], rest), exit);
i--;
}
return rest;
case EBreak:
if( currentBreak == null ) throw "Break outside loop";
return currentBreak(e);
case EContinue:
if( currentLoop == null ) throw "Continue outside loop";
return block([retNull(currentLoop, e), mk(EReturn(),e)], e);
case ESwitch(v, cases, def):
var cases = [for( c in cases ) { values : c.values, expr : toCps(c.expr, rest, exit) } ];
return toCps(v, mk(EFunction([ { name : "_c", t : null } ], mk(ESwitch(ident("_c",v), cases, def == null ? retNull(rest) : toCps(def, rest, exit)),e)),e), exit );
case EThrow(v):
return toCps(v, mk(EFunction([ { name : "_v", t : null } ], mk(EThrow(v),v)), v), exit);
case 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);
}
}
}
class AsyncInterp extends Interp {
public function setContext( api : Dynamic ) {
var funs = new Array();
for( v in variables.keys() )
if( Reflect.isFunction(variables.get(v)) )
funs.push({ v : v, obj : null });
variables.set("split", split);
variables.set("makeIterator", makeIterator);
var c = Type.getClass(api);
for( f in (c == null ? Reflect.fields(api) : Type.getInstanceFields(c)) ) {
var fv = Reflect.field(api, f);
if( !Reflect.isFunction(fv) ) continue;
if( f.charCodeAt(0) == "_".code ) f = f.substr(1);
variables.set(f, fv);
// create the async wrapper if doesn't exists
if( f.substr(0, 2) != "a_" )
funs.push({ v : f, obj : api });
}
for( v in funs ) {
if( variables.exists("a_" + v.v) ) continue;
var fv : Dynamic = variables.get(v.v);
var obj = v.obj;
variables.set("a_" + v.v, Reflect.makeVarArgs(function(args:Array<Dynamic>) {
var onEnd = args.shift();
onEnd(Reflect.callMethod(obj, fv, args));
}));
}
}
public function hasMethod( name : String ) {
var v = variables.get(name);
return v != null && Reflect.isFunction(v);
}
public function callValue( value : Dynamic, args : Array<Dynamic>, ?onResult : Dynamic -> Void, ?vthis : {} ) {
var oldThis = variables.get("this");
if( vthis != null )
variables.set("this", vthis);
if( onResult == null )
onResult = function(_) {};
args.unshift(onResult);
Reflect.callMethod(null, value, args);
variables.set("this", oldThis);
}
public function callAsync( id : String, args, ?onResult, ?vthis : {} ) {
var v = variables.get(id);
if( v == null )
throw "Missing function " + id + "()";
callValue(v, args, onResult, vthis);
}
function split( rest : Dynamic -> Void, args : Array<Dynamic> ) {
if( args.length == 0 )
rest(null);
else {
var count = args.length;
function next(_) {
if( --count == 0 ) rest(null);
}
for( a in args )
a(next);
}
}
override function fcall( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
var m = Reflect.field(o, f);
if( m == null ) {
if( f.substr(0, 2) == "a_" ) {
m = Reflect.field(o, f.substr(2));
// fallback on sync version
if( m != null ) {
var onEnd = args.shift();
onEnd(call(o, m, args));
return null;
}
// fallback on generic script
m = Reflect.field(o, "scriptCall");
if( m != null ) {
call(o, m, [args.shift(), f.substr(2), args]);
return null;
}
} else {
// fallback on generic script
m = Reflect.field(o, "scriptCall");
if( m != null ) {
var result : Dynamic = null;
call(o, m, [function(r) result = r, f, args]);
return result;
}
}
error(ECustom(o + " has no method " + f));
}
return call(o, m, args);
}
}

View File

@ -0,0 +1,518 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
class Bytes {
var bin : haxe.io.Bytes;
var bout : haxe.io.BytesBuffer;
var pin : Int;
var hstrings : Map<String,Int>;
var strings : Array<String>;
var nstrings : Int;
public var storeTypes : Bool = false;
function new( ?bin ) {
this.bin = bin;
pin = 0;
bout = new haxe.io.BytesBuffer();
hstrings = new Map();
strings = [null];
nstrings = 1;
}
function doEncodeString( v : String ) {
var vid = hstrings.get(v);
if( vid == null ) {
if( nstrings == 256 ) {
hstrings = new Map();
nstrings = 1;
}
hstrings.set(v,nstrings);
bout.addByte(0);
var vb = haxe.io.Bytes.ofString(v);
bout.addByte(vb.length);
bout.add(vb);
nstrings++;
} else
bout.addByte(vid);
}
function doDecodeString() {
var id = bin.get(pin++);
if( id == 0 ) {
var len = bin.get(pin);
var str = #if (haxe_ver < 3.103) bin.readString(pin+1,len); #else bin.getString(pin+1,len); #end
pin += len + 1;
if( strings.length == 255 )
strings = [null];
strings.push(str);
return str;
}
return strings[id];
}
function doEncodeInt(v: Int) {
bout.addInt32(v);
}
function doEncodeConst( c : Const ) {
switch( c ) {
case CInt(v):
if( v >= 0 && v <= 255 ) {
bout.addByte(0);
bout.addByte(v);
} else {
bout.addByte(1);
doEncodeInt(v);
}
case CFloat(f):
bout.addByte(2);
doEncodeString(Std.string(f));
case CString(s):
bout.addByte(3);
doEncodeString(s);
}
}
function doDecodeInt() {
var i = bin.getInt32(pin);
pin += 4;
return i;
}
function doDecodeConst() {
return switch( bin.get(pin++) ) {
case 0:
CInt(bin.get(pin++));
case 1:
var i = doDecodeInt();
CInt(i);
case 2:
CFloat( Std.parseFloat(doDecodeString()) );
case 3:
CString( doDecodeString() );
default:
throw "Invalid code "+bin.get(pin-1);
}
}
function doEncode( e : Expr ) {
#if hscriptPos
doEncodeString(e.origin);
doEncodeInt(e.line);
var e = e.e;
#end
bout.addByte(exprIndex(e));
switch( e ) {
case EConst(c):
doEncodeConst(c);
case EIdent(v):
doEncodeString(v);
case EVar(n,t,e):
doEncodeString(n);
encodeType(t);
if( e == null )
bout.addByte(255);
else
doEncode(e);
case EParent(e):
doEncode(e);
case EBlock(el):
bout.addByte(el.length);
for( e in el )
doEncode(e);
case EField(e,f):
doEncode(e);
doEncodeString(f);
case EBinop(op,e1,e2):
doEncodeString(op);
doEncode(e1);
doEncode(e2);
case EUnop(op,prefix,e):
doEncodeString(op);
bout.addByte(prefix?1:0);
doEncode(e);
case ECall(e,el):
doEncode(e);
bout.addByte(el.length);
for( e in el )
doEncode(e);
case EIf(cond,e1,e2):
doEncode(cond);
doEncode(e1);
if( e2 == null )
bout.addByte(255);
else
doEncode(e2);
case EWhile(cond,e):
doEncode(cond);
doEncode(e);
case EDoWhile(cond,e):
doEncode(cond);
doEncode(e);
case EFor(v,it,e):
doEncodeString(v);
doEncode(it);
doEncode(e);
case EForGen(it,e):
doEncode(it);
doEncode(e);
case EBreak, EContinue:
case EFunction(params,e,name,t):
bout.addByte(params.length);
for( p in params )
doEncodeString(p.name);
doEncode(e);
doEncodeString(name == null?"":name);
encodeType(t);
case EReturn(e):
if( e == null )
bout.addByte(255);
else
doEncode(e);
case EArray(e,index):
doEncode(e);
doEncode(index);
case EArrayDecl(el):
if( el.length >= 255 ) throw "assert";
bout.addByte(el.length);
for( e in el )
doEncode(e);
case ENew(cl,params):
doEncodeString(cl);
bout.addByte(params.length);
for( e in params )
doEncode(e);
case EThrow(e):
doEncode(e);
case ETry(e,v,t,ecatch):
doEncode(e);
doEncodeString(v);
encodeType(t);
doEncode(ecatch);
case EObject(fl):
bout.addByte(fl.length);
for( f in fl ) {
doEncodeString(f.name);
doEncode(f.e);
}
case ETernary(cond, e1, e2):
doEncode(cond);
doEncode(e1);
doEncode(e2);
case ESwitch(e, cases, def):
doEncode(e);
for( c in cases ) {
if( c.values.length == 0 ) throw "assert";
for( v in c.values )
doEncode(v);
bout.addByte(255);
doEncode(c.expr);
}
bout.addByte(255);
if( def == null ) bout.addByte(255) else doEncode(def);
case EMeta(name,args,e):
doEncodeString(name);
bout.addByte(args == null ? 0 : args.length + 1);
if( args != null ) for( e in args ) doEncode(e);
doEncode(e);
case ECheckType(e,t):
doEncode(e);
encodeType(t);
case ECast(e, t):
doEncode(e);
encodeType(t);
}
}
function encodeType( t : CType ) {
if( !storeTypes )
return;
if( t == null ) {
bout.addByte(255);
return;
}
bout.addByte(t.getIndex());
switch( t ) {
case CTPath(path, params):
doEncodeString(path.join("."));
if( params == null )
bout.addByte(255);
else {
bout.addByte(params.length);
for( p in params )
encodeType(p);
}
case CTFun(args, ret):
bout.addByte(args.length);
for( a in args )
encodeType(a);
encodeType(ret);
case CTAnon(fields):
doEncodeInt(fields.length);
for( f in fields ) {
doEncodeString(f.name);
encodeType(f.t);
if( f.meta == null )
bout.addByte(255);
else {
bout.addByte(f.meta.length);
for( m in f.meta ) {
doEncodeString(m.name);
doEncodeInt(m.params.length);
for( p in m.params )
doEncode(p);
}
}
}
case CTParent(t):
encodeType(t);
case CTOpt(t):
encodeType(t);
case CTNamed(n, t):
doEncodeString(n);
encodeType(t);
case CTExpr(e):
doEncode(e);
}
}
function exprIndex(e):Int {
return switch (e) {
case EConst(_): 0;
case EIdent(_): 1;
case EVar(_): 2;
case EParent(_): 3;
case EBlock(_): 4;
case EField(_): 5;
case EBinop(_): 6;
case EUnop(_): 7;
case ECall(_): 8;
case EIf(_): 9;
case EWhile(_): 10;
case EFor(_): 11;
case EBreak: 12;
case EContinue: 13;
case EFunction(_): 14;
case EReturn(_): 15;
case EArray(_): 16;
case EArrayDecl(_): 17;
case ENew(_): 18;
case EThrow(_): 19;
case ETry(_): 20;
case EObject(_): 21;
case ETernary(_): 22;
case ESwitch(_): 23;
case EDoWhile(_): 24;
case EMeta(_): 25;
case ECheckType(_): 26;
case EForGen(_): 27;
case ECast(_): 28;
}
}
function doDecode() : Expr {
#if hscriptPos
if( getByte() == 255 )
return null;
pin--;
var origin = doDecodeString();
var line = doDecodeInt();
return { e : _doDecode(), pmin : 0, pmax : 0, origin : origin, line : line };
}
function _doDecode() : ExprDef {
#end
return switch( getByte() ) {
case 0:
EConst( doDecodeConst() );
case 1:
EIdent( doDecodeString() );
case 2:
var v = doDecodeString();
EVar(v,decodeType(),doDecode());
case 3:
EParent(doDecode());
case 4:
var a = new Array();
for( i in 0...getByte() )
a.push(doDecode());
EBlock(a);
case 5:
var e = doDecode();
EField(e,doDecodeString());
case 6:
var op = doDecodeString();
var e1 = doDecode();
EBinop(op,e1,doDecode());
case 7:
var op = doDecodeString();
var prefix = getByte() != 0;
EUnop(op,prefix,doDecode());
case 8:
var e = doDecode();
var params = new Array();
for( i in 0...getByte() )
params.push(doDecode());
ECall(e,params);
case 9:
var cond = doDecode();
var e1 = doDecode();
EIf(cond,e1,doDecode());
case 10:
var cond = doDecode();
EWhile(cond,doDecode());
case 11:
var v = doDecodeString();
var it = doDecode();
EFor(v,it,doDecode());
case 12:
EBreak;
case 13:
EContinue;
case 14:
var params = new Array<Argument>();
for( i in 0...getByte() )
params.push({ name : doDecodeString() });
var e = doDecode();
var name = doDecodeString();
EFunction(params,e,(name == "") ? null: name,decodeType());
case 15:
EReturn(doDecode());
case 16:
var e = doDecode();
EArray(e,doDecode());
case 17:
var el = new Array();
for( i in 0...getByte() )
el.push(doDecode());
EArrayDecl(el);
case 18:
var cl = doDecodeString();
var el = new Array();
for( i in 0...getByte() )
el.push(doDecode());
ENew(cl,el);
case 19:
EThrow(doDecode());
case 20:
var e = doDecode();
var v = doDecodeString();
ETry(e,v,decodeType(),doDecode());
case 21:
var fl = new Array();
for( i in 0...getByte() ) {
var name = doDecodeString();
var e = doDecode();
fl.push({ name : name, e : e });
}
EObject(fl);
case 22:
var cond = doDecode();
var e1 = doDecode();
var e2 = doDecode();
ETernary(cond, e1, e2);
case 23:
var e = doDecode();
var cases = [];
while( true ) {
var v = doDecode();
if( v == null ) break;
var values = [v];
while( true ) {
v = doDecode();
if( v == null ) break;
values.push(v);
}
cases.push( { values : values, expr : doDecode() } );
}
var def = doDecode();
ESwitch(e, cases, def);
case 24:
var cond = doDecode();
EDoWhile(cond,doDecode());
case 25:
var name = doDecodeString();
var count = getByte();
var args = count == 0 ? null : [for( i in 0...count - 1 ) doDecode()];
EMeta(name, args, doDecode());
case 26:
ECheckType(doDecode(), decodeType());
case 27:
EForGen(doDecode(), doDecode());
case 28:
ECast(doDecode(), decodeType());
case 255:
null;
default:
throw "Invalid code "+bin.get(pin - 1);
}
}
inline function getByte() {
return bin.get(pin++);
}
function decodeType() : CType {
if( !storeTypes )
return null;
return switch( getByte() ) {
case 255: null;
case 0:
var path = doDecodeString().split(".");
var plen = getByte();
var params = plen == 255 ? null : [for( i in 0...plen ) decodeType()];
CTPath(path, params);
case 1:
CTFun([for( v in 0...getByte() ) decodeType()], decodeType());
case 2:
CTAnon([for( v in 0...doDecodeInt() ) { name : doDecodeString(), t : decodeType(), meta : {
var v = getByte();
if( v == 255 ) null else [for( i in 0...v ) { name : doDecodeString(), params : [for( i in 0...getByte() ) doDecode()] }];
}}]);
case 3:
CTParent(decodeType());
case 4:
CTOpt(decodeType());
case 5:
CTNamed(doDecodeString(),decodeType());
case 6:
CTExpr(doDecode());
default:
throw "Invalid code "+bin.get(pin - 1);
}
}
public static function encode( e : Expr ) : haxe.io.Bytes {
var b = new Bytes();
b.doEncode(e);
return b.bout.getBytes();
}
public static function decode( bytes : haxe.io.Bytes ) : Expr {
var b = new Bytes(bytes);
return b.doDecode();
}
}

File diff suppressed because it is too large Load Diff

180
lib/hscript/hscript/Expr.hx Normal file
View File

@ -0,0 +1,180 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
enum Const {
CInt( v : Int );
CFloat( f : Float );
CString( s : String );
}
#if hscriptPos
typedef Expr = {
var e : ExprDef;
var pmin : Int;
var pmax : Int;
var origin : String;
var line : Int;
}
enum ExprDef {
#else
typedef ExprDef = Expr;
enum Expr {
#end
EConst( c : Const );
EIdent( v : String );
EVar( n : String, ?t : CType, ?e : Expr );
EParent( e : Expr );
EBlock( e : Array<Expr> );
EField( e : Expr, f : String );
EBinop( op : String, e1 : Expr, e2 : Expr );
EUnop( op : String, prefix : Bool, e : Expr );
ECall( e : Expr, params : Array<Expr> );
EIf( cond : Expr, e1 : Expr, ?e2 : Expr );
EWhile( cond : Expr, e : Expr );
EFor( v : String, it : Expr, e : Expr );
EBreak;
EContinue;
EFunction( args : Array<Argument>, e : Expr, ?name : String, ?ret : CType );
EReturn( ?e : Expr );
EArray( e : Expr, index : Expr );
EArrayDecl( e : Array<Expr> );
ENew( cl : String, params : Array<Expr> );
EThrow( e : Expr );
ETry( e : Expr, v : String, t : Null<CType>, ecatch : Expr );
EObject( fl : Array<{ name : String, e : Expr }> );
ETernary( cond : Expr, e1 : Expr, e2 : 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 );
EForGen( it : Expr, e : Expr );
ECast( e : Expr, ?t : CType );
}
typedef Argument = { name : String, ?t : CType, ?opt : Bool, ?value : Expr };
typedef Metadata = Array<{ name : String, params : Array<Expr> }>;
enum CType {
CTPath( path : Array<String>, ?params : Array<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 );
CTExpr( e : Expr ); // for type parameters only
}
#if hscriptPos
class Error {
public var e : ErrorDef;
public var pmin : Int;
public var pmax : Int;
public var origin : String;
public var line : Int;
public function new(e, pmin, pmax, origin, line) {
this.e = e;
this.pmin = pmin;
this.pmax = pmax;
this.origin = origin;
this.line = line;
}
public function toString(): String {
return Printer.errorToString(this);
}
}
enum ErrorDef {
#else
enum Error {
#end
EInvalidChar( c : Int );
EUnexpected( s : String );
EUnterminatedString;
EUnterminatedComment;
EInvalidPreprocessor( msg : String );
EUnknownVariable( v : String );
EInvalidIterator( v : String );
EInvalidOp( op : String );
EInvalidAccess( f : String );
ECustom( msg : String );
}
enum ModuleDecl {
DPackage( path : Array<String> );
DImport( path : Array<String>, ?everything : Bool, ?name : String );
DClass( c : ClassDecl );
DTypedef( c : TypeDecl );
}
typedef ModuleType = {
var name : String;
var params : {}; // TODO : not yet parsed
var meta : Metadata;
var isPrivate : Bool;
}
typedef ClassDecl = {> ModuleType,
var extend : Null<CType>;
var implement : Array<CType>;
var fields : Array<FieldDecl>;
var isExtern : Bool;
}
typedef TypeDecl = {> ModuleType,
var t : CType;
}
typedef FieldDecl = {
var name : String;
var meta : Metadata;
var kind : FieldKind;
var access : Array<FieldAccess>;
}
enum FieldAccess {
APublic;
APrivate;
AInline;
AOverride;
AStatic;
AMacro;
}
enum FieldKind {
KFunction( f : FunctionDecl );
KVar( v : VarDecl );
}
typedef FunctionDecl = {
var args : Array<Argument>;
var expr : Expr;
var ret : Null<CType>;
}
typedef VarDecl = {
var get : Null<String>;
var set : Null<String>;
var expr : Null<Expr>;
var type : Null<CType>;
}

View File

@ -0,0 +1,745 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import haxe.PosInfos;
import hscript.Expr;
import haxe.Constraints.IMap;
private enum Stop {
SBreak;
SContinue;
SReturn;
}
class Interp {
public var variables : Map<String,Dynamic>;
var locals : Map<String,{ r : Dynamic }>;
var binops : Map<String, Expr -> Expr -> Dynamic >;
var depth : Int;
var inTry : Bool;
var declared : Array<{ n : String, old : { r : Dynamic } }>;
var returnValue : Dynamic;
#if hscriptPos
var curExpr : Expr;
#end
public function new() {
locals = new Map();
declared = new Array();
resetVariables();
initOps();
}
private function resetVariables(){
variables = new Map<String,Dynamic>();
variables.set("null",null);
variables.set("true",true);
variables.set("false",false);
variables.set("trace", Reflect.makeVarArgs(function(el) {
var inf = posInfos();
var v = el.shift();
if( el.length > 0 ) inf.customParams = el;
haxe.Log.trace(Std.string(v), inf);
}));
}
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() {
var me = this;
binops = new Map();
binops.set("+",function(e1,e2) return me.expr(e1) + me.expr(e2));
binops.set("-",function(e1,e2) return me.expr(e1) - me.expr(e2));
binops.set("*",function(e1,e2) return me.expr(e1) * me.expr(e2));
binops.set("/",function(e1,e2) return me.expr(e1) / me.expr(e2));
binops.set("%",function(e1,e2) return me.expr(e1) % me.expr(e2));
binops.set("&",function(e1,e2) return me.expr(e1) & me.expr(e2));
binops.set("|",function(e1,e2) return me.expr(e1) | me.expr(e2));
binops.set("^",function(e1,e2) return me.expr(e1) ^ me.expr(e2));
binops.set("<<",function(e1,e2) return me.expr(e1) << me.expr(e2));
binops.set(">>",function(e1,e2) return me.expr(e1) >> me.expr(e2));
binops.set(">>>",function(e1,e2) return me.expr(e1) >>> me.expr(e2));
binops.set("==",function(e1,e2) return me.expr(e1) == me.expr(e2));
binops.set("!=",function(e1,e2) return me.expr(e1) != me.expr(e2));
binops.set(">=",function(e1,e2) return me.expr(e1) >= me.expr(e2));
binops.set("<=",function(e1,e2) return me.expr(e1) <= me.expr(e2));
binops.set(">",function(e1,e2) return me.expr(e1) > me.expr(e2));
binops.set("<",function(e1,e2) return me.expr(e1) < me.expr(e2));
binops.set("||",function(e1,e2) return me.expr(e1) == true || me.expr(e2) == true);
binops.set("&&",function(e1,e2) return me.expr(e1) == true && me.expr(e2) == true);
binops.set("=",assign);
binops.set("...",function(e1,e2) return new IntIterator(me.expr(e1),me.expr(e2)));
binops.set("is",function(e1,e2) return #if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (me.expr(e1), me.expr(e2)));
assignOp("+=",function(v1:Dynamic,v2:Dynamic) return v1 + v2);
assignOp("-=",function(v1:Float,v2:Float) return v1 - v2);
assignOp("*=",function(v1:Float,v2:Float) return v1 * v2);
assignOp("/=",function(v1:Float,v2:Float) return v1 / v2);
assignOp("%=",function(v1:Float,v2:Float) return v1 % v2);
assignOp("&=",function(v1,v2) return v1 & v2);
assignOp("|=",function(v1,v2) return v1 | v2);
assignOp("^=",function(v1,v2) return v1 ^ v2);
assignOp("<<=",function(v1,v2) return v1 << v2);
assignOp(">>=",function(v1,v2) return v1 >> v2);
assignOp(">>>=",function(v1,v2) return v1 >>> v2);
}
function setVar( name : String, v : Dynamic ) {
variables.set(name, v);
return v;
}
function assign( e1 : Expr, e2 : Expr ) : Dynamic {
var v = expr(e2);
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
if( l == null )
setVar(id,v)
else
l.r = v;
case EField(e,f):
v = set(expr(e),f,v);
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
setMapValue(arr, index, v);
}
else {
arr[index] = v;
}
default:
error(EInvalidOp("="));
}
return v;
}
function assignOp( op, fop : Dynamic -> Dynamic -> Dynamic ) {
var me = this;
binops.set(op,function(e1,e2) return me.evalAssignOp(op,fop,e1,e2));
}
function evalAssignOp(op,fop,e1,e2) : Dynamic {
var v;
switch( Tools.expr(e1) ) {
case EIdent(id):
var l = locals.get(id);
v = fop(expr(e1),expr(e2));
if( l == null )
setVar(id,v)
else
l.r = v;
case EField(e,f):
var obj = expr(e);
v = fop(get(obj,f),expr(e2));
v = set(obj,f,v);
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
v = fop(getMapValue(arr, index), expr(e2));
setMapValue(arr, index, v);
}
else {
v = fop(arr[index],expr(e2));
arr[index] = v;
}
default:
return error(EInvalidOp(op));
}
return v;
}
function increment( e : Expr, prefix : Bool, delta : Int ) : Dynamic {
#if hscriptPos
curExpr = e;
var e = e.e;
#end
switch(e) {
case EIdent(id):
var l = locals.get(id);
var v : Dynamic = (l == null) ? resolve(id) : l.r;
if( prefix ) {
v += delta;
if( l == null ) setVar(id,v) else l.r = v;
} else
if( l == null ) setVar(id,v + delta) else l.r = v + delta;
return v;
case EField(e,f):
var obj = expr(e);
var v : Dynamic = get(obj,f);
if( prefix ) {
v += delta;
set(obj,f,v);
} else
set(obj,f,v + delta);
return v;
case EArray(e, index):
var arr:Dynamic = expr(e);
var index:Dynamic = expr(index);
if (isMap(arr)) {
var v = getMapValue(arr, index);
if (prefix) {
v += delta;
setMapValue(arr, index, 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:
return error(EInvalidOp((delta > 0)?"++":"--"));
}
}
public function execute( expr : Expr ) : Dynamic {
depth = 0;
locals = new Map();
declared = new Array();
return exprReturn(expr);
}
function exprReturn(e) : Dynamic {
try {
return expr(e);
} catch( e : Stop ) {
switch( e ) {
case SBreak: throw "Invalid break";
case SContinue: throw "Invalid continue";
case SReturn:
var v = returnValue;
returnValue = null;
return v;
}
}
return null;
}
function duplicate<T>( h : Map<String,T> ) {
var h2 = new Map();
for( k in h.keys() )
h2.set(k,h.get(k));
return h2;
}
function restore( old : Int ) {
while( declared.length > old ) {
var d = declared.pop();
locals.set(d.n,d.old);
}
}
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;
return null;
}
inline function rethrow( e : Dynamic ) {
#if hl
hl.Api.rethrow(e);
#else
throw e;
#end
}
function resolve( id : String ) : Dynamic {
var v = variables.get(id);
if( v == null && !variables.exists(id) )
error(EUnknownVariable(id));
return v;
}
public function expr( e : Expr ) : Dynamic {
#if hscriptPos
curExpr = e;
var e = e.e;
#end
switch( e ) {
case EConst(c):
switch( c ) {
case CInt(v): return v;
case CFloat(f): return f;
case CString(s): return s;
}
case EIdent(id):
var l = locals.get(id);
if( l != null )
return l.r;
return resolve(id);
case EVar(n,_,e):
declared.push({ n : n, old : locals.get(n) });
locals.set(n,{ r : (e == null)?null:expr(e) });
return null;
case EParent(e):
return expr(e);
case EBlock(exprs):
var old = declared.length;
var v = null;
for( e in exprs )
v = expr(e);
restore(old);
return v;
case EField(e,f):
return get(expr(e),f);
case EBinop(op,e1,e2):
var fop = binops.get(op);
if( fop == null ) error(EInvalidOp(op));
return fop(e1,e2);
case EUnop(op,prefix,e):
switch(op) {
case "!":
return expr(e) != true;
case "-":
return -expr(e);
case "++":
return increment(e,prefix,1);
case "--":
return increment(e,prefix,-1);
case "~":
return ~expr(e);
default:
error(EInvalidOp(op));
}
case ECall(e,params):
var args = new Array();
for( p in params )
args.push(expr(p));
switch( Tools.expr(e) ) {
case EField(e,f):
var obj = expr(e);
if( obj == null ) error(EInvalidAccess(f));
return fcall(obj,f,args);
default:
return call(null,expr(e),args);
}
case EIf(econd,e1,e2):
return if( expr(econd) == true ) expr(e1) else if( e2 == null ) null else expr(e2);
case EWhile(econd,e):
whileLoop(econd,e);
return null;
case EDoWhile(econd,e):
doWhileLoop(econd,e);
return null;
case EFor(v,it,e):
forLoop(v,it,e);
return null;
case EForGen(it,e):
Tools.getKeyIterator(it, function(vk,vv,it) {
if( vk == null ) {
#if hscriptPos
curExpr = it;
#end
error(ECustom("Invalid for expression"));
return;
}
forKeyValueLoop(vk,vv,it,e);
});
return null;
case EBreak:
throw SBreak;
case EContinue:
throw SContinue;
case EReturn(e):
returnValue = e == null ? null : expr(e);
throw SReturn;
case EFunction(params,fexpr,name,_):
var capturedLocals = duplicate(locals);
var me = this;
var hasOpt = false, minParams = 0;
for( p in params )
if( p.opt )
hasOpt = true;
else
minParams++;
var f = function(args:Array<Dynamic>) {
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+"'";
error(ECustom(str));
}
// make sure mandatory args are forced
var args2 = [];
var extraParams = args.length - minParams;
var pos = 0;
for( p in params )
if( p.opt ) {
if( extraParams > 0 ) {
args2.push(args[pos++]);
extraParams--;
} else
args2.push(null);
} else
args2.push(args[pos++]);
args = args2;
}
var old = me.locals, depth = me.depth;
me.depth++;
me.locals = me.duplicate(capturedLocals);
for( i in 0...params.length )
me.locals.set(params[i].name,{ r : args[i] });
var r = null;
var oldDecl = declared.length;
if( inTry )
try {
r = me.exprReturn(fexpr);
} catch( e : Dynamic ) {
restore(oldDecl);
me.locals = old;
me.depth = depth;
#if neko
neko.Lib.rethrow(e);
#else
throw e;
#end
}
else
r = me.exprReturn(fexpr);
restore(oldDecl);
me.locals = old;
me.depth = depth;
return r;
};
var f = Reflect.makeVarArgs(f);
if( name != null ) {
if( depth == 0 ) {
// global function
variables.set(name, f);
} else {
// function-in-function is a local function
declared.push( { n : name, old : locals.get(name) } );
var ref = { r : f };
locals.set(name, ref);
capturedLocals.set(name, ref); // allow self-recursion
}
}
return f;
case EArrayDecl(arr):
if( arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _)) ) {
var keys = [];
var values = [];
for( e in arr ) {
switch(Tools.expr(e)) {
case EBinop("=>", eKey, eValue):
keys.push(expr(eKey));
values.push(expr(eValue));
default:
#if hscriptPos
curExpr = e;
#end
error(ECustom("Invalid map key=>value expression"));
}
}
return makeMap(keys,values);
} 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);
return arr[index];
case ENew(cl,params):
var a = new Array();
for( e in params )
a.push(expr(e));
return cnew(cl,a);
case EThrow(e):
throw expr(e);
case ETry(e,n,_,ecatch):
var old = declared.length;
var oldTry = inTry;
try {
inTry = true;
var v : Dynamic = expr(e);
restore(old);
inTry = oldTry;
return v;
} catch( err : Stop ) {
inTry = oldTry;
throw err;
} catch( err : Dynamic ) {
// restore vars
restore(old);
inTry = oldTry;
// declare 'v'
declared.push({ n : n, old : locals.get(n) });
locals.set(n,{ r : err });
var v : Dynamic = expr(ecatch);
restore(old);
return v;
}
case EObject(fl):
var o = {};
for( f in fl )
set(o,f.name,expr(f.e));
return o;
case ETernary(econd,e1,e2):
return if( expr(econd) == true ) expr(e1) else expr(e2);
case ESwitch(e, cases, def):
var val : Dynamic = expr(e);
var match = false;
for( c in cases ) {
for( v in c.values )
if( expr(v) == val ) {
match = true;
break;
}
if( match ) {
val = expr(c.expr);
break;
}
}
if( !match )
val = def == null ? null : expr(def);
return val;
case EMeta(meta, args, e):
return exprMeta(meta, args, e);
case ECheckType(e,_), ECast(e,_):
return expr(e);
}
return null;
}
function exprMeta(meta,args,e) : Dynamic {
return expr(e);
}
function doWhileLoop(econd,e) {
var old = declared.length;
do {
if( !loopRun(() -> expr(e)) )
break;
}
while( expr(econd) == true );
restore(old);
}
function whileLoop(econd,e) {
var old = declared.length;
while( expr(econd) == true ) {
if( !loopRun(() -> expr(e)) )
break;
}
restore(old);
}
function makeIterator( v : Dynamic ) : Iterator<Dynamic> {
#if js
// don't use try/catch (very slow)
if( v is Array )
return (v : Array<Dynamic>).iterator();
if( v.iterator != null ) v = v.iterator();
#else
#if (cpp) if ( v.iterator != null ) #end
try v = v.iterator() catch( e : Dynamic ) {};
#end
if( v.hasNext == null || v.next == null ) error(EInvalidIterator(v));
return v;
}
function makeKeyValueIterator( v : Dynamic ) : KeyValueIterator<Dynamic,Dynamic> {
#if js
// don't use try/catch (very slow)
if( v is Array )
return (v : Array<Dynamic>).keyValueIterator();
if( v.keyValueIterator != null ) v = v.keyValueIterator();
#else
try v = v.keyValueIterator() catch( e : Dynamic ) {};
#end
if( v.hasNext == null || v.next == null ) error(EInvalidIterator(v));
return v;
}
function forLoop(n,it,e) {
var old = declared.length;
declared.push({ n : n, old : locals.get(n) });
var it = makeIterator(expr(it));
while( it.hasNext() ) {
locals.set(n,{ r : it.next() });
if( !loopRun(() -> expr(e)) )
break;
}
restore(old);
}
function forKeyValueLoop(vk,vv,it,e) {
var old = declared.length;
declared.push({ n : vk, old : locals.get(vk) });
declared.push({ n : vv, old : locals.get(vv) });
var it = makeKeyValueIterator(expr(it));
while( it.hasNext() ) {
var v = it.next();
locals.set(vk,{ r : v.key });
locals.set(vv,{ r : v.value });
if( !loopRun(() -> expr(e)) )
break;
}
restore(old);
}
inline function loopRun( f : Void -> Void ) {
var cont = true;
try {
f();
} catch( err : Stop ) {
switch( err ) {
case SContinue:
case SBreak:
cont = false;
case SReturn:
throw err;
}
}
return cont;
}
inline function isMap(o:Dynamic):Bool {
return (o is 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 makeMap( keys : Array<Dynamic>, values : Array<Dynamic> ) : Dynamic {
var isAllString:Bool = true;
var isAllInt:Bool = true;
var isAllObject:Bool = true;
var isAllEnum:Bool = true;
for( key in keys ) {
isAllString = isAllString && (key is String);
isAllInt = isAllInt && (key is Int);
isAllObject = isAllObject && Reflect.isObject(key);
isAllEnum = isAllEnum && Reflect.isEnumValue(key);
}
#if (haxe_ver >= 4.1)
if( isAllInt ) {
var m = new Map<Int,Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
if( isAllString ) {
var m = new Map<String,Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
if( isAllEnum ) {
var m = new haxe.ds.EnumValueMap<Dynamic,Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
if( isAllObject ) {
var m = new Map<{},Dynamic>();
for( i => key in keys )
m.set(key, values[i]);
return m;
}
#else
var m: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 null;
}
if( m != null ) {
for ( n in 0...keys.length )
setMapValue(m, keys[n], values[n]);
return m;
}
#end
error(ECustom("Invalid map keys "+keys));
return null;
}
function get( o : Dynamic, f : String ) : Dynamic {
if ( o == null ) error(EInvalidAccess(f));
return {
#if php
// https://github.com/HaxeFoundation/haxe/issues/4915
try {
Reflect.getProperty(o, f);
} catch (e:Dynamic) {
Reflect.field(o, f);
}
#else
Reflect.getProperty(o, f);
#end
}
}
function set( o : Dynamic, f : String, v : Dynamic ) : Dynamic {
if( o == null ) error(EInvalidAccess(f));
Reflect.setProperty(o,f,v);
return v;
}
function fcall( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
return call(o, get(o, f), args);
}
function call( o : Dynamic, f : Dynamic, args : Array<Dynamic> ) : Dynamic {
return Reflect.callMethod(o,f,args);
}
function cnew( cl : String, args : Array<Dynamic> ) : Dynamic {
var c = Type.resolveClass(cl);
if( c == null ) c = resolve(cl);
return Type.createInstance(c,args);
}
}

View File

@ -0,0 +1,448 @@
package hscript;
class JsInterp extends Interp {
/**
Variables declared in `ctx` are directly accessed without going through the `variables` map, which is faster
**/
public var ctx : {};
/**
If properties are defined, all calls to get/set are only done for fields accesses which are listed here.
**/
public var properties : Map<String,Bool>;
var localNames : Map<String,String>;
var hasBreakContinue : Int = 0;
var hasReturn : Bool;
override function execute( expr : Expr ) : Dynamic {
depth = 0;
localNames = new Map();
var str = '(($$i) => ${exprValue(expr)})';
var f : Dynamic -> Dynamic = js.Lib.eval(str);
return f(this);
}
public static function defineArrayExtensions() {
var arr : Dynamic = Array;
arr.prototype.copy = function() { var v : Array<Dynamic> = js.Lib.nativeThis; return [for( v in v ) v]; };
arr.prototype.contains = function(i) return js.Lib.nativeThis.indexOf(i) >= 0;
arr.prototype.remove = function(x) return @:privateAccess HxOverrides.remove(js.Lib.nativeThis, x);
arr.prototype.resize = function(len) js.Lib.nativeThis.length = len;
}
function escapeString(s:String) {
return s.split("\\").join("\\\\").split("\r").join("\\r").split("\n").join("\\n").split('"').join('\\"');
}
function handleRBC( e : Expr ) {
function mkRBC(code:Int,?value) {
var fields = [{ name : "$", e : Tools.mk(EConst(CInt(code)),e) }];
if( value != null ) fields.push({name:"$val",e:value});
return Tools.mk(EThrow(Tools.mk(EObject(fields),e)),e);
}
switch( Tools.expr(e) ) {
case EReturn(v):
hasReturn = true;
return mkRBC(1,v);
case EBreak if( hasBreakContinue >= 0 ):
hasBreakContinue |= 1;
return mkRBC(2);
case EContinue if( hasBreakContinue >= 0 ):
hasBreakContinue |= 2;
return mkRBC(3);
case EWhile(_), EFor(_), EDoWhile(_):
var prev = hasBreakContinue;
hasBreakContinue = -1;
e = Tools.map(e, handleRBC);
hasBreakContinue = prev;
return e;
case EFunction(_):
return e;
default:
return Tools.map(e, handleRBC);
}
}
function exprValue( expr : Expr ) {
switch( Tools.expr(expr) ) {
case EBlock([]):
return "null";
case EIf(cond,e1,e2):
return exprJS(Tools.mk(ETernary(cond,e1,e2),expr));
case EBlock(el):
var el = [for( e in el ) handleRBC(e)];
var last = el[el.length-1];
switch( Tools.expr(last) ) {
case EFunction(_,_,name,_) if( name != null ): // don't return latest named function
default:
el[el.length - 1] = Tools.mk(EReturn(last),last);
}
var ebl = Tools.mk(EBlock(el),expr);
return '(() => ${exprJS(ebl)})()';
case ETry(e,v,t,ecatch):
e = handleRBC(e);
ecatch = handleRBC(ecatch);
var expr = Tools.mk(ETry(Tools.mk(EReturn(e),e),v,t,Tools.mk(EReturn(ecatch),ecatch)),expr);
return '(() => { ${exprJS(expr)} })()';
case EVar(_,_,e):
return e == null ? "null" : '(${exprValue(e)},null)';
case EWhile(_), EFor(_), EDoWhile(_), EThrow(_):
expr = handleRBC(expr);
return '(() => {${exprJS(expr)}})()';
case EMeta(_,_,e), ECheckType(e,_):
return exprValue(e);
case EFunction(_,_,name,_) if( name != null ):
return '(() => {${exprJS(expr)}})()';
default:
return exprJS(expr);
}
}
function exprBlock( expr : Expr ) {
switch( Tools.expr(expr) ) {
case EBlock(_):
return exprJS(expr);
default:
return '{${exprJS(expr)};}';
}
}
function addPos( estr : String ) {
#if hscriptPos
var expr = curExpr;
var p = '{pmin:,pmax:,origin:"",line:}';
return '($$i._p(${expr.pmin},${expr.pmax},"${expr.origin}",${expr.line}),$estr)';
#else
return estr;
#end
}
function isContext(v:String) {
return ctx != null && Reflect.hasField(ctx,v);
}
function isProperty(f:String) {
return properties == null || properties.get(f);
}
function exprCond( e : Expr ) {
return switch( Tools.expr(e) ) {
case EBinop("=="|"!="|">="|">"|"<="|"<"|"&&"|"||",_): exprValue(e);
default: '(${exprOp(e)} == true)';
}
}
function exprOp( e : Expr ) {
return switch( Tools.expr(e) ) {
case EBinop(_), EUnop(_): '(${exprValue(e)})';
default: exprValue(e);
}
}
function declLocal( n : String ) {
if( !localNames.exists(n) ) {
localNames.set(n, n);
return n;
}
var c = 2;
while( localNames.exists(n+c) ) c++;
localNames.set(n, n+c);
return n+c;
}
function exprBreakContinue( e, needBlock=true ) {
var prevBC = hasBreakContinue;
hasBreakContinue = 0;
var estr = needBlock ? exprBlock(e) : exprJS(e);
if( hasBreakContinue != 0 ) {
var checks = [];
if( hasBreakContinue & 1 != 0 )
checks.push("if( $e.$ == 2 ) break;");
if( hasBreakContinue & 2 != 0 )
checks.push("if( $e.$ == 3 ) continue;");
if( !needBlock && !Tools.expr(e).match(EBlock(_)) )
estr = '{$estr;}';
estr = 'try $estr catch( $$e ) { ${checks.join('')} throw $$e; }';
}
hasBreakContinue = prevBC;
return estr;
}
function exprJS( expr : Expr ) : String {
#if hscriptPos
curExpr = expr;
var expr = expr.e;
#end
switch( expr ) {
case EConst(c):
return switch c {
case CInt(v): Std.string(v);
case CFloat(f): Std.string(f);
case CString(s): '"'+escapeString(s)+'"';
}
case EIdent(v):
var v2 = localNames.get(v);
if( v2 != null )
return v2;
if( isContext(v) )
return '$$i.ctx.$v';
switch( v ) {
case "null", "true", "false": return v;
default:
}
return '$$i.resolve("$v")';
case EVar(n, t, e):
n = declLocal(n);
return e == null ? 'let $n' : 'let $n = ${exprValue(e)}';
case EParent(e):
return '(${exprValue(e)})';
case EBlock(el):
var old = localNames.copy();
// pre define name functions
for( e in el )
switch( Tools.expr(e) ) {
case EFunction(_,_,name,_): declLocal(name);
default:
}
var buf = new StringBuf();
buf.add('{');
for( e in el ) {
buf.add(exprJS(e));
buf.add(";");
}
buf.add('}');
localNames = old;
return buf.toString();
case EField(e,f) if( !isProperty(f) ):
return exprValue(e)+"."+f;
case EField(e, f):
return '$$i.get(${exprValue(e)},"$f")';
case EBinop(op, e1, e2):
switch( op ) {
case "+","-","*","/","%","&","|","^",">>","<<",">>>","==","!=",">=","<=",">","<":
return '${exprOp(e1)} $op ${exprOp(e2)}';
case "||","&&":
return '(${exprCond(e1)} $op ${exprCond(e2)})';
case "=":
switch( Tools.expr(e1) ) {
case EIdent(id) if( localNames.exists(id) ):
return localNames.get(id)+" = "+exprValue(e2);
case EIdent(id) if( isContext(id) ):
return '$$i.ctx.$id = ${exprValue(e2)}';
case EIdent(id):
return '$$i.setVar("$id",${exprValue(e2)})';
case EField(e,f):
return addPos('$$i.set(${exprValue(e)},"$f")');
case EArray(e, index):
return '$$i.setArray(${exprValue(e)},${exprValue(index)},${exprValue(e2)})';
default:
error(EInvalidOp("="));
}
case "+=","-=","*=","/=","%=","|=","&=","^=","<<=",">>=",">>>=":
var aop = op.substr(0, op.length - 1);
switch( Tools.expr(e1) ) {
case EIdent(id) if( localNames.exists(id) ):
return localNames.get(id)+" "+op+" "+exprValue(e2);
case EIdent(id) if( isContext(id) ):
return '$$i.ctx.$id $op ${exprValue(e2)}';
case EIdent(id):
return '$$i.setVar("$id",$$i.resolve("$id") $aop (${exprValue(e2)}))';
case EField(e, f):
return '(($$o,$$v) => $$i.set($$o,"$f",$$i.get($$o,"$f") $aop $$v)(${exprValue(e)},${exprValue(e2)})';
case EArray(e, index):
return '(($$a,$$idx,$$v) => $$i.setArray($$a,$$idx,$$i.getArray($$a,$$idx) $aop $$v))(${exprValue(e)},${exprValue(index)},${exprValue(e2)})';
default:
error(EInvalidOp(op));
}
case "...":
return '$$i.intIterator(${exprValue(e1)},${exprValue(e2)})';
case "is":
return '$$i.isOfType(${exprValue(e1)},${exprValue(e2)})';
default:
error(EInvalidOp(op));
}
case EUnop(op, prefix, e):
switch( op ) {
case "!":
return '${exprValue(e)} != true';
case "-","~":
return op+exprOp(e);
case "++", "--":
switch( Tools.expr(e) ) {
case EIdent(id) if( localNames.exists(id) ):
id = localNames.get(id);
return prefix ? op + id : id + op;
case EIdent(id) if( isContext(id) ):
return prefix ? op + "$i.ctx."+id : "$i.ctx."+id + op;
case _ if( prefix ):
var one = Tools.mk(EConst(CInt(1)),e);
return exprJS(Tools.mk(EBinop(op.charAt(0)+"=",e,one),e));
case EIdent(id):
var op = op.charAt(1);
return '(($$v) => ($$i.setVar("$id",$$v $op 1),$$v))($$i.resolve("$id"))';
case EArray(e, index):
var op = op.charAt(0);
var v = declLocal("$v");
var str = '(($$a,$$idx) => { let $v = $$i.getArray($$a,$$idx); $$i.setArray($$a,$$idx,$v $op 1); return $v; })(${exprValue(e)},${exprValue(index)})';
localNames.remove(v);
return str;
case EField(e, f):
var v = declLocal("$v");
var str = '(($$o) => { let $v = $$i.get($$o,"$f"); $$i.set($$o,"$f",$$v $op 1); return $v; })(${exprValue(e)})';
localNames.remove(v);
return str;
default:
error(EInvalidOp(op));
}
default:
error(EInvalidOp(op));
}
case ECall(e, params):
var args = [for( p in params ) exprValue(p)];
switch( Tools.expr(e) ) {
case EField(eobj,f):
var isCtx = false;
var obj = eobj;
while( true ) {
switch( Tools.expr(obj) ) {
case EField(o,_): obj = o;
case EIdent(i) if( isContext(i) ): isCtx = true; break;
default: break;
}
}
if( isCtx )
return '${exprValue(e)}(${args.join(',')})';
return addPos('$$i.fcall2(${exprValue(eobj)},"$f",[${args.join(',')}])');
case EIdent(id) if( localNames.exists(id) ):
id = localNames.get(id);
return '$id(${args.join(',')})';
case EIdent(id) if( isContext(id) ):
return '$$i.ctx.$id(${args.join(',')})';
default:
return '$$i.call(null,${exprValue(e)},[${args.join(',')}])';
}
case EIf(cond,e1,e2):
return 'if( ${exprCond(cond)} ) ${exprJS(e1)}'+(e2 == null ? "" : 'else ${exprJS(e2)}');
case ETernary(cond, e1, e2):
return '(${exprCond(cond)} ? ${exprValue(e1)} : ${e2 == null ? 'undefined' : exprValue(e2)})';
case EWhile(cond, e):
return 'while( ${exprValue(cond)} ) ${exprBreakContinue(e)}';
case EDoWhile(cond, e):
return 'do ${exprBreakContinue(e)} while( ${exprCond(cond)} )';
case EFor(v, it, e):
v = declLocal(v);
var block = exprBreakContinue(e,false);
localNames.remove(v);
var iter = '$$i.makeIterator(${exprValue(it)})';
var it = declLocal("$it");
var str = '{ let $it = ${addPos(iter)}; while( $it.hasNext() ) { let $v = $it.next(); $block; } }';
localNames.remove(it);
return str;
case EBreak:
return 'break';
case EContinue:
return 'continue';
case EReturn(null):
return 'return';
case EReturn(e):
return 'return '+exprValue(e);
case EArray(e, index):
return '$$i.getArray(${exprValue(e)},${exprValue(index)})';
case EArrayDecl(arr):
if( arr.length > 0 && Tools.expr(arr[0]).match(EBinop("=>", _)) ) {
var keys = [], values = [];
for( e in arr ) {
switch(Tools.expr(e)) {
case EBinop("=>", eKey, eValue):
keys.push(exprValue(eKey));
values.push(exprValue(eValue));
default:
#if hscriptPos
curExpr = e;
#end
error(ECustom("Invalid map key=>value expression"));
}
}
return '$$i.makeMap([${keys.join(',')}],[${values.join(',')}])';
}
return '['+[for( e in arr ) exprValue(e)].join(',')+']';
case ENew(cl, params):
var args = [for( e in params ) exprValue(e)];
return '$$i.cnew("$cl",[${args.join(',')}])';
case EThrow(e):
return "throw "+exprValue(e);
case ETry(e, v, t, ecatch):
v = declLocal(v);
var ec = exprBlock(ecatch);
localNames.remove(v);
return 'try ${exprBlock(e)} catch( $v ) $ec';
case EObject(fl):
var fields = [for( f in fl ) f.name+":"+exprValue(f.e)];
return '{${fields.join(',')}}'; // do not use 'set' here
case EMeta(_, _, e), ECheckType(e,_):
return exprJS(e);
case EFunction(args, e, name, ret):
var prev = localNames.copy();
if( name != null && !localNames.exists(name) )
declLocal(name);
for( a in args )
localNames.set(a.name, a.name);
var prevReturn = hasReturn;
hasReturn = false;
var bl = exprBlock(e);
if( hasReturn )
bl = '{ try { $bl } catch( $$e ) { if( $$e.$$ == 1 ) return $$e.$$val; throw $$e; }}';
hasReturn = prevReturn;
localNames = prev;
var fstr = 'function(${[for( a in args ) a.name].join(",")}) $bl';
if( name != null )
fstr = 'let $name = $$i.setVar("$name",$fstr)';
return fstr;
case ESwitch(e, cases, defaultExpr):
var checks = [for( c in cases ) 'if( ${[for( v in c.values ) '$$v == ${exprValue(handleRBC(v))}'].join(" || ")} ) return ${exprValue(handleRBC(c.expr))};'];
if( defaultExpr != null )
checks.push('return '+exprValue(handleRBC(defaultExpr)));
return '(($$v) => { ${[for( c in checks ) c+";"].join(" ")} })(${exprValue(e)})';
default:
throw "TODO";
}
}
function fcall2( o : Dynamic, f : String, args : Array<Dynamic> ) : Dynamic {
if( o == null ) {
error(EInvalidAccess(f));
return null;
}
return fcall(o,f,args);
}
function getArray( arr : Dynamic, index : Dynamic ) {
return isMap(arr) ? getMapValue(arr,index) : arr[index];
}
function setArray( arr : Dynamic, index : Dynamic, v : Dynamic ) {
if(isMap(arr) )
setMapValue(arr, index, v);
else
arr[index] = v;
return v;
}
function intIterator(v1,v2) {
return new IntIterator(v1,v2);
}
function isOfType(v1,v2) {
return Std.isOfType(v1,v2);
}
#if hscriptPos
function _p( pmin, pmax, origin, line ) {
curExpr = { e : null, pmin : pmin, pmax: pmax, origin:origin, line:line };
}
#end
}

View File

@ -0,0 +1,262 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr.Error;
#if hscriptPos
import hscript.Expr.ErrorDef;
#end
import haxe.macro.Expr;
class Macro {
var p : Position;
var binops : Map<String,Binop>;
var unops : Map<String,Unop>;
public function new(pos) {
p = pos;
binops = new Map();
unops = new Map();
for( c in Type.getEnumConstructs(Binop) ) {
if( c == "OpAssignOp" ) continue;
var op = Type.createEnum(Binop, c);
var assign = false;
var str = switch( op ) {
case OpAdd: assign = true; "+";
case OpMult: assign = true; "*";
case OpDiv: assign = true; "/";
case OpSub: assign = true; "-";
case OpAssign: "=";
case OpEq: "==";
case OpNotEq: "!=";
case OpGt: ">";
case OpGte: ">=";
case OpLt: "<";
case OpLte: "<=";
case OpAnd: assign = true; "&";
case OpOr: assign = true; "|";
case OpXor: assign = true; "^";
case OpBoolAnd: "&&";
case OpBoolOr: "||";
case OpShl: assign = true; "<<";
case OpShr: assign = true; ">>";
case OpUShr: assign = true; ">>>";
case OpMod: assign = true; "%";
case OpAssignOp(_): "";
case OpInterval: "...";
case OpArrow: "=>";
#if (haxe_ver >= 4)
case OpIn: "in";
#end
default:
continue;
};
binops.set(str, op);
if( assign )
binops.set(str + "=", OpAssignOp(op));
}
for( c in Type.getEnumConstructs(Unop) ) {
var op = Type.createEnum(Unop, c);
var str = switch( op ) {
case OpNot: "!";
case OpNeg: "-";
case OpNegBits: "~";
case OpIncrement: "++";
case OpDecrement: "--";
#if (haxe_ver >= 4.2)
case OpSpread: continue;
#end
}
unops.set(str, op);
}
}
function map<T,R>( a : Array<T>, f : T -> R ) : Array<R> {
var b = new Array();
for( x in a )
b.push(f(x));
return b;
}
function convertType( t : Expr.CType ) : ComplexType {
return switch( t ) {
case CTOpt(t): TOptional(convertType(t));
case CTPath(pack, args):
var params = [];
if( args != null ) {
for( t in args )
params.push(switch( t ) {
case CTExpr(e): TPExpr(convert(e));
default: TPType(convertType(t));
});
}
var pack = pack.copy();
TPath({
pack : pack,
name : pack.pop(),
params : params,
sub : null,
});
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 ) {
var meta = f.meta == null ? [] : [for( m in f.meta ) { name : m.name, params : m.params == null ? [] : [for( e in m.params ) convert(e)], pos : p }];
tf.push( { name : f.name, meta : meta, doc : null, access : [], kind : FVar(convertType(f.t), null), pos : p } );
}
TAnonymous(tf);
case CTExpr(_):
throw "assert";
};
}
public function convert( e : hscript.Expr ) : Expr {
return { expr : switch( #if hscriptPos e.e #else e #end ) {
case EConst(c):
EConst(switch(c) {
case CInt(v): CInt(Std.string(v));
case CFloat(f): CFloat(Std.string(f));
case CString(s): CString(s);
});
case EIdent(v):
EConst(CIdent(v));
case EVar(n, t, e):
EVars([ { name : n, expr : if( e == null ) null else convert(e), type : if( t == null ) null else convertType(t) } ]);
case EParent(e):
EParenthesis(convert(e));
case EBlock(el):
EBlock(map(el,convert));
case EField(e, f):
EField(convert(e), f);
case EBinop(op, e1, e2):
var b = binops.get(op);
if( b == null ) throw EInvalidOp(op);
EBinop(b, convert(e1), convert(e2));
case EUnop(op, prefix, e):
var u = unops.get(op);
if( u == null ) throw EInvalidOp(op);
EUnop(u, !prefix, convert(e));
case ECall(e, params):
ECall(convert(e), map(params, convert));
case EIf(c, e1, e2):
EIf(convert(c), convert(e1), e2 == null ? null : convert(e2));
case EWhile(c, e):
EWhile(convert(c), convert(e), true);
case EDoWhile(c, e):
EWhile(convert(c), convert(e), false);
case EFor(v, it, efor):
var p = #if (!macro && hscriptPos) { file : p.file, min : e.pmin, max : e.pmax } #else p #end;
EFor({ expr : EBinop(OpIn,{ expr : EConst(CIdent(v)), pos : p },convert(it)), pos : p }, convert(efor));
case EForGen(it, efor):
EFor(convert(it), convert(efor));
case EBreak:
EBreak;
case EContinue:
EContinue;
case EFunction(args, e, name, ret):
var targs = [];
for( a in args )
targs.push( {
name : a.name,
type : a.t == null ? null : convertType(a.t),
opt : false,
value : null,
});
EFunction(#if haxe4 name != null ? FNamed(name,false) : FAnonymous #else name #end, {
params : [],
args : targs,
expr : convert(e),
ret : ret == null ? null : convertType(ret),
});
case EReturn(e):
EReturn(e == null ? null : convert(e));
case EArray(e, index):
EArray(convert(e), convert(index));
case EArrayDecl(el):
EArrayDecl(map(el,convert));
case ENew(cl, params):
var pack = cl.split(".");
ENew( { pack : pack, name : pack.pop(), params : [], sub : null }, map(params, convert));
case EThrow(e):
EThrow(convert(e));
case ETry(e, v, t, ec):
ETry(convert(e), [ { type : convertType(t), name : v, expr : convert(ec) } ]);
case EObject(fields):
var tf = [];
for( f in fields )
tf.push( { field : f.name, expr : convert(f.e) } );
EObjectDecl(tf);
case ETernary(cond, e1, e2):
ETernary(convert(cond), convert(e1), convert(e2));
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));
case EMeta(m, params, esub):
var mpos = #if (!macro && 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));
case ECast(e, t):
ECast(convert(e), t == null ? null : convertType(t));
}, pos : #if (!macro && hscriptPos) { file : p.file, min : e.pmin, max : e.pmax } #else p #end }
}
public function typeEncode( t : ComplexType ) : Expr.CType {
switch( t ) {
case TPath(p):
var path = p.pack.copy();
path.push(p.name);
if( p.sub != null ) path.push(p.sub);
var params : Array<Expr.CType> = null;
if( p.params != null && p.params.length > 0 )
params = [for( p in p.params ) switch( p ) {
case TPType(t): typeEncode(t);
case TPExpr(e): CTExpr(null); // TODO : macro expr to hscript expr
}];
return CTPath(path,params);
case TFunction(args, ret):
return CTFun([for( a in args ) typeEncode(a)], typeEncode(ret));
case TAnonymous(fields):
return CTAnon([for( f in fields ) { name : f.name, t : switch( f.kind ) {
case FVar(t): typeEncode(t);
case FProp(get,set,t,_): typeEncode(t);
case FFun(f): CTFun([for( a in f.args ) typeEncode(a.type)],typeEncode(f.ret));
}}]);
case TParent(t):
return CTParent(typeEncode(t));
case TOptional(t):
return CTOpt(typeEncode(t));
case TNamed(n, t):
return CTNamed(n,typeEncode(t));
case TIntersection(_), TExtend(_):
throw "assert";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,374 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
class Printer {
var buf : StringBuf;
var tabs : String;
public function new() {
}
public function exprToString( e : Expr ) {
buf = new StringBuf();
tabs = "";
expr(e);
return buf.toString();
}
public function typeToString( t : CType ) {
buf = new StringBuf();
tabs = "";
type(t);
return buf.toString();
}
inline function add<T>(s:T) buf.add(s);
function type( t : CType ) {
switch( t ) {
case CTOpt(t):
add('?');
type(t);
case CTPath(path, params):
add(path.join("."));
if( params != null ) {
add("<");
var first = true;
for( p in params ) {
if( first ) first = false else add(", ");
type(p);
}
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 -> ");
else {
for( a in args ) {
type(a);
add(" -> ");
}
}
type(ret);
case CTAnon(fields):
add("{");
var first = true;
for( f in fields ) {
if( first ) { first = false; add(" "); } else add(", ");
add(f.name + " : ");
type(f.t);
}
add(first ? "}" : " }");
case CTParent(t):
add("(");
type(t);
add(")");
case CTExpr(e):
expr(e);
}
}
function addType( t : CType ) {
if( t != null ) {
add(" : ");
type(t);
}
}
function addConst( c : Const ) {
switch( c ) {
case CInt(i): add(i);
case CFloat(f): add(f);
case CString(s): add('"'); add(s.split('"').join('\\"').split("\n").join("\\n").split("\r").join("\\r").split("\t").join("\\t")); add('"');
}
}
function expr( e : Expr ) {
if( e == null ) {
add("??NULL??");
return;
}
switch( #if hscriptPos e.e #else e #end ) {
case EConst(c): addConst(c);
case EIdent(v):
add(v);
case EVar(n, t, e):
add("var " + n);
addType(t);
if( e != null ) {
add(" = ");
expr(e);
}
case EParent(e):
add("("); expr(e); add(")");
case EBlock(el):
if( el.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( e in el ) {
add(tabs);
expr(e);
add(";\n");
}
tabs = tabs.substr(1);
add("}");
}
case EField(e, f):
expr(e);
add("." + f);
case EBinop(op, e1, e2):
expr(e1);
add(" " + op + " ");
expr(e2);
case EUnop(op, pre, e):
if( pre ) {
add(op);
expr(e);
} else {
expr(e);
add(op);
}
case ECall(e, args):
if( e == null )
expr(e);
else switch( #if hscriptPos e.e #else e #end ) {
case EField(_), EIdent(_), EConst(_):
expr(e);
default:
add("(");
expr(e);
add(")");
}
add("(");
var first = true;
for( a in args ) {
if( first ) first = false else add(", ");
expr(a);
}
add(")");
case EIf(cond,e1,e2):
add("if( ");
expr(cond);
add(" ) ");
expr(e1);
if( e2 != null ) {
add(" else ");
expr(e2);
}
case EWhile(cond,e):
add("while( ");
expr(cond);
add(" ) ");
expr(e);
case EDoWhile(cond,e):
add("do ");
expr(e);
add(" while ( ");
expr(cond);
add(" )");
case EFor(v, it, e):
add("for( "+v+" in ");
expr(it);
add(" ) ");
expr(e);
case EForGen(it, e):
add("for( ");
expr(it);
add(" ) ");
expr(e);
case EBreak:
add("break");
case EContinue:
add("continue");
case EFunction(params, e, name, ret):
add("function");
if( name != null )
add(" " + name);
add("(");
var first = true;
for( a in params ) {
if( first ) first = false else add(", ");
if( a.opt ) add("?");
add(a.name);
addType(a.t);
}
add(")");
addType(ret);
add(" ");
expr(e);
case EReturn(e):
add("return");
if( e != null ) {
add(" ");
expr(e);
}
case EArray(e,index):
expr(e);
add("[");
expr(index);
add("]");
case EArrayDecl(el):
add("[");
var first = true;
for( e in el ) {
if( first ) first = false else add(", ");
expr(e);
}
add("]");
case ENew(cl, args):
add("new " + cl + "(");
var first = true;
for( e in args ) {
if( first ) first = false else add(", ");
expr(e);
}
add(")");
case EThrow(e):
add("throw ");
expr(e);
case ETry(e, v, t, ecatch):
add("try ");
expr(e);
add(" catch( " + v);
addType(t);
add(") ");
expr(ecatch);
case EObject(fl):
if( fl.length == 0 ) {
add("{}");
} else {
tabs += "\t";
add("{\n");
for( f in fl ) {
add(tabs);
add(f.name+" : ");
expr(f.e);
add(",\n");
}
tabs = tabs.substr(1);
add("}");
}
case ETernary(c,e1,e2):
expr(c);
add(" ? ");
expr(e1);
add(" : ");
expr(e2);
case ESwitch(e, cases, def):
add("switch( ");
expr(e);
add(") {");
for( c in cases ) {
add("case ");
var first = true;
for( v in c.values ) {
if( first ) first = false else add(", ");
expr(v);
}
add(": ");
expr(c.expr);
add(";\n");
}
if( def != null ) {
add("default: ");
expr(def);
add(";\n");
}
add("}");
case EMeta(name, args, e):
add("@");
add(name);
if( args != null && args.length > 0 ) {
add("(");
var first = true;
for( a in args ) {
if( first ) first = false else add(", ");
expr(e);
}
add(")");
}
add(" ");
expr(e);
case ECheckType(e, t):
add("(");
expr(e);
add(" : ");
addType(t);
add(")");
case ECast(e,t):
if( t == null ) {
add("cast ");
expr(e);
} else {
add("cast(");
expr(e);
add(",");
addType(t);
add(")");
}
}
}
public static function toString( e : Expr ) {
return new Printer().exprToString(e);
}
public static function errorToString( e : Expr.Error ) {
var message = switch( #if hscriptPos e.e #else e #end ) {
case EInvalidChar(c): "Invalid character: '"+(StringTools.isEof(c) ? "EOF" : String.fromCharCode(c))+"' ("+c+")";
case EUnexpected(s): "Unexpected token: \""+s+"\"";
case EUnterminatedString: "Unterminated string";
case EUnterminatedComment: "Unterminated comment";
case EInvalidPreprocessor(str): "Invalid preprocessor (" + str + ")";
case EUnknownVariable(v): "Unknown variable: "+v;
case EInvalidIterator(v): "Invalid iterator: "+v;
case EInvalidOp(op): "Invalid operator: "+op;
case EInvalidAccess(f): "Invalid access to field " + f;
case ECustom(msg): msg;
};
#if hscriptPos
return e.origin + ":" + e.line + ": " + message;
#else
return message;
#end
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (C)2008-2017 Haxe Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package hscript;
import hscript.Expr;
class Tools {
public static function iter( e : Expr, f : Expr -> Void ) {
switch( expr(e) ) {
case EConst(_), EIdent(_):
case EVar(_, _, e): if( e != null ) f(e);
case EParent(e): f(e);
case EBlock(el): for( e in el ) f(e);
case EField(e, _): f(e);
case EBinop(_, e1, e2): f(e1); f(e2);
case EUnop(_, _, e): f(e);
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 EWhile(c, e): f(c); f(e);
case EDoWhile(c, e): f(c); f(e);
case EFor(_, it, e): f(it); f(e);
case EForGen(it, e): f(it); f(e);
case EBreak,EContinue:
case EFunction(_, e, _, _): f(e);
case EReturn(e): if( e != null ) f(e);
case EArray(e, i): f(e); f(i);
case EArrayDecl(el): for( e in el ) f(e);
case ENew(_,el): for( e in el ) f(e);
case EThrow(e): f(e);
case ETry(e, _, _, c): f(e); f(c);
case EObject(fl): for( fi in fl ) f(fi.e);
case ETernary(c, e1, e2): f(c); f(e1); f(e2);
case ESwitch(e, cases, def):
f(e);
for( c in cases ) {
for( v in c.values ) f(v);
f(c.expr);
}
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);
case ECast(e,_): f(e);
}
}
public static function map( e : Expr, f : Expr -> Expr ) {
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)]);
case EField(e, fi): EField(f(e),fi);
case EBinop(op, e1, e2): EBinop(op, f(e1), f(e2));
case EUnop(op, pre, e): EUnop(op, pre, f(e));
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 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 EForGen(it, e): EForGen(f(it), f(e));
case EFunction(args, e, name, t): EFunction(args, f(e), name, t);
case EReturn(e): EReturn(if( e != null ) f(e) else null);
case EArray(e, i): EArray(f(e),f(i));
case EArrayDecl(el): EArrayDecl([for( e in el ) f(e)]);
case ENew(cl,el): ENew(cl,[for( e in el ) f(e)]);
case EThrow(e): EThrow(f(e));
case ETry(e, v, t, c): ETry(f(e), v, t, f(c));
case EObject(fl): EObject([for( fi in fl ) { name : fi.name, e : f(fi.e) }]);
case ETernary(c, e1, e2): ETernary(f(c), f(e1), f(e2));
case 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);
case ECast(e,t): ECast(f(e),t);
}
return mk(edef, e);
}
public static inline function expr( e : Expr ) : ExprDef {
#if hscriptPos
return e.e;
#else
return e;
#end
}
public static inline function mk( e : ExprDef, p : Expr ) {
#if hscriptPos
return { e : e, pmin : p.pmin, pmax : p.pmax, origin : p.origin, line : p.line };
#else
return e;
#end
}
public static inline function getKeyIterator<T>( e : Expr, callb : String -> String -> Expr -> T ) {
var key = null, value = null, it = e;
switch( expr(it) ) {
case EBinop("in", ekv, eiter):
switch( expr(ekv) ) {
case EBinop("=>",v1,v2):
switch( [expr(v1),expr(v2)] ) {
case [EIdent(v1), EIdent(v2)]:
key = v1;
value = v2;
it = eiter;
default:
}
default:
}
default:
}
return callb(key,value,it);
}
}

20
lib/hscript/release.bat Normal file
View File

@ -0,0 +1,20 @@
@echo off
set PATH="C:\Program Files\7-Zip";%PATH%
rm -rf release
mkdir release
cp haxelib.json README.md extraParams.hxml release
cd release
mkdir hscript
mkdir script
cd ..
cp hscript/*.hx release/hscript
cp script/*.hx* release/script
cd release/script
haxe build.hxml
cd ../..
haxe -xml release/haxedoc.xml hscript.Interp hscript.Parser hscript.Bytes hscript.Macro
7z a -tzip release.zip release
rm -rf release
haxelib submit release.zip
echo Remember to "git tag vX.Y.Z && git push --tags"
pause

View File

@ -0,0 +1,154 @@
package;
import haxe.*;
import hscript.*;
import sys.*;
import sys.io.*;
import sys.net.*;
#if neko
import neko.Lib;
#end
#if cpp
import cpp.Lib;
#end
class RunScript {
public static function main () {
var args = Sys.args ();
var workingDirectory = args.pop();
try {
Sys.setCwd(workingDirectory);
} catch (e:Dynamic) {
error("Failed to set current working directory to [" + workingDirectory + "]");
}
if (args.length == 0) {
error("Argument missing. Expected 1 got 0");
}
if (args[0] == "-f" || args[0] == "--file") {
if (args.length == 1) {
error("Argument missing. Expected 2 got 1");
}
if (args.length > 2) {
error("Too many arguments. Expected 2 got " + args.length);
}
executeScriptFile(args[1]);
}
if (args[0] == "--help") {
printHelp();
}
executeScript(args.join(" "));
}
static function executeScriptFile(scriptPath:String) {
if (!FileSystem.exists(scriptPath)) {
error("Specified file [" + scriptPath + "] not found");
}
if (FileSystem.isDirectory(scriptPath)) {
error("Specified file [" + scriptPath + "] is a directory");
}
var script = File.getContent(scriptPath);
executeScript(script);
}
static function executeScript(script:String) {
var parser = new hscript.Parser();
var program = parser.parseString(script);
var interp = new hscript.Interp();
// export some useful classes
interp.variables.set("Array", Array);
interp.variables.set("DateTools", DateTools);
interp.variables.set("Math", Math);
interp.variables.set("StringTools", StringTools);
interp.variables.set("Sys", Sys);
interp.variables.set("Xml", Xml);
interp.variables.set("sys", {
"FileSystem": sys.FileSystem,
"io": {
"File": sys.io.File
},
"net": {
"Host": sys.net.Host
}
});
interp.variables.set("haxe", {
"Json": haxe.Json,
"Http": haxe.Http,
"Serializer": haxe.Serializer,
"Unserializer": haxe.Unserializer
});
info(interp.execute(program));
}
static function printHelp(exit:Bool = true) {
var hscriptPath = getHaxelibPath("hscript");
var meta:Dynamic = Json.parse(File.getContent(hscriptPath + "haxelib.json"));
info('${meta.name} v${meta.version}');
info('${meta.description}');
info("");
info("usage: haxelib run hscript SCRIPT");
info(" or: haxelib run hscript -f SCRIPTPATH");
info(" or: haxelib run hscript --file SCRIPTPATH");
info("");
info("examples:");
info(" haxelib run hscript var x = 4; 1 + 2 * x");
info(" 9");
info("");
info(" haxelib run hscript \"5 | 6\"");
info(" 7");
info("");
info(" haxelib run hscript for(i in 0...5) Sys.stdout().writeString(i + ', '); 'done'");
info(" 0, 1, 2, 3, 4, done");
info("");
info(" haxelib run hscript sys.net.Host.localhost()");
info(" mycomputer");
info("");
if(Sys.systemName() == "Windows") {
info(" for /f %i in ('haxelib run hscript \"new sys.net.Host('')\"') do @set MY_IP=%i");
info(" echo %MY_IP%");
} else {
info(" MY_IP=$(haxelib run hscript \"new sys.net.Host('')\"')");
info(" echo $MY_IP");
}
info(" 192.168.248.1");
if(exit)
Sys.exit(0);
}
static function info(msg:Dynamic) {
Sys.stdout().writeString("" + msg + "\n");
}
static function error(msg:Dynamic, exit:Bool = true) {
Sys.stderr().writeString("Error: " + msg + "!\n");
if(exit)
Sys.exit(1);
}
static function getHaxelibPath(libraryName:String):String {
var proc = new Process("haxelib", ["path", libraryName]);
var result = "";
var ex:Dynamic = null;
try {
while(true) {
var line = proc.stdout.readLine();
if (line.substr(0, 1) != "-")
result = line;
}
} catch (e:Dynamic) {
ex = e;
};
proc.close();
if (ex) error(ex);
return result;
}
}

View File

@ -0,0 +1,4 @@
-main RunScript
-neko ../run.n
-lib hscript
-dce no