A Dart binding code generator for Hetu script language. You can generate glue codes from Flutter/Dart library, your codes, or any packages. Then you can use them in the Hetu script:
The classes and enums that need to be used in the scripts:
@HTBinding()
enum Ingredients {
Apple,
Banana,
Cinnamon,
}
@HTBinding()
class RootAssetBundle extends services.AssetBundle{
@override
Future<services.ByteData> load(String key) {
return services.rootBundle.load(key);
}
@override
Future<String> loadString(String key, {bool cache = true}) {
return services.rootBundle.loadString(key, cache: cache);
}
@override
Future<T> loadStructuredData<T>(String key, Future<T> Function(String value) parser) {
return services.rootBundle.loadStructuredData(key, parser);
}
}
@HTBinding()
class ScriptHelper {
ScriptHelper._();
static void futureHandler(HT_Function callback, Future future) {
future.then((value) {
try {
callback.call(positionalArgs: [value]);
}
catch(err, stack) {
print('$err\n$stack');
}
});
}
}
The binding codes are generated by this tool:
class IngredientsClassBinding extends HT_ExternNamespace {
@override
dynamic fetch(String id) {
switch (id) {
case 'Apple':
return Ingredients.Apple;
case 'Banana':
return Ingredients.Banana;
case 'Cinnamon':
return Ingredients.Cinnamon;
default:
throw HTErr_Undefined(id);
}
}
}
class RootAssetBundleClassBinding extends HT_ExternNamespace {
@override
dynamic fetch(String id) {
switch (id) {
case 'RootAssetBundle':
return () => RootAssetBundleObjectBinding(RootAssetBundle());
default:
throw HTErr_Undefined(id);
}
}
}
class RootAssetBundleObjectBinding extends HT_ExternObject<RootAssetBundle> {
RootAssetBundleObjectBinding(RootAssetBundle value) : super(value);
@override
final typeid = HT_TypeId('RootAssetBundle');
@override
dynamic fetch(String id) {
switch (id) {
case 'load':
return externObject.load;
case 'loadString':
return externObject.loadString;
case 'loadStructuredData':
return externObject.loadStructuredData;
default:
throw HTErr_Undefined(id);
}
}
}
class ScriptHelperClassBinding extends HT_ExternNamespace {
@override
dynamic fetch(String id) {
switch (id) {
case 'futureHandler':
return (callback, future) => ScriptHelper.futureHandler(callback, future);
default:
throw HTErr_Undefined(id);
}
}
}
And scripts are generated too:
external class Ingredients {
static var Apple
static var Banana
static var Cinnamon
}
external class RootAssetBundle {
construct
fun load
fun loadString
fun loadStructuredData
}
external class ScriptHelper {
static fun futureHandler (callback, future)
}
Then you can use the dart class from the script:
fun handleString(string) {
print(string)
}
fun main() {
print(Ingredients.Banana)
var r = RootAssetBundle()
var strFuture = r.loadString('assets/data/test.txt')
ScriptHelper.futureHandler(handleString, strFuture)
}
You can build the executable file by the script: build.sh
-
Flutter/Dart Framework (-f)
Should only point to the root of the Flutter repository folder (containing 'packages' folder)
-
Third-party packages (-p)
Could point to any packages' lib folder or the root of the repository folder.
You can find the packages from ~/.pub-cache/hosted/pub.dartlang.org/ or from a repository folder.
-
Your own codes (-u)
Point to the folder that containing your codes. You should also add a
@HTBinding()
annotation to your custom class/enum for binding:
@HTBinding()
class CustomClass {
var m;
}
@HTBinding()
enum CustomEnum {
a,
b,
}
More options could be found by flagging -h
:
./bin/ht-binding-generator -h
-u, --user-lib-paths=<path1, path2, ...>
Will iterate over all the folders recursively.
-p, --package-lib-paths=<package1/lib, package2/lib, ...>
Will iterate over all the package cache folders.
-f, --flutter-lib-path=<flutter-framework-path>
Will iterate the Flutter/Dart framework recursively. The path should point to the Flutter root folder.
-o, --output
The output path for .dart code generation and .json intermediate files.
-s, --script-output
The output path for .ht code generation.
-j, --[no-]json-export
Whether to export the intermediate JSON files for diagnostics.
-h, --[no-]help
-i, --ignores=<ignored-file-name, ignored-file-name:ignored-class-name, ...>
The files/classes from this list will be ignored during the code generation.
-w, --whitelist=<whitelist-file-name, whitelist-file-name2, ...>
Only the files from the list will be parsed, working with 'ignores' too.
call loadAutoBinding
and loadAutoBindingScripts
on a HetuScriptBinding()
will initialize all definitions for Hetu script usage.
void initBinding() async {
var hetu = HTAstInterpreter(readFileMethod: (path) {
return rootBundle.loadString(path);
});
var binding = HetuScriptBinding();
binding.loadAutoBinding(hetu);
binding.loadAutoBindingScripts(hetu, 'assets/path').then((value) {
print('initialization done.');
});
}
You can also add manual-binding codes by subclassing HetuScriptBinding
class, and use ManualBinding
instead of HetuScriptBinding
.
The auto-binding codes will be loaded by calling the super
.
class ManualBinding extends HetuScriptBinding {
@override
void loadAutoBinding(HTAstInterpreter interpreter) {
super.loadAutoBinding(interpreter);
var bindings = {
'RootAssetBundle' : RootAssetBundleClassBinding(),
'ScriptHelper' : ScriptHelperClassBinding(),
'ExternalAssetBundle' : ExternalAssetBundleClassBinding(),
};
bindings.forEach((key, value) {
interpreter.bindExternalNamespace(key, value);
});
}
@override
Future loadAutoBindingScripts(HTAstInterpreter interpreter, String path) {
var future = super.loadAutoBindingScripts(interpreter, path);
var futures = <Future>[];
futures.add(future);
futures.add(interpreter.evalf('$path/user/root_asset_bundle.ht'));
futures.add(interpreter.evalf('$path/user/external_asset_bundle.ht'));
return Future.wait(futures);
}
}