よく考えると coke とか joke とかいい名前があるじゃまいか

よさげなビルドツールを作ることはよさげな開発人生に一条のいい感じの光を投げかける的ななんかアレなわけです。
以下の C 言語なソースコードをビルドすることを考えてみましょう:

// main.c
#include <stdio.h>

int main() {
    printf( "Hello, World!\n" );

    return 0;
}

このようなケースは実の所あんまりないんですが、えーっと考えるのが簡単だよねって思ったのでそーいうことにしています。

SCons 風のビルドツール

SCons さんは Python 製のビルドツールっていうかなんかそれです。
ごっごる神がお作りになった次世代ビルドツールである Bazel だって SCons さん風に*1ビルドスクリプトを書きます。

const joke = require( '../lib/' );

joke.program( 'main.c' );

こうして見ると、JavaScript にはもってこいのような気がしなくもないと云うか何と云うか………。

CMake 風のビルドツール

CMake さんは Makefile を生成するメタビルドツールだと云うこともできるかもしれません。
めんどくさい AutoTools よりは佳いと思われます。

const joke = require( 'joke' );

joke.project( 'hello-example' );

joke.addExecutable( 'main', 'main.c' );

っていうか、パッと見、SCons とあんまり変わってないですね………。

それで?

joke を SCons 風なビルドツールとして作ってみようかなと思ったり思わなかったり……………。

*1:ドキュメント見ただけだから、そんなことはないかもしれない

class が export キーワードで export できなくてちょっと悲しかった

node のバージョンはこんな感じ:

% node --version
v6.3.1

wasp を書いてたら、

// ↓ class は書けるのに、export ができない!!!><
/* export */ class Emitter {
    constructor() {
        this.events = [];
    }

    on(name, cb) {
        this.events = ( this.events || [] );
        this.events[name] = cb;

        return this;
    }

    emit(name, that) {
        let self = this;

        let events = self.events[name] || [];

        events.forEach( anEvent => {
            anEvent.call( self, that );
        } );
        return self;
    }
}

// ↓ ので、従来通りの module.exports に Emitter を設定する。
module.exports = Emitter;

という感じだったので、悲しい。

ビルドツール Wasp とかどうかな

Fly を見ていたらこういう風に書けたって良いんじゃないかなと思った:

const gcc = require( 'wasp-gcc' );

const paths = {
    scripts: [ 'src/**/*.c' ],
    dist: 'obj'
};

export default function () {
    this.ready( 'build' );
}

export function compile() {
    this.from( paths.scripts )
        .gcc()
        .to( paths.dist );
}

うん、source() とか target() じゃなくて、from() とか to() って書けた方が良いと思う。

モジュールが Fly オブジェクトのメソッドを使えるようにするのってどうするんだーーーー?

Nemerle やってみた

インストール

yaourt でインストールできる:

% yaourt -S nemerle

へろーわーるど

using System;

Console.WriteLine( "Hello, World!" );

このファイルを hello.n とすると、ncc を使ってこんな風にコンパイルできる:

% ncc ./hello.n -o hello.exe

関数

関数は def から始まる:

def add(a : int, b : int) : int {
    a + b;
}

FParsec のチュートリアル的な何か

さて、今日は FParsec 日本語チュートリアルを読んでやってみるよ。

nuget はインストールしてるかな?
私は nuget3 をインストールしてみたよ:

% yaourt -S nuget3

勉強用のディレクトリを作成しよう:

% mkdir ./hello-fparsec
% cd ./hello-fparsec

FParsec をインストールしてみよう:

% nuget install FParsec -outputDirectory ./packages

-outputDirectory ./packages を付けない場合、カレントディレクトリに FParsec がインストールされてしまうので注意だ。
なので、Visual Studio の慣習に習って ./packages/ にインストールしてみた。

さっそく使ってみる

run 関数に何らかのパーサー関数と文字列を渡すと結果が返ってくる模様。

ʕ•͡ω•ʔ pfloat は FParsec 浮動小数点数をパースして浮動小数点数を返す組み込みのパーサー関数だぞ

// demo000.fsx
open FParsec


let test p str =
  match run p str with
    | Success(result      , _, _)   -> printfn "Success: %A" result
    | Failure(errorMessage, _, _)   -> printfn "Failure: %s" errorMessage

test pfloat "1.25"
test pfloat "1.25E"
test pfloat "1.25E 3"

それで、こんな風に打ってみると:

% fsharpi -I ./packages/FParsec.1.0.2/lib/net40-client/ -r "FParsec.dll" ./demo000.fsx
Success: 1.25
Failure: Error in Ln: 1 Col: 6
1.25E
     ^
Note: The error occurred at the end of the input stream.
Expecting: decimal digit

Failure: Error in Ln: 1 Col: 6
1.25E 3
     ^
Expecting: decimal digit

最初の test pfloat "1.25" だけパースされた。

ヽ(=´▽`=)ノ

OCaml の Test::Simple を目コピしてみた その 4

OCamlソースコードに沿って TAPDocument.ml を test-document.fs にして、TAPBuilder.ml を test-builder.fs に書いてみた。

なんかクラスっぽいやつでやってみたんだけど、これでいいのかな…………(汗

module Test.Builder

type Plan(?count : int) =
  let current_plan : option<int> = count

type TestCaseBuilder () =
  let mutable running_tests : (int -> Test.Document.node) list = [];

  member this.BuildTestCase(todo : option<string>, diag : option<Test.Document.diagnostic>, test : bool, description : string) =
    let running_test = fun number ->
      match todo with
        | None          -> Test.Document.TestCaseNode((if test then Test.Document.Ok else Test.Document.NotOk),
                                                      number,
                                                      description,
                                                      diag)
        | Some x        -> Test.Document.TodoTestCaseNode((if test then Test.Document.Ok else Test.Document.NotOk),
                                                          number,
                                                          description,
                                                          Test.Document.Todo(x),
                                                          diag)
    running_tests <- running_test :: running_tests
    running_test

  member this.BuildDiagnostic(line : string) =
    let running_test = fun (_ : int) ->
                       Test.Document.DiagnosticNode (Test.Document.Diag( [ line ] ))
    running_tests <- running_test :: running_tests
    running_test

今ブログ記事を書いてる時に気がついたけど、これじゃダメだよねwwwwww

だって、Plan とか let plan = Plan(12) とかってしないとわかんないじゃんwwwwww ほげーーーーーーー!!!

あと、F# では、関数に省略した引数を使えないので、typeコンストラクタ?の引数に使わなければならない………

(。・_・。) ………

TestCaseBuilderTestCaseResult とかにして、そのリストをどこかに持ったほうが良いのかも?
こんな感じ:

type TestCase(?todo, ?diag, test, description) =
  let running_test = fun number ->
      match todo with
        | None          -> Test.Document.TestCaseNode((if test then Test.Document.Ok else Test.Document.NotOk),
                                                      number,
                                                      description,
                                                      diag)
        | Some x        -> Test.Document.TodoTestCaseNode((if test then Test.Document.Ok else Test.Document.NotOk),
                                                          number,
                                                          description,
                                                          Test.Document.Todo(x),
                                                          diag)

OCaml の Test::Simple を目コピしてみた その 3

続き。

ʕ•͡ω•ʔ TAPDocument.ml 完全目コピでけたー!

init_document はなんか初期化しそう。

let init_document doc =
  let count     = count_tests         doc in
  let failures  = count_test_failures doc in
  match doc with
  | Document(PlanNode(plan)::nodes) ->
     Document(
         PlanNode(plan)::nodes
         @
           (if count = plan then [] else [ (create_count_footer count plan) ] )
         @
           (if failures = 0 then [] else [ (create_failure_footer count failures) ])
       )
  | Document(nodes)                 ->
     Document(
         nodes
         @
           [ PlanNode(count) ]
         @
           (if failures = 0 then [] else [ (create_failure_footer count failures) ])
       )

後は文字列化する関数。

そんでさ、function Document(nodes) -> は Warning 出るけど、どーすれば回避できるかわかんないって書いたけど、わかった。
let hoge Document nodes = ... でいいみたい。
引数が複数あってわけわかんない場合は let piyo (Document nodes) = みたいに括弧つけとこう。

let string_of_status status =
  match status with
  | OK          -> "ok"
  | NotOk       -> "not ok"
let string_of_diagnostic (Diag lines) =
  List.map (fun line -> "# " + line + "\n") lines
  |> List.fold (+) ""

こことかかっこよく |>(パイプライン) つこうた

let string_of_directive (Todo s) =
  "# TODO " + s
let string_of_node node =
  let emit_diagnostic diag =
    (match diag with
     | None          -> "\n"
     | Some diag     -> "\n" + (string_of_diagnostic diag))
  in
  match node with
  | TestCaseNode(status, num, desc, diag)          ->
     (string_of_status status)
     + " "
     + num.ToString()
     + " - "
     + desc
     + (emit_diagnostic diag)
  | TodoTestCaseNode(status, num, desc, dir, diag) ->
     (string_of_status status)
     + " "
     + num.ToString()
     + " - "
     + desc
     + (string_of_directive dir)
     + (emit_diagnostic diag)
  | DiagnosticNode(diag)                           ->
     (string_of_diagnostic diag)
  | PlanNode(count)                                ->
     "1.." + count.ToString() + "\n"
let string_of_document (Document nodes) =
  List.map string_of_node nodes
  |> String.concat "" 

ここも。

全部のソースはここに置いてある。

OCaml の Test::Simple を目コピしてみた その 2

続き。

OCaml では文字列の結合は ^ だけど*1、F# では + だ。
また、OCaml での string_of_int は F# には存在しないけど、書くとしたらこんな感じになる:

let string_of_int (n:int):string =
  n.ToString()

DiagnosticNode を返す関数 2 連発:

let create_failure_footer test_count failure_count =
  DiagnosticNode(
    Diag(["Looks like you failed "
          + failure_count.ToString()
          + " tests of "
          + test_count.ToString()
          + " run."])
    )
let create_count_footer test_count plan_count =
  DiagnosticNode(
    Diag(["Looks like you planed "
          + plan_count.ToString()
          + " tests but "
          + (if test_count < plan_count then
               ("only ran " + test_count.ToString())
             else
               ("ran " + ((test_count - plan_count).ToString()) + " extra"))
          + " ."])
    )

*1:F# でも Warning は出るものの、使うことができる