Repe [T3DU] Update - 31f26d171bba0355ce2a77031e3aad4c64dbc7e9
This commit is contained in:
43
lib/hscript/.github/workflows/main.yml
vendored
Normal file
43
lib/hscript/.github/workflows/main.yml
vendored
Normal 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
2
lib/hscript/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/hscript.swf
|
||||
/release.zip
|
||||
16
lib/hscript/.vscode/launch.json
vendored
Normal file
16
lib/hscript/.vscode/launch.json
vendored
Normal 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
16
lib/hscript/.vscode/tasks.json
vendored
Normal 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
22
lib/hscript/LICENSE
Normal 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
100
lib/hscript/README.md
Normal 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
263
lib/hscript/TestHScript.hx
Normal 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
2
lib/hscript/bin/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
1
lib/hscript/extraParams.hxml
Normal file
1
lib/hscript/extraParams.hxml
Normal file
@ -0,0 +1 @@
|
||||
--macro keep('IntIterator')
|
||||
9
lib/hscript/haxelib.json
Normal file
9
lib/hscript/haxelib.json
Normal 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
2
lib/hscript/hscript.hxml
Normal file
@ -0,0 +1,2 @@
|
||||
bin/build-each.hxml
|
||||
-hl bin/Test.hl
|
||||
536
lib/hscript/hscript/Async.hx
Normal file
536
lib/hscript/hscript/Async.hx
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
518
lib/hscript/hscript/Bytes.hx
Normal file
518
lib/hscript/hscript/Bytes.hx
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
1618
lib/hscript/hscript/Checker.hx
Normal file
1618
lib/hscript/hscript/Checker.hx
Normal file
File diff suppressed because it is too large
Load Diff
180
lib/hscript/hscript/Expr.hx
Normal file
180
lib/hscript/hscript/Expr.hx
Normal 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>;
|
||||
}
|
||||
745
lib/hscript/hscript/Interp.hx
Normal file
745
lib/hscript/hscript/Interp.hx
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
448
lib/hscript/hscript/JsInterp.hx
Normal file
448
lib/hscript/hscript/JsInterp.hx
Normal 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
|
||||
|
||||
}
|
||||
262
lib/hscript/hscript/Macro.hx
Normal file
262
lib/hscript/hscript/Macro.hx
Normal 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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1801
lib/hscript/hscript/Parser.hx
Normal file
1801
lib/hscript/hscript/Parser.hx
Normal file
File diff suppressed because it is too large
Load Diff
374
lib/hscript/hscript/Printer.hx
Normal file
374
lib/hscript/hscript/Printer.hx
Normal 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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
133
lib/hscript/hscript/Tools.hx
Normal file
133
lib/hscript/hscript/Tools.hx
Normal 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
20
lib/hscript/release.bat
Normal 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
|
||||
154
lib/hscript/script/RunScript.hx
Normal file
154
lib/hscript/script/RunScript.hx
Normal 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;
|
||||
}
|
||||
}
|
||||
4
lib/hscript/script/build.hxml
Normal file
4
lib/hscript/script/build.hxml
Normal file
@ -0,0 +1,4 @@
|
||||
-main RunScript
|
||||
-neko ../run.n
|
||||
-lib hscript
|
||||
-dce no
|
||||
Reference in New Issue
Block a user