Skip to content

Library differences

ashley edited this page Aug 9, 2026 · 2 revisions

HscriptInsanity has a really different feature set compared to the original Hscript...
This library's main goal is to implement most of the original Haxe language into scripting!

Should I use HscriptInsanity?

Perchance.

Since this fork introduces a lot of features (seen below), it does have the disadvantage of performing relatively slower.
Speed isn't a huge priority for me right now, but I hope to make performance improvements and optimizations after the main feature set is finished.
(If you think you can help improve this project, feel free to open issues or pull requests, too!)

  • The original Hscript library is better suited for simple prototyping and evaluating small expressions! As such, it should be a bit faster and more lightweight.

  • But if you want Haxe's high range of features on your hands, or to implement heavy modding support in your project, consider giving this library a shot!

What's new?

Simple script execution

You can use the insanity.Script class to easily execute code from a string! Look at the Loading scripts page for a quick rundown.

Scripted types

The ability to define new types in scripts is the largest feature available in this library!
Scripted types can be defined within both Hscript scripts and modules.

Refer to the Defining scripted types page.

Module parsing

You can use the insanity.Module class to execute module code from a string, and create custom modules and types! Look at the Loading scripts page to learn how to implement this, along with a scripted "source" folder similar to the real Haxe language, in your project with HscriptInsanity!

Type definitions are currently limited to what is listed in the Scripted types section above.

Abstracts

Some abstract, and enum abstract features are supported!! See the page Exposing abstracts in Hscript to see what you can or cannot currently do with abstracts in scripts.

You can use cast to cast a type to an abstract.

import flixel.util.FlxColor;

function colorToString(color:FlxColor)
	return '(red: ${color.red} | green: ${color.green} | blue: ${color.blue})';

var color:FlxColor = cast 0xff0040; // you can also use 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)

Imports

The import keyword is supported!
You can import types by module or package path (wildcard), like the actual language. Importing a single type, type field, and module level field is also supported, as well as setting aliases!

All bottom level classes like Reflect, Type and your Main app 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, and module level fields! Due to Haxe stripping type parameters at runtime, although you can also import struct / json typedefs, their structure isn't enforced at all.

All compile-time type information can be retrieved with insanity.backend.TypeCollection.main.

Using (static extension)

The using keyword is supported!

Type checking is not enforced due to aforementioned reasons, and may or may not be implemented to some extent in the future.

using Lambda;

var array:Array<Int> = [1, 2, 3, 4, 5];

array = array.map(function(item:Int) return (item == 3 ? 10 : item));

trace(array); // [1, 2, 10, 4, 5]

Enums

Enums can be imported and created in Hscript, with and without constructors.
You can also match enums in switch cases!

// in the source code ...
enum TestEnum {
	Hi(message:String);
	Bye;
}

// in a script ...
import TestEnum;

trace(Hi('hello!!'));
trace(Bye);

String interpolation

Haxe's string interpolation feature is fully supported!

var test:Int = 1234;

trace('hello $test ${'can also be nested!! $$${test + 3210}'}');

Pattern matching

Haxe's advanced 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 Awesome

Property accessors

Haxe'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;

Field access

You can now access fields from modules and types in scripts without having to import them beforehand!

trace(haxe.io.Bytes.ofString('hello world').getString(0, 5)); // hello

Regular expression syntax

Haxe's regular expression syntax can now be used in Hscript, alternatively to new EReg()!

trace(~/hx/i.replace('HX is Awesome', 'Haxe')); // Haxe is Awesome

Call stack

Program exceptions in Scripts and Modules now throw an InterpException, containing more detailed error info, akin to Haxe's exception call stack.

Also imposes a limit for the call stack before throwing a Stack overflow exception, to prevent the program from freezing indefinitely from recursion. This is 200 by default, but 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)

Function arguments

  • 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);
     }

Conditionals & defines

Scripts now include the default compilation defines / preprocessor values by default, and you can include more custom defines.
Comparisons are now also supported in conditionals, and a small parsing bug with conditionals in the original library has also been fixed!

#if (haxe < '4.3.7')
trace('I\'m old!');
#end

Map declaration

Warning

This only applies to the first level, thus nested maps won't be inferred if empty. (todo ?)

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, to accompany array comprehension!

var map:Map<Int, String> = [for (i in 0 ... 5) i => 'number ${i}'];