LoginSignup
4
1

More than 5 years have passed since last update.

Haxe七変化

Last updated at Posted at 2017-10-18

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

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

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

-notimeoutはつけておくとタイムアウトで失敗しなくなるのであったほうがいいかな…
あとはNode.jsの置換動作が一部気に入らないので修正。
"C:\HaxeToolkit\haxe\lib\hxnodejs\x,x,x\src"
辺りに入ってるSys.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);
    }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

  ↓

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とした。

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

構成は次の通り。

C:\> haxe -cp ファイルパス -main メインクラス名及びファイル名 -言語 出力名

C#はDオプションをつけないとコンパイルバージョンが.Net2.0になり、
Node.jsはlibオプション付けないとそもそもNode.js用にコンパイルができない。

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

FizzBuzz

Haxe

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++

トランスパイル

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

ネイティブ実装

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\

トランスパイル

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

            }

        }
    }


}

ネイティブ実装

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

トランスパイル

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

ネイティブ実装

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

トランスパイル

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)

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ネイティブ実装

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

トランスパイル

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

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

ネイティブ実装

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

トランスパイル

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

ネイティブ実装

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問題の上限を入力から与えて何度でも取得できる対話プログラムを制作した。

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

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

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

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
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1