Argu すごい

Argu は F# 製のコマンドラインパーサーだよ。

gmake のオプションを前半だけ書いてみた。

type Arguments =
  | [<AltCommandLine("-B")>] Always_Make
  | [<AltCommandLine("-C")>] Directory             of dir        : string
  |                          Debug                 of mode       : DebugMode option
  | [<AltCommandLine("-e")>] Environment_Overrides
  |                          Eval                  of expr       : string
  | [<AltCommandLine("-f")>] File                  of file       : string
  | [<AltCommandLine("-i")>] Ignore_Errors
  | [<AltCommandLine("-I")>] Include_Dir           of dir        : string
  | [<AltCommandLine("-h")>] Jobs                  of n          : int option
  | [<AltCommandLine("-k")>] Keep_Going
with
  interface IArgParserTemplate with
    member this.Usage =
      match this with
        | Always_Make             -> "Unconditionally make all targets."
        | Directory _             -> "Change to <dir> before doing anything."
        | Debug _                 -> "Print various types of debugging information."
        | Environment_Overrides   -> "Environment variables override makefiles."
        | Eval _                  -> "Evaluate <expr> as a makefile statement."
        | File _                  -> "Read <file> as a makefile."
        | Ignore_Errors           -> "Ignore errors from recipes."
        | Include_Dir _           -> "Search <dir> for included makefiles."
        | Jobs _                  -> "Allow <n> jobs at once; infinite jobs with no arg."
        | Keep_Going              -> "Keep going when some targets can't be made."

DebugMode に何を入れるのかわからなかったので、スキップ。
書くならこんな感じに:

type DebugMode =
  | A = 1
  | B = 2
  | C = 3

コマンドラインをパースする

コマンドラインをパースするにはパーサーを作成する:

open Argu

let parser = ArgumentParser.Create<Arguments>( programName = "make.exe", errorHandler = ProcessExiter() )

第 2 引数の errorHandler = ProcessExiter() を忘れると恐ろしいことが起こるので注意。
後は、argv を引数に parserParse() メソッドか ParseCommandLine() メソッドを呼び出すだけ:

let results = parser.ParseCommandLine argv

すると、Arguments [] が返ってくる。
ので、これをあれこれする。

らしい。

printfn とかで出力する時に

最近までこんな風にやってたんだけど:

let name = user.GetName()    // printfn "Hello, %s!" user.GetName() はエラーになっちゃうので
                             // 一旦、変数に束縛する。

printfn "Hello, %s!" name

別にそんなことをする必要はなかったらしい。 <| を使うんだルーク!!

printfn "Hello, %s!" <| user.GetName()

ヽ(=´▽`=)ノ

よく考えると 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:ドキュメント見ただけだから、そんなことはないかもしれない

RequestPolicy じゃなくて RequestPolicy Continued にしたら捗った

いままで、RequestPolicy を愛用していたんだけど、全然更新されていないのに気がついたので、RequestPolicy Continued に切り替えた。

(๑´ڡ`๑) めっちゃ使いやすい

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)