Haxe七変化

ということで、Haxe触ってみた。
今回はHaxeの単一ソースからNeko、HashLink、C++、C#、JavaScript(Node.js)、Python3、Lua、PHPに対するコンパイルを行った。

まず、C++、C#、Node.jsに対してコンパイルを行うにはhaxelibでパッケージを導入する必要がある。
インストーラーで導入してパスが通っている前提で、

```bat:C++
C:\> haxelib install hxcpp -notimeout
```
```bat:C#
C:\> haxelib install hxcs -notimeout
```
```bat:Node.js
C:\> haxelib install hxnodejs -notimeout
```
```bat:Java(おまけ)
C:\> haxelib install hxjava -notimeout
```

-notimeoutはつけておくとタイムアウトで失敗しなくなるのであったほうがいいかな…
あとはNode.jsの置換動作が一部気に入らないので修正。
"C:\HaxeToolkit\haxe\lib\hxnodejs\x,x,x\src"
辺りに入ってるSys.hxをテキストエディタで開いて下記を置換した。

```hx:befour
import js.node.ChildProcess;
import js.Node.process;

@:dce
// @:coreApi
class Sys {
    public static inline function print(v:Dynamic):Void {
        process.stdout.write(v);
    }

    public static inline function println(v:Dynamic):Void {
        process.stdout.write("\n");
        process.stdout.write(v);
    }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
```
　　↓

```hx:befour
import js.Node.console;
import js.node.Util;
import js.node.ChildProcess;
import js.Node.process;

@:dce
// @:coreApi
class Sys {
    public static inline function print(v:Dynamic):Void {
        process.stdout.write(Util.format(v));
    }

    public static inline function println(v:Dynamic):Void {
        console.log(v);
    }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
```

各言語に対しては次のようにトランスパイル/コンパイルする。
ファイル名、クラス名は全てFbとした。

```bat:
haxe -main Fb -neko Fb/Fb.n
haxe -main Fb -hl Fb/Fb.hl
haxe -main Fb -cpp Fb/Fb.cpp
haxe -main Fb -cs Fb/Fb.cs -D net-ver=45
haxe -main Fb -js Fb/Fb.js -lib hxnodejs
haxe -main Fb -python Fb/Fb.py
haxe -main Fb -lua Fb/Fb.lua
haxe -main Fb -php Fb/Fb.php
```
構成は次の通り。

```bat:
C:\> haxe -cp ファイルパス -main メインクラス名及びファイル名 -言語 出力名
```
C#はDオプションをつけないとコンパイルバージョンが.Net2.0になり、
Node.jsはlibオプション付けないとそもそもNode.js用にコンパイルができない。

ここから本題。
適当な題材としてFizzBuzz問題を選んだ。

# FizzBuzz
## Haxe
```hx:Fb.hx
class Fb{
	static function main():Void {
		for(i in 1...100+1){
			if(i%3==0 && i%5==0){
				Sys.println("Fizz Buzz");
			}
			else if(i%3==0){
				Sys.println("Fizz");
			}
			else if(i%5==0){
				Sys.println("Buzz");
			}
			else {
				Sys.println(i);
			}
		}
	}
}
```
以下にトランスパイルしたソース(※主要部分のみ)とネイティブ実装を併記する。

## C++
### トランスパイル
```cpp:Fb.cpp
// Generated by Haxe 3.4.4
#include <hxcpp.h>

#ifndef INCLUDED_Fb
#include <Fb.h>
#endif
#ifndef INCLUDED_Sys
#include <Sys.h>
#endif

HX_LOCAL_STACK_FRAME(_hx_pos_42f183277727ffcb_3_main,"Fb","main",0x529cbc8b,"Fb.main","Fb.hx",3,0x8ef6c6e2)

void Fb_obj::__construct() { }

Dynamic Fb_obj::__CreateEmpty() { return new Fb_obj; }

void *Fb_obj::_hx_vtable = 0;

Dynamic Fb_obj::__Create(hx::DynamicArray inArgs)
{
	hx::ObjectPtr< Fb_obj > _hx_result = new Fb_obj();
	_hx_result->__construct();
	return _hx_result;
}

bool Fb_obj::_hx_isInstanceOf(int inClassId) {
	return inClassId==(int)0x00000001 || inClassId==(int)0x00003d5c;
}

void Fb_obj::main(){
            	HX_STACKFRAME(&_hx_pos_42f183277727ffcb_3_main)
HXDLIN(   3)		int _g1 = (int)1;
HXDLIN(   3)		int _g = (int)101;
HXDLIN(   3)		while((_g1 < _g)){
HXDLIN(   3)			_g1 = (_g1 + (int)1);
HXDLIN(   3)			int i = (_g1 - (int)1);
HXLINE(   4)			bool _hx_tmp;
HXDLIN(   4)			if ((hx::Mod(i,(int)3) == (int)0)) {
HXLINE(   4)				_hx_tmp = (hx::Mod(i,(int)5) == (int)0);
            			}
            			else {
HXLINE(   4)				_hx_tmp = false;
            			}
HXDLIN(   4)			if (_hx_tmp) {
HXLINE(   5)				::Sys_obj::println(HX_("Fizz Buzz",d0,f9,43,55));
            			}
            			else {
HXLINE(   7)				if ((hx::Mod(i,(int)3) == (int)0)) {
HXLINE(   8)					::Sys_obj::println(HX_("Fizz",e3,06,95,2e));
            				}
            				else {
HXLINE(  10)					if ((hx::Mod(i,(int)5) == (int)0)) {
HXLINE(  11)						::Sys_obj::println(HX_("Buzz",73,47,f9,2b));
            					}
            					else {
HXLINE(  14)						::Sys_obj::println(i);
            					}
            				}
            			}
            		}
            	}


STATIC_HX_DEFINE_DYNAMIC_FUNC0(Fb_obj,main,(void))


Fb_obj::Fb_obj()
{
}

bool Fb_obj::__GetStatic(const ::String &inName, Dynamic &outValue, hx::PropertyAccess inCallProp)
{
	switch(inName.length) {
	case 4:
		if (HX_FIELD_EQ(inName,"main") ) { outValue = main_dyn(); return true; }
	}
	return false;
}

#if HXCPP_SCRIPTABLE
static hx::StorageInfo *Fb_obj_sMemberStorageInfo = 0;
static hx::StaticInfo *Fb_obj_sStaticStorageInfo = 0;
#endif

static void Fb_obj_sMarkStatics(HX_MARK_PARAMS) {
	HX_MARK_MEMBER_NAME(Fb_obj::__mClass,"__mClass");
};

#ifdef HXCPP_VISIT_ALLOCS
static void Fb_obj_sVisitStatics(HX_VISIT_PARAMS) {
	HX_VISIT_MEMBER_NAME(Fb_obj::__mClass,"__mClass");
};

#endif

hx::Class Fb_obj::__mClass;

static ::String Fb_obj_sStaticFields[] = {
	HX_HCSTRING("main","\x39","\x38","\x56","\x48"),
	::String(null())
};

void Fb_obj::__register()
{
	hx::Object *dummy = new Fb_obj;
	Fb_obj::_hx_vtable = *(void **)dummy;
	hx::Static(__mClass) = new hx::Class_obj();
	__mClass->mName = HX_HCSTRING("Fb","\x5c","\x3d","\x00","\x00");
	__mClass->mSuper = &super::__SGetClass();
	__mClass->mConstructEmpty = &__CreateEmpty;
	__mClass->mConstructArgs = &__Create;
	__mClass->mGetStaticField = &Fb_obj::__GetStatic;
	__mClass->mSetStaticField = &hx::Class_obj::SetNoStaticField;
	__mClass->mMarkFunc = Fb_obj_sMarkStatics;
	__mClass->mStatics = hx::Class_obj::dupFunctions(Fb_obj_sStaticFields);
	__mClass->mMembers = hx::Class_obj::dupFunctions(0 /* sMemberFields */);
	__mClass->mCanCast = hx::TCanCast< Fb_obj >;
#ifdef HXCPP_VISIT_ALLOCS
	__mClass->mVisitFunc = Fb_obj_sVisitStatics;
#endif
#ifdef HXCPP_SCRIPTABLE
	__mClass->mMemberStorageInfo = Fb_obj_sMemberStorageInfo;
#endif
#ifdef HXCPP_SCRIPTABLE
	__mClass->mStaticStorageInfo = Fb_obj_sStaticStorageInfo;
#endif
	hx::_hx_RegisterClass(__mClass->mName, __mClass);
}
```

### ネイティブ実装
```cpp:Fbn.cpp
#include<iostream>

int main(){
	for(int i=1;i<=100;i++){
		if(i%3==0 && i%5==0){
			std::cout<<"Fizz Buzz"<<std::endl;
		}
		else if(i%3==0){
			std::cout<<"Fizz"<<std::endl;
		}
		else if(i%5==0){
			std::cout<<"Buzz"<<std::endl;
		}
		else {
			std::cout<<i<<std::endl;
		}
	}
}
```

## C\#
### トランスパイル
```cs:Fb.cs
// Generated by Haxe 3.4.4

#pragma warning disable 109, 114, 219, 429, 168, 162
public class Fb : global::haxe.lang.HxObject {
	
	public static void Main(){
		global::cs.Boot.init();
		{
			global::Fb.main();
		}
	}
	public Fb(global::haxe.lang.EmptyObject empty) {
	}
	
	
	public Fb() {
		global::Fb.__hx_ctor__Fb(this);
	}
	
	
	public static void __hx_ctor__Fb(global::Fb __hx_this) {
	}
	
	
	public static void main() {
		unchecked {
			int _g1 = 1;
			int _g = 101;
			while (( _g1 < _g )) {
				int i = _g1++;
				if (( ( ( i % 3 ) == 0 ) && ( ( i % 5 ) == 0 ) )) {
					global::System.Console.WriteLine(((object) ("Fizz Buzz") ));
				}
				else if (( ( i % 3 ) == 0 )) {
					global::System.Console.WriteLine(((object) ("Fizz") ));
				}
				else if (( ( i % 5 ) == 0 )) {
					global::System.Console.WriteLine(((object) ("Buzz") ));
				}
				else {
					global::System.Console.WriteLine(((object) (i) ));
				}
				
			}
			
		}
	}
	
	
}
```

### ネイティブ実装
```cs:Fbn.cs
using System;

class Program{
	static void Main(){
		for(var i=1;i<=100;i++){
			if(i%3==0 && i%5==0){
				Console.WriteLine("Fizz Buzz");
			}
			else if(i%3==0){
				Console.WriteLine("Fizz");
			}
			else if(i%5==0){
				Console.WriteLine("Buzz");
			}
			else {
				Console.WriteLine(i);
			}
		}
	}
}
```
```vbnet:Fb.vb
Imports System

Module Program
	Sub Main()
		For i=1 To 100
			If i Mod 3=0 And i Mod 5=0 Then
				Console.WriteLine("Fizz Buzz")
			ElseIf i Mod 3=0 Then
				Console.WriteLine("Fizz")
			ElseIf i Mod 5=0 Then
				Console.WriteLine("Buzz")
			Else
				Console.WriteLine(i)
			End If
		Next
	End Sub
End Module
```

## JavaScript(Node.js)
### トランスパイル
```js:Fb.js
// Generated by Haxe 3.4.4
if (process.version < "v4.0.0") console.warn("Module " + (typeof(module) == "undefined" ? "" : module.filename) + " requires node.js version 4.0.0 or higher");
(function () { "use strict";
var Fb = function() { };
Fb.main = function() {
	var _g1 = 1;
	var _g = 101;
	while(_g1 < _g) {
		var i = _g1++;
		if(i % 3 == 0 && i % 5 == 0) {
			console.log("Fizz Buzz");
		} else if(i % 3 == 0) {
			console.log("Fizz");
		} else if(i % 5 == 0) {
			console.log("Buzz");
		} else {
			console.log(i);
		}
	}
};
var haxe_io_Bytes = function() { };
var js_node_buffer_Buffer = require("buffer").Buffer;
Fb.main();
})();
```

### ネイティブ実装
```js:Fbn.js
(function(){
	for(var i=1;i<=100;i++){
		if(i%3==0 && i%5==0){
			console.log("Fizz Buzz");
		}
		else if(i%3==0){
			console.log("Fizz");
		}
		else if(i%5==0){
			console.log("Buzz");
		}
		else {
			console.log(i);
		}
	}
})();
```

## Python
### トランスパイル
```py3:Fb.py
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class Fb:
    _hx_class_name = "Fb"
    __slots__ = ()
    _hx_statics = ["main"]

    @staticmethod
    def main():
        _g1 = 1
        _g = 101
        while (_g1 < _g):
            i = _g1
            _g1 = (_g1 + 1)
            if ((HxOverrides.mod(i, 3) == 0) and ((HxOverrides.mod(i, 5) == 0))):
                Sys.println("Fizz Buzz")
            elif (HxOverrides.mod(i, 3) == 0):
                Sys.println("Fizz")
            elif (HxOverrides.mod(i, 5) == 0):
                Sys.println("Buzz")
            else:
                Sys.println(i)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

### ネイティブ実装
```py3:Fbn.py
if __name__=="__main__":
	for i in range(1,100+1):
		if i%3==0 and i%5==0:
			print("Fizz Buzz")
		elif i%3==0:
			print("Fizz")
		elif i%5==0:
			print("Buzz")
		else:
			print(i)
```

## Lua
### トランスパイル
```lua:Fb.lua
--[[~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]]

Fb.new = {}
Fb.main = function() 
  local _g1 = 1;
  local _g = 101;
  while (_g1 < _g) do 
    _g1 = _g1 + 1;
    local i = _g1 - 1;
    if (((_G.math.fmod(i, 3)) == 0) and ((_G.math.fmod(i, 5)) == 0)) then 
      _G.print("Fizz Buzz");
    else
      if ((_G.math.fmod(i, 3)) == 0) then 
        _G.print("Fizz");
      else
        if ((_G.math.fmod(i, 5)) == 0) then 
          _G.print("Buzz");
        else
          _G.print(Std.string(i));
        end;
      end;
    end;
    end;
end

--[[~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]]
```

### ネイティブ実装
```lua:Fbn.lua
(function()
	for i=1,100 do
		if i%3==0 and i%5==0 then
			print("Fizz Buzz");
		elseif i%3==0 then
			print("Fizz");
		elseif i%5==0 then
			print("Buzz");
		else
			print(i);
		end
	end
end)();
```

## PHP
### トランスパイル
```php:Fb.class.php
<?php

// Generated by Haxe 3.4.4
class Fb {
	public function __construct(){}
	static function main() {
		$_g1 = 1;
		$_g = 101;
		while($_g1 < $_g) {
			$_g1 = $_g1 + 1;
			$i = $_g1 - 1;
			$tmp = null;
			if(_hx_mod($i, 3) === 0) {
				$tmp = _hx_mod($i, 5) === 0;
			} else {
				$tmp = false;
			}
			if($tmp) {
				Sys::println("Fizz Buzz");
			} else {
				if(_hx_mod($i, 3) === 0) {
					Sys::println("Fizz");
				} else {
					if(_hx_mod($i, 5) === 0) {
						Sys::println("Buzz");
					} else {
						Sys::println($i);
					}
				}
			}
			unset($tmp,$i);
		}
	}
	function __toString() { return 'Fb'; }
}
```
### ネイティブ実装
```php:Fbn.php
<?php
(function(){
	for($i=1;$i<=100;$i++){
		if($i%3==0 && $i%5==0){
			print "Fizz Buzz\n";
		}
		else if($i%3==0){
			print "Fizz\n";
		}
		else if($i%5==0){
			print "Buzz\n";
		}
		else {
			print "${i}\n";
		}
	}
})();
?>
```

## FizzBuzzまとめ
* JavaScriptは元となっているだけあって最も原型を保ち、コード量も少なかった。
* よくわからないけどC++凄い。
* Pythonの圧倒的長さ。1000行越えの超大作に。

# FizzBuzz REPL(標準入力入りHaxe)
Haxe共通の標準入力は
Sys.stdin().readLine()
より取得できる、がjsは元より対応しておらず、
C#でも動作に支障があるため、そのまま使用するには難があった。
そこで同じソースからそれぞれ7言語に変換できるようにプリプロセッサ命令を用いてコンパイル分けを行ってみた。
題材として先ほどのFizzBuzz問題の上限を入力から与えて何度でも取得できる対話プログラムを制作した。

```hx:Fbio.hx
#if js
import js.Node.process;
import js.node.Readline;
#elseif cs
import cs.system.Console;
#else
import haxe.io.Input;
#end

class Fbio{
	#if js
	static var rl=Readline.createInterface(process.stdin,process.stdout);
	#end

	static function myPrompt(pt:String):Void {
	#if js
		rl.setPrompt(pt);
		rl.prompt();
	#else
		Sys.print(pt);
	#end
	}

	#if !js static function readln():String { #end
	#if cs
		return Console.ReadLine();
	#elseif !js
		var input:Input=Sys.stdin();
		return input.readLine();
	#end
	#if !js } #end

	static function main():Void {
		var pt="> ";

		#if js (function loop():Void {
		#else  while(true){ 
		#end
			myPrompt(pt);

			#if js rl.once("line",(str:String)->{
			#else  var str=readln();
			#end

			var num=Std.parseInt(str);
			if(!Std.is(num,Int)){
				myPrompt("Exit Console? (y/n) >> ");

				#if js rl.once("line",(str:String)->{
				#else  str=readln();
				#end

				if(str=="y") Sys.exit(0);
				#if js loop(); }); #end
			}
			else {
				fizzbuzz(num);
				myPrompt(pt);
				#if js loop(); #end
			}
		}
		#if js ); })(); #end //rl.once, loop
	}

	static function fizzbuzz(n:Int):Void {
		for(i in 1...n+1){
			if(i%3==0 && i%5==0){
				Sys.println("Fizz Buzz");
			}
			else if(i%3==0){
				Sys.println("Fizz");
			}
			else if(i%5==0){
				Sys.println("Buzz");
			}
			else {
				Sys.println(i);
			}
		}
	}
}
```

JavaScriptまでターゲットに含む場合はいかに非同期処理を抑え込むかが課題となると思う。
Generator FunctionもAsync/Awaitも使用不可能?であるため大変厳しい。
(Promiseは使えるっぽい?)
最後に比較用としてjsとVB・HSPの実装を載せておく。

## Node.js FizzBuzz REPL
```js:Fbnio.js
"use strict";

const rl=require("readline").createInterface(process.stdin,process.stdout);

function myPrompt(pt){
	rl.setPrompt(pt);
	rl.prompt();
}

function readln(){
	return new Promise(resolve=>rl.once("line",resolve));
}

(async function main(){
	const pt="> ";
	for(;;){
		myPrompt(pt);
		var str=await readln();
		var num=+str;
		if(isNaN(num)){
			myPrompt("Exit Console? (y/n) >> ");
			str=await readln();
			if(str=="y") process.exit();
		}
		else{
			fizzbuzz(0|num);
		}
	}
})();

function fizzbuzz(n){
	for(var i=1;i<=n;i=0|i+1){
		if(i%3==0 && i%5==0){
			console.log("Fizz Buzz");
		}
		else if(i%3==0){
			console.log("Fizz");
		}
		else if(i%5==0){
			console.log("Buzz");
		}
		else {
			console.log(i);
		}
	}
}
```

## VB.net
```vbnet:Fbnio.vb
Option Strict On
Imports System

Module Fbio
	Sub myPrompt(pt As String)
 		Console.Write(pt)
 	End Sub

	Function readln() As String 
		return Console.ReadLine()
	End Function

	Sub Main()
		Const pt="> "
		Do
			myPrompt(pt)
			Dim str=readln()
			Dim num As Integer
			If Not Integer.TryParse(str,num) Then
				myPrompt("Exit Console? (y/n) >> ")
				str=readln()

				If str="y" Then Environment.Exit(0)
			Else
				fizzbuzz(num)
			End If
		Loop
	End Sub

	Sub fizzbuzz(n As Integer)
		For i=1 To n
			If i Mod 3=0 And i Mod 5=0 Then
				Console.WriteLine("Fizz Buzz")
			ElseIf i Mod 3=0 Then
				Console.WriteLine("Fizz")
			ElseIf i Mod 5=0 Then
				Console.WriteLine("Buzz")
			Else 
				Console.WriteLine(i)
			End If
		Next
	End Sub
End Module
```

## HSP
```cpp:Fbnio.hsp
#runtime "hsp3cl"
#cmpopt varinit 1

#module Fbio
	#uselib "msvcrt"
	#func printf "printf" str
	#deffunc myPrompt str pt
		printf pt
	return

	#defcfunc readln
		sdim res
		input res,,1
		br=$0D,$0A
		foreach br
			res=strtrim(res,2,br(cnt))
		loop
	return res

	#deffunc main
		pt="> "
		repeat
			myPrompt pt
			_str=readln()
			num=int(_str)
			if num=0 && _str!="0" {
				myPrompt "Exit Console? (y/n) >> "
				_str=readln()
				mes _str
				if _str="y" :end
			}
			else {
				fizzbuzz num
			}
		loop
	return

	#deffunc fizzbuzz int n
		repeat n,1
			if cnt\3=0 && cnt\5=0 {
				mes "Fizz Buzz"
			}
			else:if cnt\3=0 {
				mes "Fizz"
			}
			else:if cnt\5=0 {
				mes "Buzz"
			}
			else { 
				mes cnt
			}
		loop
	return
#global
main
```