Note
This project is inspired by hscript-iris and RuleScript!
Give these projects a look as well!
Tip
This is my first time messing with / writing code this complex so I apologize in advance for any bad code!! (feel free to PR)
This project is also still a heavy work in progress... see the TO-DO for all implemented / missing features!
Experimental fork of Hscript (Parse and evaluate Haxe expressions dynamically).
Simple Script class
Allows you to load code from a string, give it a name, and run it easily!
import insanity.Script;
var script:Script = new Script('
function testFunction(a, b, c)
trace(a + b + c);
trace("hi!!");
', 'MyScript');
script.start();
script.call('testFunction', [1, 2, 3]);You can also edit the variables map in a Script to define custom globals on a script.
By default, this and script are defined as the Script instance, and interp as the instance's interpreter.
Scripted modules and types (with Module and Environment)
Warning
This feature is experimental and still incomplete.
CLASS, TYPEDEF (alias) and ENUM types are supported (ABSTRACTS and MODULE LEVEL FIELDS may be supported later)
You can load custom modules from strings and use them in scripts!
var path:String = 'test/source/TestModule.hxs';
var module:Module = new Module(File.getContent(path), 'TestModule', ['package', 'name'], path);
var environment:Environment = new Environment([module]);
environment.start();
/*
new Module creates a new module script instance...
you can then add it to a new Environment ! (think of it as the source code folder)
start() it to initialize all added modules and make them usable in new Scripts !!
*/
var path:String = 'test/scripts/TestScript.hxs';
var script:Script = new Script(File.getContent(path), path, environment);
script.start();...or you can define module types in a script itself, for example:
class TestClass {
public function new() {
trace('hi!!');
}
}
var instance:TestClass = new TestClass();
/*
do note that classes defined in scripts have certain limitations,
such as (maybe expectedly) not being importable in other scripts !
*/To make a Haxe class extendable for scripting, extend it and implement the insanity.IScripted interface, like the following example:
class BaseThing {
// ...
}
class ScriptedThing extends BaseThing implements insanity.IScripted {}You can also edit the variables map in a Module or Environment to define custom globals on subtypes and submodules, respectively.
By default, module is defined as the Module instance in modules, and interp as the class interpreter in scripted classes.
(NOTE: currently only most behavior is properly implemented from extending classes. while i dont see why implement the interface in the base class, some things might have to be promptly fixed to correctly support them ...)
Global script configs (with Config)
insanity.Config allows you to define custom behaviors, such as...
- Proxying or blacklisting types, modules and packages
- defining preprocessors for conditionals,
- defining variables, and
- defining imports!
These behaviors will be applied globally, to all scripts.
Warning
This feature is very experimental. Use with caution!
Add the @:build(insanity.backend.macro.AbstractMacro.build()) metadata to your abstracts to make them usable in Hscript.
Importing abstracts and abstract features are mostly supported.
Due to technical limitations, you must explicitly cast an expression to the desired type (recommendably, store it in a local variable to modify it with less overhead).
You can also include a type parameter for an implicit cast in variable / method argument declarations.
Enum abstracts are also supported!
import flixel.util.FlxColor;
function colorToString(color:FlxColor)
return '(red: ${color.red} | green: ${color.green} | blue: ${color.blue})';
var color:FlxColor = cast 0xff0040; // or cast(0xff0040, FlxColor)
trace(colorToString(color)); // (red: 255 | green: 0 | blue: 64)
color.green = FlxColor.GREEN.green;
trace(colorToString(color)); // (red: 255 | green: 128 | blue: 64)The import keyword is supported!
You can import classes by module or package path (wildcard), similarly to actual Haxe. Importing a single class or class field is supported, as well as aliases!
All bottom level classes like Reflect, Type and your Main application class should similarly also be exposed by default in scripts.
import sys.*; // sys package wildcard
import Reflect.getProperty as get;
trace(FileSystem.exists('Main.hx'));
trace(get({hi: 123}, 'hi'));You can also import type alias typedefs!
Due to type parameters being mostly stripped at runtime, adding support for importing anonymous structure typedefs is not very practical.
All compile-time type information is accessible in insanity.backend.TypeCollection.main.
The using keyword is supported (to most capacity)!
using Lambda;
var array:Array<Int> = [1, 2, 3, 4, 5];
array = array.map(function(item) {
if (item == 3) return 10;
else return item;
});
trace(array); // [1, 2, 10, 4, 5]Enums can be imported or created in Hscript and support constructors.
Enum matching in switch-case statements is also fully implemented!
// in source code ...
enum TestEnum {
Hi(message:String);
Bye;
}
// in script ...
import TestEnum;
trace(Hi('hello!!'));
trace(Bye);Haxe's string interpolation feature is fully supported!
var test:Int = 1234;
trace('hello $test ${'can also be nested!! $$${test + 3210}'}');Haxe's switch-case pattern matching features are fully supported!
var struct:Dynamic = {name: 'Haxe', rating: 'Awesome'};
trace(switch (struct) {
case {name: a, rating: b}:
'$a is $b';
default:
'no awesome language found';
}); // Haxe is AwesomeHaxe's property accessors can be defined in variables within scripts and scripted classes!
var customSetter(default, set):Dynamic = 123;
function set_customSetter(v:Dynamic):Dynamic {
trace('setting to $v !');
return customSetter = v;
}
customSetter = 456;Haxe's regular expression syntax can now be used in Hscript (instead of just new EReg)!
trace(~/hx/i.replace('HX is Awesome', 'Haxe')); // Haxe is AwesomeScript program exceptions now throw an InterpException, containing more detailed error info more akin to Haxe's exception call stack.
Also imposes a limit for the call stack before a Stack overflow exception (200 by default, can be adjusted with callStackDepth in an Interp instance)
Exception: ouch...
Called from test/TestScript.hxs.crash (test/TestScript.hxs line 2 column 8)
Called from script test/TestScript.hxs (test/TestScript.hxs line 4 column 1)
Called from Main.main (Main.hx line 10 column 3)
Albeit partially supported in the original library (?.) the other null coalescing operators (?? and ??=) are now implemented
also fixes unintended behavior with ident?.method() throwing an error is the ident is null
(these seem to be implemented in the original library too now!)
-
Rest
Rest argument can now be used in functions
-
Optional arguments
Providing a default value for an argument now treats it as optional, regardless of a
?preceding the argument name (which is, presumably, unintended behavior in the original library)A bug where default argument values didn't work as intended in specific conditions is also corrected.
function test(?arg = false, arg2 = false) { trace(arg); trace(arg2); }
Scripts now include the default compilation defines / preprocessor values by default, and you can add custom defines in Config.
Comparisons are now also supported in conditionals!
#if (haxe >= '4.3.7')
// ...
#endA small EOF bug with conditionals has also been fixed.
You can now declare empty maps, inferring from type parameters (in the original library, [] usually just declares an empty array).
var map:Map<String, Dynamic> = [];
trace(Type.typeof(map));
var array = [];
trace(Type.typeof(array));Map comprehension is now also supported, joining array comprehension!
var map:Map<Int, String> = [for (i in 0 ... 5) i => 'number ${i}'];It represents my dwindling mental state as I figure how to modify this library!!
-
abstracts
- static fields
- instance fields
- cast from / to types
- overload operators (
@:op)
-
enum abstracts
- static fields
- constructors
-
enums
- constructors
- constructor arguments
-
typedefs
- type alias import
-
anonymous structure
-
types
- classes
- extends
- Nothing (or scripted class)
- Real types
- fields
- property getters & setters
- accessor error checking in modules
- scripted toString
- iterables and iterators
- property getters & setters
- extends
- enums
- typedefs (type alias only)
- abstracts
- classes
-
general
- fix compile errors in HashLink (for now)
- fix module exceptions (can merge call stack?)
- abstract type fields (currently untested)
importkeyword- module level fields
usingkeyword- explicit type checking?
switchkeyword- complex pattern matching
- capture variables
- extractors
- enum
- array
- struct
- guard conditions
- multiple values (sorta)
- complex pattern matching
Printerclass- fix printed expressions with escape characters
- module declaration to string ?