Inline-0.43 > Inline-API

名前

Inline-API - Inline.pmを使って、あるプログラミング言語をPerlに結びつける方法

概要

    #!/usr/bin/perl
    
    use Inline Foo;
    say_it('foo');  # Use Foo to print "Hello, Foo"
    
    __Foo__ 
    foo-sub say_it {
        foo-my $foo = foo-shift;
        foo-print "Hello, $foo\n";
    }

説明

Inline Cをめちゃクールと思っても、実際には新しいプログラミング言語"Foo"と一緒にPerlを機能させる必要があるとします。あなたは幸運です。Inline.pmはあなたの独自のInline::Fooのような、Inline言語サポート・モジュール(ILSM)を追加することをサポートします。

Inlineは常に違うプログラミング言語と一緒に機能するように意図してきました。処理の細かい部分は実装をまたがって共有することができます。そのためInline::JavaInline::ASMによく似たインターフェースを持っています。全てに共通のコードはInline.pmに入っています。

Inline::Pythonのような特定言語のモジュールはInline.pmのサブクラスです。共通の動きを好きなだけ多く継承し、独自の動きを提供することが出来ます。これは通常構成設定オプションと言語特有のコンパイルの形式を取ります。

InlineのCのサポートが、おそらくはコピーするには一番よい雛型でしょう。バージョン0.30から、全てのCのサポートはモジュールInline::Cに分離され、さらに文法の解析はInline::C::grammarに分けられています。これらの構成要素はInlineディストリビューションの中に入っています。

このPODはILSMを実装するために必要な詳細のすべてを提供します。さらなる支援については inline@perl.org にコンタクトをとってください。下記の"参考資料"をご覧下さい。

私たちは、Inlineと一緒に配布される仮の言語Inline::Fooを吟味していきます。これは実際には完全に機能するILSMです。私はInlineの基本的な機能をテストするため、Inlineのテスト・ハーネスで使っています。それは非常に短く、Inline APIについて理解することを助けることが出来ます。

スケルトン

このチュートリアルの残りの部分のために、想像上の言語FooのためのILSMを書くものと想定しましょう。それをInline::Fooと呼びます。完全な(機能する)実装を以下に示します。

    package Inline::Foo;
    use strict;
    $Inline::Foo::VERSION = '0.01';
    @Inline::Foo::ISA = qw(Inline);
    require Inline;
    use Carp;
    
    #===========================================================
    # Register Foo as an Inline Language Support Module (ILSM)
    #===========================================================
    sub register {
        return {
            language => 'Foo',
            aliases => ['foo'],
            type => 'interpreted',
            suffix => 'foo',
           };
    }
    
    #===========================================================
    # Error messages
    #===========================================================
    sub usage_config { 
        my ($key) = @_;
        "'$key' is not a valid config option for Inline::Foo\n";
    }
    
    sub usage_config_bar { 
        "Invalid value for Inline::Foo config option BAR";
    }
    
    #===========================================================
    # Validate the Foo Config Options
    #===========================================================
    sub validate {
        my $o = shift;
        $o->{ILSM}{PATTERN} ||= 'foo-';
        $o->{ILSM}{BAR} ||= 0;
        while (@_) {
        my ($key, $value) = splice @_, 0, 2;
        if ($key eq 'PATTERN') {
            $o->{ILSM}{PATTERN} = $value;
            next;
        }
        if ($key eq 'BAR') {
            croak usage_config_bar
              unless $value =~ /^[01]$/;
            $o->{ILSM}{BAR} = $value;
            next;
        }
        croak usage_config($key);
        }
    }
    
    #===========================================================
    # Parse and compile Foo code
    #===========================================================
    sub build {
        my $o = shift;
        my $code = $o->{API}{code};
        my $pattern = $o->{ILSM}{PATTERN};
        $code =~ s/$pattern//g;
        $code =~ s/bar-//g if $o->{ILSM}{BAR};
        sleep 1;             # imitate compile delay
        {
            package Foo::Tester;
            eval $code;
        }
        croak "Foo build failed:\n$@" if $@;
        my $path = "$o->{API}{install_lib}/auto/$o->{API}{modpname}";
        my $obj = $o->{API}{location};
        $o->mkpath($path) unless -d $path;
        open FOO_OBJ, "> $obj"
          or croak "Can't open $obj for output\n$!";
        print FOO_OBJ $code;
        close \*FOO_OBJ;
    }
    
    #===========================================================
    # Only needed for interpreted languages
    #===========================================================
    sub load {
        my $o = shift;
        my $obj = $o->{API}{location};
        open FOO_OBJ, "< $obj"
          or croak "Can't open $obj for output\n$!";
        my $code = join '', <FOO_OBJ>;
        close \*FOO_OBJ;
        eval "package $o->{API}{pkg};\n$code";
        croak "Unable to load Foo module $obj:\n$@" if $@;
    }
    
    #===========================================================
    # Return a small report about the Foo code.
    #===========================================================
    sub info {
        my $o = shift;
        my $text = <<'END';
    This is a small report about the Foo code. Perhaps it contains
    information about the functions the parser found which will be
    bound to Perl. It will get included in the text produced by the
    Inline 'INFO' command.
    END
        return $text;
    }
    
    1;

load()を除いて、このコードでのサブルーチンはILSMでは必須です。それらが何をしているのかは、これから説明します。いくつかの点に注意してください:

  1. Inline::Foo はInlineのサブクラスでなければなりません。これは以下の行により実現できます:

        @Inline::Foo::ISA = qw(Inline);
  2. 'require Inline;'という行は必須ではありません。しかし'use Inline;'と呼び出さないことを忘れないようにさせるため、そこにあります。これは機能しません。

  3. 利用者が以下のように言うことは適切ではないということを忘れないで下さい:

        use Inline::Foo;

    Inline.pmは、そのimportメソッドでそのような利用方法を検知します。Inline::Fooがサブクラスであるので、それは自動的に継承されます。

  4. build関数では、通常あなたのソースコードを解析する必要があります。Inline::CはこれをするためにParse::RecDescentを使っています。Inline::Fooは単にevalを使っています。('foo-'の並びの全てを取り出した後に)。

    (JavaやPythonのような)多くのILSMのために機能する、代わりの解析方法は、あなたに代わって解析するため、その言語のコンパイラそのものを使うことです。これはコンパイラが解析情報を返すことが出来る限り、うまくいきます。

Inline API

このセクションは、ILMSを実装するために提供する必要がある機能の、より形式的な仕様になります。

Inlineが、なんらかのFooコードがコンパイルされる必要があると判断するとき、それは自動的にあなたのILSMモジュールをロードします。そして、それは適用する必要のある様々なサブルーチンを呼び出します。これらのサブルーチンを"コールバック"と呼びます。

以下の5つのコールバック・サブルーチンを提供する必要があります。

register() コールバック

このサブルーチンは何も引数を受け取りません。それはILSMメタデータのハッシュへのリファレンスを返します。Inlineはシステムにインストールされている新しいILSMを検出しようとするときにだけ、このサブルーチンを呼び出します。Fooのために返すハッシュ・リファレンスの例を以下に示します:

    {
     language => 'Foo',
     aliases => ['foo'],
     type => 'interpreted',
     suffix => 'foo',
    };

メタデータの要素には以下の意味があります:

language

これはその言語の適切な名前です。通常、ある言語'X'のためには、Inline::Xとして実装されます。

aliases

これは言語名のエイリアスの配列へのリファレンスです。ある言語の適切な名前には単語の文字[A-Za-z0-9_]しか入れることが出来ません。エイリアスには空白やクォートを除く任意の文字を入れることが出来ます。これは'C++'や'C#'のような名前には便利です。

type

'compiled'(=コンパイル言語) あるいは'interpreted'(=インタープリタ言語)に設定されなければなりません。言語のカテゴリを示します。

suffix

これは作成されるオブジェクトのキャッシュのためのファイルの拡張子です。'compiled'(=コンパイル言語)では、おそらく'so'や'dll'になるでしょう。適切な値がConfig.pmに入っています。

インタープリタ言語では、、この値はあなたの好きなようにすることができます。Pythonはpydatを使います。Fooはfooを使います。

validate()コールバック

このルーチンは、基本のInlineモジュールによって既に扱われていない、渡された全ての構成設定オプションを取得します。オプションはキー/値の組み合わせで渡されます。各オプションの評価とその値のInlineオブジェクト(これも渡されます)での格納は、あなたに掛かっています。もしあるオプションが適切でなければ、適切なエラメッセージでcroakするべきです。

build()コールバック

このサブルーチンはFooソースコードの解析とコンパイルを行うことに責任を持ちます。Inlineオブジェクトが唯一の引数として渡されます。全ての関連する情報は、このオブジェクトに格納されます。特定の名前のキャッシュ・オブジェクトを作成したり、適切なエラーメッセージでcroakするために、build()が必要です。

これがILSMの肝です。それが非常にわかりにくいものになりがちなので、Inline::Cのような既存のILSMを学習するのがおそらく一番よいでしょう。

load()コールバック

このメソッドはインタープリタ言語に提供されるためだけに必要です。これはインタープリタを起動することに責任を持ちます。

コンパイル言語では、共有オブジェクトやDLLをロードするためにDynaLoaderを使っている、Inline.pmのloadルーチンが呼び出されます。

info() コールバック

このメソッドはユーザがINFOという短縮形を使うときに呼び出されます。Inlineコードについての短いレポートが入った文字列を返さなければなりません。

Inlineオブジェクト

Inline.pmは、それが受け取ったInlineされたソースコードのセクション毎にハッシュをベースとしたPerlオブジェクトを生成します。このオブジェクトはコード、環境、使用された構成設定オプションについての多くの情報を持っています。

このオブジェクトは、さらにさいくつかのサブハッシュに分けられるハッシュです。ILSMは使わなければならないのは、$o->{API}と$o->{ILSM}という2つのサブハッシュだけです。前者には、Inlineがあなたの設計したキャッシュされたオブジェクトの作成/ロードするためにInlineが集めてきたすべての情報が入っています。後者はあなたのILSMが後で必要になりそうなデータを自由に格納できる貯蔵庫です。

このセクションはInlineオブジェクトの"API"属性の全てを説明します。

code属性

これは利用者によって渡された実際のソースコードです。これは1つの長い文字列で格納されます。

language属性

使用されている言語の適切な名前

language_id属性

利用者によって指定された言語名。'CPP'の代わりに'C++'かもしれません。

module属性

共有オブジェクトのファイル名。

modfname属性

共有オブジェクトのファイル名

modpname属性

共有オブジェクトのインストール・パスの拡張子

version属性

利用されているInline.pmのバージョン

pkg属性

Inlineを呼び出したPerlパッケージ

install_lib属性

共有オブジェクトを出力するディレクトリ

build_dir属性

その下に構築に関連するファイルを出力するべきディレクトリ

script属性

Inlineを呼び出したスクリプトの名前

location属性

問題になっている実行可能なオブジェクトのフル・パス名

suffix属性

共有ライブラリの拡張子(通常は'so' か 'dll')

Inline名前空間

Inline.pmは、だれもが独自の言語サポート・モジュールを作成することが出来るように設定されています。それは例えばCのような既存のInline言語の別の実装を、さらにだれもが作成することも可能にしています。あなたはそれをCPANで配布することが出来ます。

Inlineモジュールを実装し配布することを計画しているのであれば、Inlineコミュニティで一緒にやってみる気はありませんか。Inlineメーリングリストで連絡を取ることが出来ます: inline@perl.org (参加するためには inline-subscribe@perl.org にメールしてください)。あなたのモジュールを成功させるために必要なアドバイスと支援をうけることができるでしょう。

Inlineコミュニティは、もしあなたのCOBOLの実装を公式なInline::COBOLとして配布するか、他の名前空間を使うべきかを判定するでしょう。その点では、私(Brian Ingerson) が最終的な権限を保有しています。(そして私はそれを使う必要がないことを望んでいます :-) 実際には modules@perl.org が最終的な権限を保有しています。

たとえ一人で仕事したいと思っていても、それは自由ですし、Inline言語サポートモジュールを作成し、CPANで配布することは喜ばれます。それらを異なるパッケージ名で配布することだけが必要です。

参考資料

Inlineについての一般的な情報については、Inlineをご覧ください。

CでInlineを使うことについての情報についてはInline::Cをご覧ください。

サポートされている言語とプラットホームについての情報はInline-Supportをご覧ください。

Inlineのメーリングリストはinline@perl.orgです。

参加するためには、inline-subscribe@perl.orgにメールしてください。

作者(=AUTHOR)

Brian Ingerson <INGY@cpan.org>

著作権(=COPYRIGHT)

Copyright (c) 2000, 2001. Brian Ingerson. All rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See http://www.perl.com/perl/misc/Artistic.html