5. モジュールシステム

モジュールシステムランタイム のサブシステムで、 モジュール を管理する。

moduleモジュールシステム によってロードされる値である。通常、モジュールはひとつの機能グループに属する関数の集合を、変数として持つ。「モジュール」は「mod」と略される。

5.1. モジュール名

モジュール名は次の正規表現パターンを満たさなければならない: ([a-z_][a-z0-9_]*/)*_*[A-Z][A-Z0-9_]*

有効なモジュール名の例:

  • MAIN

  • kink/STR

  • kink/test/TEST

  • org/example/_internal/INTERNAL_MOD

  • com/example/_PRIVATE_MOD

5.2. モジュール基底パス集合

モジュールのソースはファイルシステム上のディレクトリに配置できる。これらディレクトリの一覧を モジュール基底パス集合 と呼ぶ。

モジュール基底パス集合は -p / --path コマンドラインオプションか、 MOD.add_path によって指定される。

5.3. モジュールソースの種別

モジュールのソースには3つの種別がある。

5.3.1. 組み込み部品

ランタイムはモジュールのソースとなる組み込み部品を提供する。これらモジュールは組み込みモジュールと呼ばれる。公開の組み込みモジュールの仕様については、 組み込みライブラリAPI を参照。組み込みモジュールには公開APIに含まれない、非公開のモジュールも存在する。

5.3.2. Kinkプログラムファイル

モジュール基底パス上のKinkプログラムファイルはモジュールのソースになり得る。Kinkプログラムファイルのパスは次の形式でなければならない: {module base path}/{module name}.kn 。ファイルはUTF-8でエンコードされていなければならない。

例として、 org/example/TARAI をモジュール名としよう。また、モジュール基底パス集合は /home/you/kinkmod/usr/share/kinkmod だとしよう。このとき、Kinkプログラムファイルは次のパスに配置できる。

  • /home/you/kinkmod/org/example/TARAI.kn

  • /usr/share/kinkmod/org/example/TARAI.kn

プログラムのトップレベルの 束縛 がモジュールになる。詳細は compile_and_run 疑似手続きを見よ。

次に示す org/example/rat/RAT モジュールのソースのプログラムファイルが、 /home/me/mymods/org/example/rat/RAT.kn にあるとする。

:new <- {(:Numer :Denom)
  new_val(
    ... Rat_trait
    'Numer' Numer
    'Denom' Denom
  )
}

:Rat_trait <- [
  'numer' {[:R]
    R.Numer
  }
  'denom' {[:R]
    R.Denom
  }
  'repr' {[:R]
    '{}/{}'.format(R.numer R.denom)
  }
]

プログラムファイルclient.knが、カレントディレクトリにあるとする。client.knはRATモジュールを使う。

:RAT.require_from('org/example/rat/')

:Rat <- RAT.new(1 3)
stdout.print_line('rat: {}'.format(Rat.repr))

client.knはつぎのように実行できる。

$ kink --path /home/me/mymods client.kn
rat: 1/3

RAT モジュールは単にトップレベルの束縛なので、 Rat_trait など非公開の変数も持っている。単体テストの際には、これら非公開の変数にアクセスするのが有用かもしれない。

5.3.3. データファイル

モジュール基底パス上にある、任意のバイト列のファイルがモジュールのソースになり得る。データファイルのパスは次の形式でなければならない: {module base path}/{module name}.data

バイト列は バイナリ として、モジュールの data 関数を通して提供される。詳細は make_data_mod 疑似手続きを見よ。

データファイルのソースを使うと、任意の形式のデータをモジュールとともに配布できる。たとえば、次のようなUTF-8のテキストファイルが /home/me/mymods/org/example/conversation/_PROMPT_TEMPLATE.data にあるとする。

Your name is {Name}.
You work in {Department}.
Always state your name at the beginning of a conversation.

次に示すプログラムファイル /home/me/mymods/org/example/conversation/TOOL.kn があるとする。

:_PROMPT_TEMPLATE.require_from('org/example/conversation/')
:CHARSET.require_from('kink/charset/')

:main <- {
  :Bin = _PROMPT_TEMPLATE.data
  :Template = CHARSET.utf8.bin_to_str(Bin)
  :Prompt = Template.format{(:C)
    C.named_arg('Name' 'John Doe')
    C.named_arg('Department' 'Support Department')
  }
  stdout.print(Prompt)
}

TOOL モジュールは main 関数を持っているから、 起動可能モジュール だ。TOOLモジュールは次のように実行できる。

$ kink --path /home/me/mymods mod:com/example/conversation/TOOL
Your name is John Doe.
You work in Support Department.
Always state your name at the beginning of a conversation.

5.4. モジュールのロード

モジュールは、モジュール名を指定して、モジュールシステムからロードできる。モジュールのロードは Varref.require_from または MOD.require で実行できる。モジュールのロードは三つの状態で終了する: success, not_found, compile_error

モジュールのロードは、次の疑似コードに示すように行われる:

if not is_associated(Mod_name) {
  Source = find_source(Mod_name) or return not_found

  if Source is a builtin component {
    Mod = make_builtin_mod(Source)
  } elsif Source is a Kink program file {
    Mod = compile_and_run(Source) or return compile_error(Compile_error)
  } else {
    assert Source is a data file
    Mod = make_data_mod(Source)
  }

  associate(Mod_name Mod)
}

return success(get_associated(Mod_name))

5.4.1. is_associated

is_associated 疑似手続きは、指定されたモジュール名に既にモジュールが紐付いているときに真を、そうでないときに偽を戻す。

5.4.2. find_source

find_source 疑似手続きは、 指定されたモジュール名に対応するモジュールのソースを探索する。探索が失敗した場合、モジュールのロードの手順は not_found の状態で終了する。 find_source は次の疑似コードのように定義できる。

def find_source(Mod_name) {
  if builtin component exists for Mod_name {
    return the builtin component
  }

  for base_dir in module_base_paths {
    kn_path = base_dir + '/' + Mod_name + '.kn'
    if file kn_path exists {
      return file kn_path as a Kink program file
    }

    data_path = base_dir + '/' + Mod_name + '.data'
    if file data_path exists {
      return file data_path as a data file
    }
  }

  return not_found
}

5.4.3. make_builtin_mod

make_builtin_mod 疑似手続きは、ランタイムの組み込み部品からモジュールを作る。

5.4.4. compile_and_run

compile_and_run 疑似手続きは、 モジュールのソースをコンパイルし 、コンパイルされたプログラムからモジュールを作る。コンパイルが失敗した場合、モジュールのロードの手順は compile_error の状態で終了する。

compile_and_run は次の疑似Kinkコードのように定義できる。

:BINDING.require_from('kink/')
:CHARSET.require_from('kink/charset/')
:PROGRAM.require_from('kink/porgram/')

:compile_and_run <- {(:Source)
  :Text = CHARSET.utf8.bin_to_str(Source.bin)
  :Program = PROGRAM.new(Source.file_path Text)
  :Mod = BINDING.new
  Mod:repr <- _make_repr
  :run_program = Program.compile{(:C)
    C.binding(Mod)
    C.on_error{(:Compile_error)
      _end_compile_error(Compile_error)
    }
  }
  run_program
  Mod
}

5.4.5. make_data_mod

make_data_mod 疑似手続きは、次の疑似Kinkコードのようにして、バイト列からモジュールを作る。

:make_data_mod <- {(:Source)
  :Mod = new_val
  :Bin = Source.bin
  Mod:data <- {() Bin }
  Mod:repr <- _make_repr
  Mod
}

5.4.6. associate

associate 疑似手続きは、モジュール名が既にモジュールに紐付いているかをテストし、紐付いていない場合、モジュール名を指定されたモジュールに紐付ける。テストと紐付けはアトミック操作として実行される。

5.4.7. get_associated

get_associated 疑似手続きは、モジュール名に紐付いたモジュールを戻す。

5.5. 起動可能モジュール

モジュールが main 関数を持っているとき、そのモジュールはコマンドラインから起動できる。詳細は kink and kinkw commands を見よ。