haxe 漫游符号模块
WanderingSymbolsModule.hx
package wanderingSymbolsBonus;
import actions.ActionContainer;
import display.EventParams;
import display.interfaces.IGameAnimation;
import display.interfaces.IGameContainer;
import hxevents.Notifier;
import library.Library;
import modular.IModule;
import net.response.Spin;
import pipes.flow.FlowActors;
using actions.ActionTools;
class WanderingSymbolsModule implements IModule {
/* IModule */
public function registerFlowActor():Void {
FlowActors.setActor(Type.getClassName(Type.getClass(this)), this);
}
public var complete:Notifier = new Notifier();
var response:Spin;
public function setSpinResponse(spin:Spin):Void {
response = spin;
}
/* IModule */
var parent:IGameContainer = null;
var root = null;
var libraryName:String;
var posContainers:Array<Array<IGameContainer>> = [];
var SYMBOL_MARGIN(get, null):Int = 100;
public var setSymbolStateAtPositionEnd:Notifier = new Notifier();
public var allMovesSymbolsComplete:Notifier = new Notifier();
public var moveAnimEnd:Notifier = new Notifier();
public static var WANDERING_SYMBOLS_ID:Map<Int, Int> = [
0 => 0,
];
var currentSymbolsPos:Array<Int> = [];
var newSymbolsPos:Array<Int> = [];
public var remainingWanderingSymbols(get, null):Int;
//--------------- show stickyData ------------------
var showStickyData = {
stickyPos:[1, 1, 2, 0],
stickyReels:[1, 0, 0, 4],
stickySymbolIds:[11804, 11804, 11804, 11804]
}
//--------------- show stickyData end ---------------
//--------------- move stickyData -------------------
var moveStickyData = {
stickyPos: [1, 2, 1, 0],
stickyReels: [0, 0, 2, 3],
stickySymbolIds: [11804, 11804, 11804, 11804]
}
var moveFromTo = [
[0, 1, 0, 1],
[0, 2, 0, 2],
[1, 1, 2, 1],
[4, 0, 3, 0]
];
//--------------- move stickyData end ---------------
function get_remainingWanderingSymbols():Int {
return showStickyData.stickySymbolIds.length;
}
function shiftShowStickyData():Void {
showStickyData.stickyPos.shift();
showStickyData.stickyReels.shift();
showStickyData.stickySymbolIds.shift();
}
function shiftMoveStickyData():Void {
moveStickyData.stickyPos.shift();
moveStickyData.stickyReels.shift();
moveStickyData.stickySymbolIds.shift();
moveFromTo.shift();
}
public function new(jsonName:String, parantContainer:IGameContainer) {
libraryName = jsonName;
parent = parantContainer;
createRoot();
init();
registerFlowActor();
}
function get_SYMBOL_MARGIN():Int {
return SYMBOL_MARGIN;
}
function createRoot():Void {
root = cast(Library.create(libraryName, "root"), IGameContainer);
parent.addChild(root);
}
function init():Void {
addSymbolsToPosContainers();
}
function addSymbolsToPosContainers():Void {
for (child in root.children) {
if (StringTools.startsWith(child.name, "pos_")) {
var column:Int = Std.parseInt(child.name.charAt(4));
if (posContainers[column] == null) posContainers[column] = [];
posContainers[column].push(cast (child, IGameContainer));
}
}
for (column in 0...posContainers.length) {
for (row in 0...posContainers[column].length) {
for (symbolId in WANDERING_SYMBOLS_ID.keys()) {
var wandaringSymbolAnim = cast (Library.create(libraryName, "WanderingSymbol_" + WANDERING_SYMBOLS_ID[symbolId]), IGameAnimation);
wandaringSymbolAnim.name = Std.string(symbolId);
posContainers[column][row].addChild(wandaringSymbolAnim);
}
}
}
}
@:flowExposed
public function wanderingSymbolNext(state:String, event:String = ""):Notifier {
var wanderingSymbolNextEnd:Notifier = new Notifier();
var column:Int = showStickyData.stickyReels[0];
var row:Int = showStickyData.stickyPos[0];
var symbolId = showStickyData.stickySymbolIds[0];
var symbol:IGameAnimation = cast posContainers[column][row].getChildByName(Std.string(symbolId % SYMBOL_MARGIN));
symbol.forceState(state);
if (event != "") {
symbol.events[event].addOnce(function(e:EventParams){
wanderingSymbolNextEnd.dispatch();
});
} else {
wanderingSymbolNextEnd.dispatch();
}
shiftShowStickyData();
return wanderingSymbolNextEnd;
}
//function setSymbolStateAtPosition(state:String, column:Int, row:Int, symbol:Int, ?animEndNotifier:Notifier = null):Void {
//var symbol:IGameAnimation = cast posContainers[column][row].getChildByName(Std.string(symbol % SYMBOL_MARGIN));
//symbol.events["anim_end"].addOnce(function(e:EventParams){
//animEndNotifier != null ? animEndNotifier.dispatch() : setSymbolStateAtPositionEnd.dispatch();
//});
//symbol.forceState(state);
//}
//function setStateForAllBonusSymbols(state:String, delayBetweenAnims:Float = 0.0, columns:Array<Int>, rows:Array<Int>, symbols:Array<Int>, ?completeNotifier:Notifier = null):Void {
//var ac:ActionContainer = ActionContainer.get();
//for (i in 0...symbols.length) {
//if (i > 0) ac.addDelay(delayBetweenAnims);
//ac.addFunction(setSymbolStateAtPosition.bind(state, columns[i], rows[i], symbols[i]));
//if (i == symbols.length - 1) ac.addWaitEvent(setSymbolStateAtPositionEnd);
//}
//ac.start(completeNotifier != null ? completeNotifier.dispatch : null);
//}
//@:flowExposed
//public function playAnimToAllSymbols(?state:String, ?delayBetweenAnims:Float = 0.0, ?columns:Array<Int>, ?rows:Array<Int>, ?symbols:Array<Int>, ?completeNotifier:Notifier):INotifier {
//setStateForAllBonusSymbols(state, delayBetweenAnims, columns, rows, symbols, completeNotifier);
//var a:Notifier = new Notifier();
//trace("-----------------------------------------------------------------------------------------------------");
//hide
//init
//show
//shown
//win
//hidden
//TimeOffset.setDelay(0.1, a.dispatch);
//return a;
//}
//public function moveSymbols(allSymbolsMoves:Array<Array<Int>>, symbols:Array<Int>, ?diagonalMotionAllowed:Bool = true):Void {
//var allSymbolsMovesCopy = allSymbolsMoves.map(function(arr:Array<Int>) { return arr.copy(); });
//var symbolsToMove = symbols.copy();
//var symbolMove = allSymbolsMovesCopy[0].copy();
//var symbolId = symbolsToMove[0];
//
//if (!currentAndNewPosAreTheSame(symbolMove)) {
//moveAnimEnd.addOnce(function(){
//setSymbolStateAtPosition("hidden", symbolMove[0], symbolMove[1], symbolId);
//symbolMove = setNewPosBaseOnDirection(getMoveDirection(symbolMove, diagonalMotionAllowed), symbolMove);
//allSymbolsMovesCopy[0] = symbolMove;
//moveSymbols(allSymbolsMovesCopy, symbolsToMove, diagonalMotionAllowed);
//});
//setSymbolStateAtPosition(getMoveDirection(symbolMove, diagonalMotionAllowed), symbolMove[0], symbolMove[1], symbolId, moveAnimEnd);
//} else {
//setSymbolStateAtPositionEnd.addOnce(function(){
//allSymbolsMovesCopy.shift();
//symbolsToMove.shift();
//if (allSymbolsMovesCopy.length > 0) {
//moveSymbols(allSymbolsMovesCopy, symbolsToMove, diagonalMotionAllowed);
//} else {
//allMovesSymbolsComplete.dispatch();
//}
//});
//setSymbolStateAtPosition("shown", symbolMove[0], symbolMove[1], symbolId);
//}
//}
function getMoveDirection(moveFromTo:Array<Int>, diagonalMotionAllowed:Bool = true):String {
var direction:String = "";
var currentColumnPos:Int = moveFromTo[0];
var currentRowPos:Int = moveFromTo[1];
var newColumnPos:Int = moveFromTo[2];
var newRowPos:Int = moveFromTo[3];
if (diagonalMotionAllowed) {
if (currentColumnPos > newColumnPos && currentRowPos == newRowPos) direction = "go_w";
else if (currentColumnPos < newColumnPos && currentRowPos == newRowPos) direction = "go_e";
else if (currentRowPos > newRowPos && currentColumnPos == newColumnPos) direction = "go_n";
else if (currentRowPos < newRowPos && currentColumnPos == newColumnPos) direction = "go_s";
else if (currentColumnPos > newColumnPos && currentRowPos > newRowPos) direction = "go_nw";
else if (currentColumnPos < newColumnPos && currentRowPos > newRowPos) direction = "go_ne";
else if (currentColumnPos > newColumnPos && currentRowPos < newRowPos) direction = "go_sw";
else if (currentColumnPos < newColumnPos && currentRowPos < newRowPos) direction = "go_se";
} else {
if (currentColumnPos > newColumnPos) direction = "go_w";
else if (currentColumnPos < newColumnPos) direction = "go_e";
else if (currentRowPos > newRowPos) direction = "go_n";
else if (currentRowPos < newRowPos) direction = "go_s";
}
return direction;
}
function currentAndNewPosAreTheSame(fromToPos:Array<Int>):Bool {
return fromToPos[0] == fromToPos[2] && fromToPos[1] == fromToPos[3];
}
function setNewPosBaseOnDirection(direction:String, fromToPos:Array<Int>):Array<Int> {
switch (direction) {
case "go_w":
fromToPos[0] -= 1;
case "go_e":
fromToPos[0] += 1;
case "go_n":
fromToPos[1] -= 1;
case "go_s":
fromToPos[1] += 1;
case "go_nw":
fromToPos[0] -= 1;
fromToPos[1] -= 1;
case "go_ne":
fromToPos[0] += 1;
fromToPos[1] -= 1;
case "go_sw":
fromToPos[0] -= 1;
fromToPos[1] += 1;
case "go_se":
fromToPos[0] += 1;
fromToPos[1] += 1;
}
return fromToPos;
}
// N
// |
// NW ) | ( NE
// ) | (
// ) | (
// W--)--------O--------(--E
// ) | (
// ) | (
// SW ) | ( SE
// )|(
// |
// S
}
haxe Haxe-OpenFL-Flixel字体嵌入
font embedding
// In project XML add the following line
<assets path="assets/fonts" rename="fonts" if="html5" />
// CREATE Font.hx Class
package;
import openfl.text.Font;
import openfl.Assets;
class Fonts {
public static var RALEWAY_EXTRALIGHT(default, null):String;
public static function init():Void {
#if js
RALEWAY_EXTRALIGHT = Assets.getFont("fonts/raleway-extralight.ttf").fontName;
#else
Font.registerFont(RalewayExtraLight);
RALEWAY_EXTRALIGHT = (new RalewayExtraLight()).fontName;
#end
}
}
@:font("assets/fonts/raleway-extralight.ttf")
private class RalewayExtraLight extends Font {}
// In Main.hx use it like so
package;
import openfl.display.Sprite;
import openfl.Lib;
import openfl.text.TextField;
import openfl.text.TextFormat;
class Main extends Sprite {
public function new() {
super();
Fonts.init();
var tf = new TextFormat(Fonts.RALEWAY_EXTRALIGHT, 32);
var t = new TextField();
t.embedFonts = true;
t.defaultTextFormat = tf;
t.text = Fonts.RALEWAY_EXTRALIGHT;
t.width = t.textWidth;
this.addChild(t);
}
}
haxe JSGenerator.hx
JSGenerator.hx
package amd;
#if macro
import haxe.macro.Type;
import haxe.macro.Expr;
import haxe.macro.*;
import haxe.ds.*;
using Lambda;
using StringTools;
using amd.JsGenerator.StringExtender;
class StringExtender {
static public function asJSFieldAccess(s:String, api:JSGenApi) {
return api.isKeyword(s) ? '["' + s + '"]' : "." + s;
}
static public function asJSPropertyAccess(s:String, api:JSGenApi) {
return api.isKeyword(s) ? '"' + s + '"' : s;
}
static public function indent(s:String, level:Int) {
var iStr = '';
while (level > 0) {
iStr += '\t';
level -= 1;
}
return s.replace('\n', '\n$iStr');
}
static public function dedent(s:String, level:Int) {
var iStr = '';
while (level > 0) {
iStr += '\t';
level -= 1;
}
return s.replace('\n$iStr', '\n');
}
}
enum Forbidden {
prototype;
__proto__;
constructor;
}
enum StdTypes {
Int;
Float;
Bool;
Class;
Enum;
Dynamic;
}
enum JSTypes {
Array;
String;
Object;
Date;
XMLHttpRequest;
Math;
}
interface IField {
var name:String;
var code:String;
var path:String;
var isStatic:Bool;
var dependencies:StringMap<String>;
public function getCode():String;
}
interface IKlass {
var name:String;
var code:String;
var superClass:String;
var interfaces:Array<String>;
var dependencies:StringMap<String>;
public function getCode():String;
var members: StringMap<IField>;
}
interface IPackage {
var name:String;
var code:String;
var members: StringMap<IKlass>;
}
class Module {
public var name:String = "";
public var path:String = "";
public var dependencies: StringMap<String> = new StringMap<String>();
public var code: String = "";
public var isStatic:Bool = false;
public var gen:JsGenerator;
public function new(_gen) {
gen = _gen;
}
function addDependency(dep:String) {
gen.addDependency(dep, this);
}
inline function _print(b:StringBuf, str:String){
b.add(str);
}
inline function _newline(b:StringBuf) {
b.add(";\n");
}
function formatFieldName(name:String):String {
return gen.api.isKeyword(name) ? '["' + name + '"]' : "." + name;
}
public var fieldName(get, never):String;
function get_fieldName() {
return name.asJSFieldAccess(gen.api);
}
}
class Field extends Module implements IField {
public var fieldAccessName:String;
public var propertyAccessName:String;
public function getCode() {
return code;
}
public function build(f: ClassField, classPath:String) {
name = f.name;
path = '$classPath.$name';
fieldAccessName = name.asJSFieldAccess(gen.api);
propertyAccessName = name.asJSPropertyAccess(gen.api);
var e = f.expr();
if( e == null ) {
code = 'null';
} else {
code = gen.api.generateValue(e);
}
for (dep in gen.getDependencies().keys()) {
addDependency(dep);
}
}
}
class Klass extends Module implements IKlass {
public var members: StringMap<IField> = new StringMap();
public var init: TypedExpr;
public var superClass:String = null;
public var interfaces:Array<String> = new Array();
public var properties:Array<String> = new Array();
public function getCode() {
var t = new haxe.Template('
// Class: ::path::
::if (dependencies.length > 0)::
// Dependencies:
::foreach dependencies::
// ::__current__::
::end::
::end::
::if (overrideBase)::::if (useHxClasses)::$$hxClasses["::path::"] = ::className::::end::
::else::var ::className:: = ::if (useHxClasses == true)::$$hxClasses["::path::"] = ::end::::code::;
::if (interfaces != "")::::className::.__interfaces__ = [::interfaces::];
::end::::if (superClass != null)::::className::.__super__ = ::superClass::;
::className::.prototype = $$extend(::superClass::.prototype, {
::else::::className::.prototype = {
::end::::if (propertyString != ""):: "__properties__": {::propertyString::},
::end::::foreach members:: ::propertyAccessName::: ::code::,
::end:: __class__: ::className::
}::if (superClass != null)::)::end::;
::className::.__name__ = "::path::";::end::
::foreach statics::::className::::fieldAccessName:: = ::code::;
::end::::if (init != "")::// Initialization Code
::init::::end::
');
function filterMember(member:IField) {
var f = new Field(gen);
f.name = member.name;
f.fieldAccessName = f.name.asJSFieldAccess(gen.api);
f.propertyAccessName = f.name.asJSPropertyAccess(gen.api);
f.isStatic = member.isStatic;
f.code = member.getCode();
if (!f.isStatic) {
f.code = f.code.indent(1);
}
return f;
}
var initCode = "";
if (init != null) {
initCode = gen.api.generateStatement(init);
initCode = initCode.substring(1, initCode.length - 1).trim().dedent(1);
}
var data = {
overrideBase: Reflect.hasField(JSTypes, name),
className: name,
path: path,
code: code,
useHxClasses: gen.hasFeature('Type.resolveClass') || gen.hasFeature('Type.resolveEnum'),
init: initCode,
dependencies: [for (key in dependencies.keys()) key],
interfaces: interfaces.join(','),
superClass: superClass,
propertyString: [for (prop in properties) '"$prop":"$prop"'].join(','),
members: [for (member in members.iterator()) filterMember(member)].filter(function(m) { return !m.isStatic; }),
statics: [for (member in members.iterator()) filterMember(member)].filter(function(m) { return m.isStatic; })
};
return t.execute(data);
}
public function addField(c: ClassType, f: ClassField) {
gen.checkFieldName(c, f);
gen.setContext(path + '.' + f.name);
if(f.name.indexOf("get_") == 0 || f.name.indexOf("set_") == 0)
{
properties.push(f.name);
}
switch( f.kind )
{
case FVar(r, _):
if( r == AccResolve ) return;
default:
}
var field = new Field(gen);
field.build(f, path);
for (dep in field.dependencies.keys()) {
addDependency(dep);
}
members.set(f.name, field);
}
public function addStaticField(c: ClassType, f: ClassField) {
gen.checkFieldName(c, f);
gen.setContext(path + '.' + f.name);
var field = new Field(gen);
field.build(f, path);
field.isStatic = true;
for (dep in field.dependencies.keys()) {
addDependency(dep);
}
members.set(field.name, field);
}
public function build(c: ClassType) {
name = c.name;
path = gen.getPath(c);
gen.setContext(path);
init = c.init;
if( c.constructor != null ) {
code = gen.api.generateStatement(c.constructor.get().expr());
} else {
code = "function() {}";
}
// Add Haxe type metadata
if( c.interfaces.length > 0 ) {
interfaces = [for (i in c.interfaces) gen.getTypeFromPath(gen.getPath(i.t.get()))];
}
if( c.superClass != null ) {
gen.hasClassInheritance = true;
superClass = gen.getTypeFromPath(gen.getPath(c.superClass.t.get()));
}
for (dep in gen.getDependencies().keys()) {
addDependency(dep);
}
if (!c.isExtern) {
for( f in c.fields.get() ) {
addField(c, f);
}
for( f in c.statics.get() ) {
addStaticField(c, f);
}
}
}
}
class EnumModuleField extends Module implements IField {
var isFunction:Bool;
var index:Int;
var enumName:String;
var argNames:String;
public var fieldAccessName:String;
public function getCode() {
var t = new haxe.Template('function(::argNames::) { var $$x = [::quoteName::,::index::::if (isFunction)::,::argNames::::end::]; $$x.__enum__ = ::enumName::; return $$x; }::if (!isFunction)::()::end::');
var data = {
argNames: argNames,
index: index,
isFunction: isFunction,
enumName: enumName,
quoteName: gen.api.quoteString(name)
};
return t.execute(data);
}
public function build(e: EnumField, classPath:String) {
name = e.name;
fieldAccessName = name.asJSFieldAccess(gen.api);
enumName = classPath;
path = '$classPath.$name';
index = e.index;
switch( e.type )
{
case TFun(args, _):
argNames = args.map(function(a) return a.name).join(",");
isFunction = true;
default:
isFunction = false;
argNames = "";
}
}
}
class EnumModule extends Module implements IKlass {
var names:String;
var constructs:String;
public var superClass:String;
public var interfaces:Array<String> = [];
public var members:StringMap<IField> = new StringMap();
public function getCode() {
var t = new haxe.Template('
// Enum: ::path::
::if (dependencies.length > 0)::
// Dependencies:
::foreach dependencies::
// ::__current__::
::end::
::end::
var ::enumName:: = { __ename__ : [::names::], __constructs__ : [::constructs::] };
::if (code != "")::::enumName::.__meta__ = ::code::;::end::
::foreach members::::enumName::::fieldAccessName:: = ::code::;
::end::
');
function filterMember(member:IField) {
var f = new EnumModuleField(gen);
f.name = member.name;
f.fieldAccessName = f.name.asJSFieldAccess(gen.api);
f.code = member.getCode();
return f;
}
var data = {
enumName: name,
code: code,
path: path,
dependencies: [for (key in dependencies.keys()) key],
names: names,
constructs: constructs,
members: [for (member in members.iterator()) filterMember(member)]
};
return t.execute(data);
}
public function addField(e: EnumType, construct: EnumField) {
gen.checkFieldName(e, construct);
gen.setContext(path + '.' + e.name);
var field = new EnumModuleField(gen);
field.build(construct, path);
members.set(construct.name, field);
}
public function build(e: EnumType) {
name = e.name;
path = gen.getPath(e);
gen.setContext(path);
names = path.split(".").map(gen.api.quoteString).join(",");
constructs = e.names.map(gen.api.quoteString).join(",");
for( c in e.constructs.keys() ) {
addField(e, e.constructs.get(c));
}
var meta = gen.api.buildMetaData(e);
if( meta != null ) {
code = gen.api.generateStatement(meta);
}
}
}
class Package extends Module implements IPackage {
public var isMain:Bool = false;
public var members: StringMap<IKlass> = new StringMap();
public function isEmpty():Bool {
return !members.keys().hasNext() && code == "";
}
public function collectDependencies() {
function hasDependency(key) {
return ! dependencies.exists(key);
}
for( member in members ) {
for( dep in [for (key in member.dependencies.keys()) key] ) {
gen.addDependency(dep, this);
member.dependencies.remove(dep);
}
}
}
public function getCode() {
var pre = new haxe.Template('// Package: ::packageName::
define([::dependencyNames::],
function (::dependencyVars::) {
');
// Collect the package's dependencies into one array
var allDeps = new StringMap();
var memberValues = [for (member in members.iterator()) member];
var depKeys = [for (k in dependencies.keys()) k];
function formatMember(m: IKlass) {
var name = m.name;
var access = m.name.asJSPropertyAccess(gen.api);
return '$access: $name';
}
var data = {
packageName: name.replace('.', '_'),
path: path,
dependencyNames: [for (k in depKeys) gen.api.quoteString(k.replace('.', '_'))].join(', '),
dependencyVars: [for (k in depKeys) k.replace('.', '_')].join(', '),
members: [for (member in memberValues) formatMember(member)].join(',\n\t\t'),
singleMember: ""
};
code = pre.execute(data);
for (member in members) {
code += member.getCode().indent(1);
}
var post:haxe.Template;
if (memberValues.length == 1) {
data.singleMember = memberValues[0].name;
post = new haxe.Template('return ::singleMember::;
});
');
} else {
post = new haxe.Template('return {
::members::
};
});
');
}
code += post.execute(data);
return code;
}
}
class MainPackage extends Package {
public override function getCode() {
var pre = new haxe.Template('// Package: ::packageName::
require([::dependencyNames::],
function (::dependencyVars::) {
');
// Collect the package's dependencies into one array
var allDeps = new StringMap();
var depKeys = [for (k in dependencies.keys()) k];
var data = {
packageName: name.replace('.', '_'),
path: path,
dependencyNames: depKeys.map(gen.api.quoteString).join(', '),
dependencyVars: [for (k in depKeys) k.replace('.', '_')].join(', '),
};
var _code = pre.execute(data);
_code += '\t$code';
var post = new haxe.Template('
});
');
_code += post.execute(data);
return _code;
}
}
class JsGenerator
{
public var api : JSGenApi;
var packages : StringMap<Package>;
var forbidden : StringMap<Bool>;
var baseJSModules : haxe.ds.StringMap<Bool>;
public var currentContext: Array<String>;
var dependencies: StringMap<String> = new StringMap();
var assumedFeatures: StringMap<Bool> = new StringMap();
var curBuf : StringBuf;
var mainBuf : StringBuf;
var external : Bool;
public var hasClassInheritance: Bool = false;
public function new(api)
{
this.api = api;
mainBuf = new StringBuf();
curBuf = mainBuf;
currentContext = [];
packages = new StringMap<Package>();
forbidden = new StringMap();
external = false;
api.setTypeAccessor(getType);
}
public function hasFeature(name:String):Bool {
var d = Context.definedValue(name);
if (d != null) {
return ["false", "no", ""].indexOf(d.toLowerCase()) == -1;
}
#if (haxe_ver >= 3.2)
return api.hasFeature(name);
#else
if (!assumedFeatures.exists(name)) {
Context.warning('Assuming feature "$name" is true until 3.2 is released.', Context.currentPos());
assumedFeatures.set(name, true);
}
return true;
#end
}
public function addDependency(dep:String, ?container:Module) {
var name = dep;
if (container == null) {
dependencies.set(dep, name);
} else if (! Reflect.hasField(JSTypes, dep)) {
if (dep != container.path) {
container.dependencies.set(name, name);
} else {
}
} else {
return "";
}
return name;
}
function getType( t : Type )
{
var origName = switch(t)
{
case TInst(c, _):
getPath(c.get());
case TEnum(e, _):
getPath(e.get());
case TAbstract(c, _):
c.get().name;
default: throw "assert: " + t;
};
return getTypeFromPath(origName);
}
public function getTypeFromPath(origName: String) {
if (Reflect.hasField(StdTypes, origName)) {
addDependency("Std");
} else {
addDependency(origName);
}
if (Reflect.hasField(JSTypes, origName)) {
return origName;
} else {
return '/* "$origName" */';
}
}
inline function print(str){
curBuf.add(str);
}
public function getPath( t : BaseType ) {
return (t.pack.length == 0) ? t.name : t.pack.join(".") + "." + t.name;
}
public function checkFieldName( c : {pos:Position}, f : {name:String} ) {
if( forbidden.exists(f.name) )
Context.error("The field " + f.name + " is not allowed in JS", c.pos);
}
public function setContext(ctxt:String) {
currentContext = [ctxt];
dependencies = new StringMap<String>();
}
public function getDependencies() {
var depCopy = new StringMap<String>();
for (key in dependencies.keys()) {
depCopy.set(key, dependencies.get(key));
}
dependencies = new StringMap<String>();
return depCopy;
}
function traverseClass( c : ClassType )
{
var pack = new Package(this);
var kls = new Klass(this);
api.setCurrentClass(c);
kls.build(c);
pack.path = getPath(c);
pack.name = pack.path;
if (pack.name == "") {
pack.name = "core";
}
packages.set(pack.path, pack);
pack.members.set(c.name, kls);
}
function traverseEnum( e : EnumType )
{
var kls = new EnumModule(this);
kls.build(e);
var pack = new Package(this);
pack.path = getPath(e);
pack.name = pack.path;
if (pack.name == "") {
pack.name = "core";
}
packages.set(pack.path, pack);
pack.members.set(kls.name, kls);
}
function traverseType( t : Type )
{
switch( t )
{
case TInst(c, _):
var c = c.get();
if( !c.isExtern || ["Math", "Number"].indexOf(c.name) != -1) {
traverseClass(c);
} else {
var path = getPath(c);
// Context.warning('Skipping over Extern Class: $path', Context.currentPos());
}
case TEnum(r, _):
var e = r.get();
if( !e.isExtern ) {
traverseEnum(e);
} else {
var path = getPath(e);
// Context.warning('Skipping over Extern Enum: $path', Context.currentPos());
}
// case TAbstract(a, _):
// var name = a.get().name;
// Context.warning('Skipping over Abstract: $name', Context.currentPos());
// case TType(tt, _):
// var name = tt.get().name;
// Context.warning('Skipping over Type: $name', Context.currentPos());
default:
// Context.error('' + t, Context.currentPos());
}
}
function purgeEmptyPackages() {
// Dispose of Empty Packages
var emptyPackages = [for (k in packages.keys()) k].filter(function(pName) { return packages.get(pName).isEmpty(); });
if (emptyPackages.length > 0) {
Context.warning('' + emptyPackages + ' are all empty packages.', Context.currentPos());
for (name in emptyPackages) {
packages.remove(name);
}
}
}
function cleanPackageDependencies(message="") {
// Remove dependencies to non-existent packages
var packageNames = [for (pack in packages) pack.path];
for (pack in packages) {
for (dep in pack.dependencies.keys()) {
if (packageNames.indexOf(dep) == -1) {
Context.warning('Removing dependency "$dep" from "${pack.name}". $message', Context.currentPos());
pack.dependencies.remove(dep);
}
}
}
}
function checkForCyclicPackageDependencies():Array<String> {
// Check packages for cyclic dependencies
for( pack in packages.iterator() ) {
var alreadyChecked = [pack.path];
var depQueue = [for (dep in pack.dependencies.keys()) {path: dep, depPath: [pack.path]} ];
while (depQueue.length > 0) {
var dep = depQueue.shift();
if (dep.path == pack.path) {
Context.warning('${pack.name} is cyclically dependent along: ' + dep.depPath.join(' -> '), Context.currentPos());
return dep.depPath;
}
if (alreadyChecked.indexOf(dep.path) != -1) {
continue;
}
if (packages.exists(dep.path)) {
var depPack = packages.get(dep.path);
for (packDepKey in depPack.dependencies.keys()) {
var queueStruct = {path: packDepKey, depPath: dep.depPath.concat([dep.path])}
if (depQueue.indexOf(queueStruct) == -1) {
depQueue.push(queueStruct);
}
}
} else {
Context.error('\tDepends on unknown module "$dep"', Context.currentPos());
}
alreadyChecked.push(dep.path);
}
}
return [];
}
function joinPackages(a:Package, b:Package):Package {
Context.warning('Joining packages ${a.path} and ${b.path}', Context.currentPos());
for (member in a.members.keys()) {
if (b.members.exists(member)) {
Context.error('Cannot join packages ${a.path} and ${b.path} because they both have a member named $member.', Context.currentPos());
}
b.members.set(member, a.members.get(member));
}
b.code += '\n' + a.code;
b.collectDependencies();
return b;
}
function joinCyclicPackages() {
var cyclicPackages = [for (packName in checkForCyclicPackageDependencies()) packages.get(packName)];
while(cyclicPackages.length != 0) {
var finalPackage = cyclicPackages.slice(1).fold(joinPackages, cyclicPackages[0]);
cyclicPackages = cyclicPackages.slice(1);
for (pack in cyclicPackages) {
packages.set(pack.path, finalPackage);
finalPackage.dependencies.remove(pack.path);
}
cyclicPackages = [for (packName in checkForCyclicPackageDependencies()) packages.get(packName)];
}
}
function replaceType(f:EReg):String {
var m = f.matched(1);
var pack = packages.get(currentContext[0]);
var memberName = m.substring(m.lastIndexOf('.') + 1);
if (pack.members.exists(m)) {
return m; // '(1) -> $m';
} else if (pack.members.exists(memberName)) {
return memberName; // '(2) -> $memberName';
} else if (pack.dependencies.exists(m)) {
var depPack = packages.get(m);
if (!depPack.members.exists(memberName)) {
Context.error('${pack.path} depends on $memberName from package ${depPack.path}, ${depPack.path} contains no member by that name.', Context.currentPos());
}
if (depPack.name != m) {
// When packages are joined, the dependency name doesn't get updated so we do that here.
pack.dependencies.set(depPack.name, pack.dependencies.get(m));
}
if (depPack.members.list().length == 1) {
var depName = m.replace('.', '_');
return depName; // '(3) -> $depName';
} else {
var depName = depPack.name.replace('.', '_');
return '$depName.$memberName'; // (4) -> $depName.$memberName';
}
} else {
Context.warning('Assuming "$m" is available in "${pack.name}" scope.', Context.currentPos());
return m; // '(5) -> $m';
}
}
function replaceTypeComments(pack:Package) {
var typeFinder = new EReg('\\/\\* "([A-Za-z.]+)" \\*\\/', 'g');
currentContext = [pack.path];
pack.code = typeFinder.map(pack.code, replaceType);
for (klsKey in pack.members.keys() ) {
var kls = pack.members.get(klsKey);
currentContext = [pack.path, klsKey];
for (field in kls.members.iterator()) {
field.code = typeFinder.map(field.code, replaceType);
}
kls.code = typeFinder.map(kls.code, replaceType);
if (kls.superClass != null) {
kls.superClass = typeFinder.map(kls.superClass, replaceType);
}
kls.interfaces = [for (iface in kls.interfaces) typeFinder.map(iface, replaceType)];
}
}
public function generate()
{
// Parse types and build packages
api.types.map(traverseType);
// Run through each package, making sure that it has collected the dependencies of it's members.
for (pack in packages) { pack.collectDependencies(); }
purgeEmptyPackages();
cleanPackageDependencies("Assuming a global dependency.");
joinCyclicPackages();
// Special case, merge Math into Std
if (packages.exists('Math') && packages.exists('Std')) {
var stdPackage = joinPackages(packages.get('Math'), packages.get('Std'));
packages.set('Math', stdPackage);
packages.set('Std', stdPackage);
}
// Replace type comments
for( pack in packages.iterator() ) {
replaceTypeComments(pack);
}
var mainPack:MainPackage;
if(api.main != null) {
setContext("main");
mainPack = new MainPackage(this);
mainPack.name = 'main';
mainPack.path = 'main';
mainPack.code = api.generateStatement(api.main);
for (dep in getDependencies().keys()) {
addDependency(dep, mainPack);
}
packages.set('main', mainPack);
replaceTypeComments(mainPack);
}
cleanPackageDependencies("It has been superceded by another dependency.");
print("window.$hxClasses = {};");
print('if (!window.require) alert("You must include an AMD loader such as RequireJS.");\n');
if (hasFeature("may_print_enum")) {
print("$estr = function() { return js.Boot.__string_rec(this,''); };\n");
}
if (hasClassInheritance) {
print("function $extend(from, fields) {
function Inherit() {} Inherit.prototype = from; var proto = new Inherit();
for (var name in fields) proto[name] = fields[name];
if( fields.toString !== Object.prototype.toString ) proto.toString = fields.toString;
return proto;
};\n");
}
// Loop through the created packages.
for( pack in packages.iterator() ) {
curBuf = new StringBuf();
var filename = pack.name.replace('.', '_');
print(pack.getCode());
// Put it all in a file.
var filePath = api.outputFile.substring(0, api.outputFile.lastIndexOf("/"));
filePath += '/$filename.js';
sys.io.File.saveContent(filePath, curBuf.toString());
}
print("\n");
curBuf = mainBuf;
if( api.main != null ) {
print(mainPack.getCode());
}
sys.io.File.saveContent(api.outputFile, mainBuf.toString());
}
#if macro
public static function use()
{
Compiler.setCustomJSGenerator(function(api) new JsGenerator(api).generate());
}
#end
}
#end
haxe RegTest.hx
haxe_output.js
(function () { "use strict";
var RegTest = function() {
this.ram = new Uint8Array(65536);
};
RegTest.main = function() {
var foo = new RegTest();
foo.whatIsR0();
};
RegTest.prototype = {
whatIsR0: function() {
console.log("r0 is " + this.ram[123]);
}
,get_r0: function() {
return this.ram[123];
}
,set_r0: function(value) {
return this.ram[123] = value;
}
};
RegTest.main();
})();
RegisterMacro.hx
package ;
import haxe.macro.Context;
import haxe.macro.Expr;
class RegisterMacro
{
macro static public function memoryMappedRegister(fieldName:String,index :Int):Array<Field> {
var fields = Context.getBuildFields();
var getterName = "get_" + fieldName;
var setterName = "set_" + fieldName;
var propertyFromMacro = macro : {
var $fieldName(get, set) : Int;
inline function $getterName() : Int {
return ram[ $v{index} ];
}
inline function $setterName(value:Int) : Int {
return ram[$v{index}]=value;
}
};
switch (propertyFromMacro) {
case TAnonymous(getterFields):
fields=fields.concat(getterFields);
default:
throw 'unreachable';
}
return fields;
}
}
RegTest.hx
import js.html.Uint8Array;
@:build( RegisterMacro.memoryMappedRegister("r0",123) )
class RegTest
{
var ram : Uint8Array;
public function new() {
ram = new Uint8Array(65536);
}
public function whatIsR0() {
trace("r0 is "+r0);
}
static function main() {
var foo = new RegTest();
foo.whatIsR0();
}
}
haxe Main.hx
generated.js
(function () { "use strict";
var Main = function() { };
Main.main = function() {
var tower = { a : 0, b : "", c : 0, d : false, e : [1,2,3], f : { g : []}};
console.log(tower);
};
Main.main();
})();
StructHelper.hx
#if macro
import haxe.macro.Expr;
import haxe.macro.Context;
import haxe.macro.Type;
using haxe.macro.Tools;
#end
@:dce
class StructHelper {
public static macro function make(typeExpr:Expr, fieldExprs:Array<Expr>):Expr {
var type = try
Context.getType(typeExpr.toString())
catch (e:Dynamic)
throw new Error(e.toString(), typeExpr.pos);
switch (type.follow()) {
case TAnonymous(_.get() => anon):
var fieldInits = new Map();
for (e in fieldExprs) {
switch (e) {
case macro $i{fieldName} = $value:
fieldInits[fieldName] = {expr: value.expr, pos: e.pos};
default:
throw new Error("Invalid field initialization expression: should be fieldName = value", e.pos);
}
}
var pos = Context.currentPos();
var objectDecl = getAnonDefaultValueExpr(anon, pos, fieldInits);
var objectType = type.toComplexType();
return macro @:pos(pos) ($objectDecl : $objectType);
default:
throw new Error("Make should only be used on structure types", typeExpr.pos);
}
}
#if macro
static function getDefaultValueExpr(type:Type, pos:Position):Expr {
var e = switch (type.follow()) {
case TAbstract(_.get() => {pack: [], name: "Int" | "Float"}, []):
macro 0;
case TAbstract(_.get() => {pack: [], name: "Bool"}, []):
macro false;
case TInst(_.get() => {pack: [], name: "Array"}, [_]):
macro [];
case TInst(_.get() => {pack: [], name: "String"}, []):
macro "";
case TAnonymous(_.get() => anon):
getAnonDefaultValueExpr(anon, pos);
default:
throw new Error("Unsupported type for generating default value: " + type.toString(), pos);
}
e.pos = pos;
return e;
}
static function getAnonDefaultValueExpr(anon:AnonType, pos:Position, ?fieldInits:Map<String,Expr>):Expr {
var declFields = [];
for (field in anon.fields) {
var fieldValue = null;
if (fieldInits != null) {
fieldValue = fieldInits[field.name];
fieldInits.remove(field.name);
}
if (fieldValue == null) {
if (field.meta.has(":optional"))
continue;
fieldValue = getDefaultValueExpr(field.type, pos);
}
declFields.push({field: field.name, expr: fieldValue});
}
if (fieldInits != null) {
for (fieldName in fieldInits.keys())
throw new Error("Unknown field: " + fieldName, fieldInits[fieldName].pos);
}
return {expr: EObjectDecl(declFields), pos: pos};
}
#end
}
Main.hx
import StructHelper.make;
typedef A = {
a:Int,
b:String,
c:Float,
d:Bool,
e:Array<Int>,
f:{
g:Array<Bool>,
?h:String,
},
?i:Int,
}
class Main {
static function main() {
var tower = make(A, e = [1, 2, 3]);
trace(tower);
}
}
haxe gistfile1.hx
gistfile1.hx
package ;
import neko.Lib;
import sys.db.Manager;
import sys.db.Object;
import sys.db.Sqlite;
import sys.db.Types.SDate;
import sys.db.Types.SId;
import sys.db.Types.SNull;
import sys.db.Types.SString;
import sys.db.Types.SText;
/**
* ...
* @author
*/
// SPOD example - old.haxe.org/manual/spod
// Neko/SQLite
class Main
{
static function main()
{
// Create the file my-database.sqlite if it doesn't exist and open a SQLite connection to it
var cnx = Sqlite.open('my-database.sqlite');
// Initialize the sys.db.Manager that handles the SPOD stuff behind the scenes
Manager.cnx = cnx;
// Create the User table in my-database.sqlite if it doesn't exist
if ( !sys.db.TableCreate.exists(User.manager) ) sys.db.TableCreate.create(User.manager);
// create a new user
var u = new User();
// set some parameters
u.name = "John Doe";
u.birthday = Date.now();
// store it in the db
u.insert();
// fetch user with id # 1
var u = User.manager.get(1);
if ( u == null ) throw "User #1 not found";
// trace it
trace(u.name);
}
}
// User SPOD objcet that extends sys.db.Object
class User extends Object
{
public var id : SId; // SPOD type for record Id
public var name : SString<32>; // SPOD type for strings
public var birthday : SDate; // SPOD type for date
public var phoneNumber : SNull<SText>; // SPOD tye for nullable variable-lenght text field
}
haxe gistfile1.hx
gistfile1.hx
package ;
import neko.Lib;
import sys.db.Manager;
import sys.db.Object;
import sys.db.Sqlite;
import sys.db.Types.SDate;
import sys.db.Types.SId;
import sys.db.Types.SNull;
import sys.db.Types.SString;
import sys.db.Types.SText;
/**
* ...
* @author
*/
// SPOD example - old.haxe.org/manual/spod
// Neko/SQLite
class Main
{
static function main()
{
// Create the file my-database.sqlite if it doesn't exist and open a SQLite connection to it
var cnx = Sqlite.open('my-database.sqlite');
// Initialize the sys.db.Manager that handles the SPOD stuff behind the scenes
Manager.cnx = cnx;
// Create the User table in my-database.sqlite if it doesn't exist
if ( !sys.db.TableCreate.exists(User.manager) ) sys.db.TableCreate.create(User.manager);
// create a new user
var u = new User();
// set some parameters
u.name = "John Doe";
u.birthday = Date.now();
// store it in the db
u.insert();
// fetch user with id # 1
var u = User.manager.get(1);
if ( u == null ) throw "User #1 not found";
// trace it
trace(u.name);
}
}
// User SPOD objcet that extends sys.db.Object
class User extends Object
{
public var id : SId; // SPOD type for record Id
public var name : SString<32>; // SPOD type for strings
public var birthday : SDate; // SPOD type for date
public var phoneNumber : SNull<SText>; // SPOD tye for nullable variable-lenght text field
}
haxe Gen.hx
Gen.hx
package ;
import haxe.macro.*;
import haxe.macro.Type;
using haxe.macro.Tools;
private typedef Accessor = {
name: String,
read: Bool,
field: ClassField,
}
class Gen extends ExampleJSGenerator {
var accessors:Map<String, Accessor>;
override function getType(t:Type) //just a workaround until this is merged: https://github.com/HaxeFoundation/haxe/pull/3106
return
switch Context.follow(t) {
case TAbstract(a, _): getPath(a.get());
default: super.getType(t);
}
override function genClass(c: ClassType) {
accessors = new Map();
for(f in c.fields.get())
switch f.kind {
case FVar(get, set):
if (get == AccCall)
accessors['get_' + f.name] = { read: true, name: f.name, field: null };
if (set == AccCall)
accessors['set_' + f.name] = { read: false, name: f.name, field: null };
default:
}
for (f in c.fields.get())
if (accessors.exists(f.name))
accessors[f.name].field = f;
super.genClass(c);
}
override function genClassField( c : ClassType, p : String, f : ClassField ) {
switch f.kind {
case FVar(get, set) if (get == AccCall || set == AccCall):
var name = f.name;
function makePhysical() {
var any = false;
for (kind in 'get,set'.split(','))
switch accessors['${kind}_$name'] {
case null:
case a:
a = Reflect.copy(a);
function fixField(t:TypedExpr)
return switch t.expr {
case TField(owner, FInstance(cl, f)) if (f.get().name == name && owner.expr.equals(TConst(TThis))):
t.expr = TField(owner, FDynamic('_' + name));
any = true;
t;
default: t.map(fixField);
}
var expr = fixField(a.field.expr());
untyped a.field.expr = function () return expr;
accessors['${kind}_$name'] = a;
}
if (any) {
var f = Reflect.copy(f);
f.name = '_'+name;
f.kind = FVar(AccNormal, AccNormal);
genClassField(c, p, f);
}
}
function makeAccessors() {
var first = true;
print('Object.defineProperty($p.prototype, "$name", {');
for (kind in 'get,set'.split(','))
switch accessors['${kind}_$name'] {
case null:
case { field: accessor }:
if (first)
first = false;
else
print(',');
print(' $kind : ');
genExpr(accessor.expr());
}
print('})');
newline();
}
switch [get, set] {
case [AccCall | AccNever, AccCall | AccNever]:
if (f.meta.has(':isVar'))
makePhysical();
makeAccessors();
case [AccCall | AccNormal | AccNo, AccCall | AccNormal | AccNo]:
makePhysical();
makeAccessors();
default:
throw 'assert';
}
case FMethod(_) if (accessors.exists(f.name)):
var accessor = accessors[f.name],
func = field(f.name);
var field = field(accessor.name);
print('$p.prototype$func = ');
if (accessor.read)
print('function () { return this$field; }');
else
print('function (param) { return this$field = param; }');
newline();
default:
super.genClassField(c, p, f);
}
}
static function use()
Compiler.setCustomJSGenerator(function(api) new Gen(api).generate());
}