6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

言語実装Advent Calendar 2021

Day 21

Kinx Update 2021(パッケージ管理サポートなど)

Last updated at Posted at 2021-12-20

はじめに

この記事は「言語実装 Advent Calendar 2021」 21日目の記事です。本記事は、2019 年末(GitHub への初コミットは 2019/11/28)頃から始めた Kinx という自作言語のアップデートの話題になります。

御多分に洩れず、本業の忙しさによりアップデートは細々と行っておりますが、太く短くではなく 細く長く 続けていこうと思っておりますので、今後ともよろしくお願いいたします。話題としては以下の構成になります。

  1. Kinx 2021年総括
  • まずは Kinx 開発や主な活動に関して 2021 年を総括したいと思います。V1.0.0 を 3/16 にリリースし、Kinx パッケージをサポートした V1.1.0 をつい先日リリース(v1.1.0 Release)しました。
  1. Kinx アップデート状況
  • 言語処理系そのもののアップデートの総括です。1年という期間ですので、細々とですが色々とアップデートはしました。大きくはパッケージ・システムをサポートしました。使い方をほとんど説明していない(日本語で)ので、今のところおそらく私しか使えません。
  1. Kinx パッケージの作り方・使い方
  • 英語版は「ここ(パッケージ概要)」と「ここ(kipコマンド)」にありますが、使い方を日本語で説明していないのもアレなので、ちょっとだけ作り方と使い方を。これを読んだ誰かが何か試してくれるかも... という期待を込めて。恐らくバグもたくさん見つかるでしょう。そして、それこそが OSS のあるべき姿 ですね。
  1. パッケージご紹介
    • 一先ず最初の 1 歩ということでサポートしてみたパッケージのご紹介です。お試しパッケージを 2 つ紹介します。
  2. Kinx ドキュメント執筆開始
  • Kinx をまとめてみようと思い、書籍の形式での文章を執筆し始めました。しかしながら、ドキュメント書きはご存知の通り一番時間がかかる作業です。したがってまだ完了していません。ので、ご紹介のみ。

【ご参考】Kinx

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。ご存じない方は下記をご参照ください。

1. Kinx 2021年総括

全体を通して

2021年は前半は割と頑張ったところもありましたが、後半はやや低調でした。主に本業で激動があったせいですが、まあそういうことはままあると思うので、できるペースで今後も継続していきます。

GitHub Star 推移

さて、まずは GitHub でのスター数推移です。下記の図が推移を示していますが、2020年12月31日時点で 84 だったスター数は 2021年11月10日現在で 212 となっており、この 1 年で約 2.5 倍になりました。特に 1/16 に 92 → 114 と 22 個のスターが付いて大幅にアップし、一気に 100 の大台を超えました。後で色々確認してみると、この日に Hacker News で Kinx を紹介してくださった方がおり、一気に閲覧数が増えたのが要因です。

xx.png

おかげさまでその後もじわじわと伸び続けており、200 の大台も今年中にクリアすることができました。ありがとうございました。

2022年の目標

2022年の目標は、まずは「使ってもらうこと」ですね。大きな目標としては GitHub でシンタックス・ハイライト してもらえて、一つの言語として 名前を認識してもらうこと なのですが、そのためには 「数百の(数多くの)」 リポジトリで使用されている実績が必要とのことです。いやぁハードル高いです。

色々と酔狂な方がいらっしゃれば、ぜひ何らかのリポジトリを作って使ってみていただければ幸いです。

2. Kinx アップデート状況

主なアップデート

主なアップデートをピックアップしてみました。以下の通りです。

  • デバッガのサポート
  • switch-when のサポート
  • case-when のサポート
  • 分割代入とパターンマッチ構文による代入のサポート
  • パイプライン演算子と関数合成演算子のサポート
  • SAT ソルバーのサポート
  • Kinx パッケージのサポート
  • その他バグフィックス

それでは一つ一つ軽くご紹介いたします。

デバッガ

簡単なデバッガをサポートしました。以下のような画面になります。--debug でデバッガ機能を使えようになります。使い方に関しては「ここ(英語)」 を参照してみてください。

debugger

技術的なポイントを 1 つだけ。実は、デバッガでブレークポイントを仕込めるのですが、この処理を通常の VM 動作で実行させてしまうとパフォーマンスが急激に落ちます。そこで、デバッガ用 VM と通常用 VM は別にしています(といってもマクロで切り替えて別の関数として 2 つの実装が作られるようになっているだけですが)。しかし、この関数のコンパイルが Windows だと非常に遅いため、Windows 環境でのコンパイル時間がさらに長くなりました。。。Linux でのビルドは速いのですが。

switch-when

switch-when をサポートしました。

とだけ言うと何のことやらと思いますが、まず元々 switch-case がサポートされています。これが Fall Through で動作することに対して昔から賛否ありました(Kinx ではなく一般的な話として)。そして、Kinx では、switch-caseFall Through で動作します。これが「C like syntax」の精神から出てくる挙動です。C プログラマが switch-case 文を見た時、当然 Fall Through するだろう と認識することが期待されるからです。これが Fall Through でなければ逆に混乱するでしょう。なぜなら、break が無かったら次の文に自然に続きそうに見えるから です。

一方で、Fall Through ではない動作が求められる背景も理解できます。

そこで、構文を変えることで Fall Through ではない動作をさせる ことにしました。case 文の代わりに when 文を使えば、when 文の最後のステートメントの後で自動的に break します。

代わりに、fallthrough というキーワードを用意してあり、when 文で Fall Through 動作をさせたい場合は、任意の場所で fallthourgh キーワードを挿入します。ただし、これは break とは異なり静的に決定されるため、動的に Fall Through かどうかを決定するコードを書くことはできません。when 文のどこかで fallthrough キーワードを書くと、case を使ったのと同じになります。

もう一つ、default に代わるキーワードは otherwise になります。言い換えれば、otherwise は自動的に break する default です。以下がサンプルです。

function test(a) {
    var r;
    switch (a) {
    otherwise: // break するので when 1: にフォールスルーはしない。
    when 1: r = a * 2;
    when 2: r = a * 4;
    when 3: r = a * 8;
    when 4: r = a * 16;
    }
    return r ?? -1;
}
0.upto(8, &(n) => System.println("%d => %3d" % n % test(n)));
0 =>  -1
1 =>   2
2 =>   8
3 =>  24
4 =>  64
5 =>  -1
6 =>  -1
7 =>  -1
8 =>  -1

なお、switch 文の中で casewhen は混在可能です。ただし、紛らわしいのでどちらかに統一したほうが良いとは思います。

case-when

case-when もサポートしました。実は switch-when よりも先に実装しました。case-when は文ではなく であり、評価に応じた値を持ちます。case-when に関しては(switch-case と後述の分割代入あたりの説明も一緒に)以下に詳しく説明していますのでご参照ください。

色々できるので、以下のサンプルコード(テストコード)を参照すると理解が深まるかもしれません。

分割代入とパターンマッチ代入

上記に書いた通り、分割代入とパターンマッチ代入をサポートしました。上記の記事を読むと分かるのですが、順番的に「分割代入とパターンマッチ代入」→「case-when」→「switch-when」の順で実装していますが説明の順が逆になってますね。「分割代入とパターンマッチ代入」に関しても、上記記事をご参照ください。

パイプライン演算子と関数合成演算子

パイプライン演算子と関数合成演算子をサポートしました。元々は JavaScript で議論されているものですが、早々に取り入れました。最終的には JavaScript 側は違う構文になっているかもしれませんが、元々完全互換でもないので特に問題はないでしょう。こんなことできます。

まずはパイプライン演算子です。

function doubleSay(str) {
    return "%{str}, %{str}";
}
function capitalize(str) {
    return str.toUpper(0, 1);
}
function exclaim(str) {
    return str + '!';
}

var result = exclaim(capitalize(doubleSay("hello")));
System.println(result); // => "Hello, hello!"

var result = "hello"
  |> doubleSay
  |> capitalize
  |> exclaim;

System.println(result); // => "Hello, hello!"

関数合成演算子を使うとこんな感じになります。

const double = &(n) => n * 2;
const increment = &(n) => n + 1;

// Normal case.
var r1 = double(increment(double(double(5)))); // 42
System.println(r1);

// By the function composition operator, it creates a new function.
var f = double +> double +> increment +> double;
var r2 = 5 |> f;
System.println(r2); // 42

SAT ソルバー

SAT ソルバーなるものをサポートしてみました。数独とか解けます。以下の記事をご参照ください。

解いてみた数独の回答例です。ピンク色の数字が、元々の問題の数字を表しています。

数独解読結果例

Kinx パッケージ

V1.1.0 で Kinx パッケージをサポートしました。今年の本命 です。まだ準備は完ぺきではありませんが、リリースこそが善 だという気合いでリリースさせました。どうぞバグでも見つけてやってください。

他の場所では全くまだ説明していないのですが、この機会に次の章で軽く説明します。

その他バグフィクス

2021年に直したバグは全部で 29 件(11/22現在)。色々ありました。現在は全て GitHub の Issues で管理していますので、何かありましたらここに Issue 登録していただければ感謝いたします。

3. Kinx パッケージの使い方/作り方

さて、今年の目玉であるパッケージ管理についてです。とは言っても、つい先日正式にリリースさせたのと、あまり使い方を説明していないので、ここで簡単に説明してみることにいたします。簡単な説明は以下にあります(英文)。

Kinx パッケージは GitHub リポジトリで管理・配布

まず、Kinx パッケージの特長として、GitHub に依存します。パッケージのリポジトリは GitHub リポジトリそのものです。パッケージ・リポジトリの Release モジュールを使ってダウンロードし、インストールしますので、GitHub でリリースすることが所謂「パッケージの公開」になります。このパッケージのリポジトリを パッケージ・リポジトリ と呼びます。このパッケージ・リポジトリを管理するためのリポジトリとして、中央リポジトリ が存在します。

デフォルト中央リポジトリ

Kinx パッケージは中央リポジトリ方式です。デフォルトでは以下の中央リポジトリが登録されていますが、個人で追加の中央リポジトリを作成することも可能です。ここでは中央リポジトリの作り方には触れませんが、いずれ説明の機会を設けたいと思います。

Kinx パッケージの使い方(kip コマンド)

パッケージをコントロールするには kip というコマンドを使用します。なぜ kip か? そこには大きな理由が ... あるはずもなく、あまりいい名前が思いつかなかったため、Python のパッケージ管理コマンド pip にあやかって kip と名付けた、といった程度の理由になります。

さて kip コマンドの使い方ですが、まずは -h で表示されます。ここでは理解を容易にするために、全てを説明するのではなく、必要な主要なコマンドのみ説明することにしますのでご了承ください。

$ kip -h
Options
    -h                        Display this help.
    -v                        Verbose mode.

Configuration
    config set <name> <value> Sets a value to a name.
    config remove <name>      Removes a name and a value.
    config show [<name>]      Lists up a name and a value.

Central Repository Access
    repo add <repo>           Adds a central repository.
    repo remove <repo>        Removes a central repository.
    repo list                 Lists up a central repository.

Package Control
    search <key>              Searches a package.
    install <key> [<ver>]     Installs a specified package.
    uninstall <key> <ver>|all Uninstalls a specified package.
    devinst                   Installs a develpment package.
    devuninst                 Uninstalls a develpment package.
    list                      Lists up installed packages.
    init <key>                Creates and initializes a package.
    help <key> [<ver>]        Shows README.md of a package.

パッケージの検索

現時点では、パッケージのリストアップしかできません。今はまだこれで十分ですが、将来的にはパッケージ数を増やしていきたいので、パターンマッチなどでの検索・フィルタリング機能を追加したいと思っています。コマンドラインとしては kip search コマンドを使用します。2021/12/21 時点では以下のように表示されます。既にインストールされているパッケージは - Installed の表示もされます。

$ kip search
* kacc
    - 0.0.1 (148.6 KB) [Pre-release] - Installed
* typesetting
    - 0.0.1 ( 83.4 MB) [Pre-release] - Installed
    - 0.0.0 ( 83.3 MB) [Pre-release]

このとき、上記で表示される kacctypesetting がパッケージ名(キー <key>)となります。この名前は全てのパッケージでユニークであり、重複させることはできません。

インストールパッケージの確認

上記では全てのパッケージが表示されますが、インストールされているパッケージを確認する目的には kip list コマンドを使用します。

$ kip list
kacc: [0.0.1]
typesetting: [0.0.1]

複数のバージョンを共存させることが可能です。その場合、利用する側でバージョンを指定している場合はそのバージョンのパッケージが利用され、指定されていない場合はデフォルト有効バージョンが使われます。デフォルト有効バージョンは基本的に一番新しい版数のバージョンを示します。

パッケージのインストール

パッケージをインストールするには kip install <key> [<ver>] コマンドを使います。<ver> を省略すると、最新版数をインストールします。

$ kip install typesetting
[2021/12/05 11:35:31] Downloading the package typesetting(0.0.1) with 87423875 bytes.
[2021/12/05 11:35:31] from https://github.com/Kray-G/kinx-tiny-typesetting/releases/download/v0.0.1/package.zip
[2021/12/05 11:36:08] Received 87423875/87423875 (100%)
[2021/12/05 11:36:08] Created C:\Users\***********\AppData\Local\Temp/typesetting0.0.1.zip done.
[2021/12/05 11:36:11] Extracted 268/268 (100%)
[2021/12/05 11:36:11] Set the latest version of typesetting to 0.0.1
[2021/12/05 11:36:11] Made the file of phantomjs executable
[2021/12/05 11:36:11] Generated a command of kxkitty.exe

パッケージのアンインストール

パッケージのアンインストールは kip uninstall uninstall <key> <ver>|all コマンドを使います。アンインストールの場合、<ver> を省略することはできません。全てをアンインストールする場合は明示的に all を指定します。

$ kip uninstall typesetting
[2021/12/05 11:35:23] No version for typesetting specified
[2021/12/05 11:35:23] If you want to uninstall all versions, specify 'all'
$ kip uninstall typesetting all
[2021/12/05 11:35:26] Removed an executable of kxkitty.exe
[2021/12/05 11:35:26] All versions of typesetting is successfully uninstalled

パッケージ内のファイルの参照方法

パッケージのライブラリを参照するには、使う側で以下のように using 指定を行います。この時、パッケージの lib フォルダが参照の際のルート・ディレクトリになります。

using @packagekey.LibFile;          // 最新バージョンの参照
using @packagekey(0.0.1).LibFile;   // バージョン指定する場合

現在提供しているパッケージでも、Kacc (https://github.com/Kray-G/kacc) ではレキサ(字句解析器)を使用するのに以下のように指定させています。

using @kacc.Lexer;

Kinx パッケージの作り方(GitHub リポジトリ)

さて、ここからはパッケージの作り方です。パッケージを作る作業は大まかに以下に分解されます。

  1. パッケージ・リポジトリを作成する。
  2. 開発用インストールを行い、ローカルで動作検証を行う。
  3. パッケージ・リポジトリでリリースする。
  4. 中央リポジトリに登録する。

一旦中央リポジトリに登録できれば、あとはパッケージ・リポジトリ側でバージョンを上げてリリースすることで自動的に最新バージョンを更新することができます。

バージョンの付け方

バージョンは x.y.z の形式です。基本的にはセマンティック・バージョニングに準しています。なお、開発用のバージョンは自動的に 99.99.99 と認識されます。

パッケージ・リポジトリの作成

ボイラープレート作成

パッケージ・リポジトリは、GitHub リポジトリです。従って、通常の GitHub リポジトリをまず作成します。その後、リポジトリ・フォルダ内で kip init <key> コマンドを使用します。例えば、パッケージのキー名を pacname とした場合、以下のように実行します。

$ kip init pacname
[2021/12/05 11:13:42] Created the directory of 'docs'.
[2021/12/05 11:13:42] Created a default README file of 'docs/README.md'.
[2021/12/05 11:13:42] Created the directory of 'src/bin'.
[2021/12/05 11:13:42] Created a hook script file of 'src/bin/pacname.kx'.
[2021/12/05 11:13:42] Created the directory of 'src/lib'.
[2021/12/05 11:13:42] Created an initial library file of 'src/lib/Pacname.kx'.
[2021/12/05 11:13:42] Created the directory of 'src/etc'.
[2021/12/05 11:13:42] Created an initial setting file of 'src/etc/pacname.json'.
[2021/12/05 11:13:42] Created the directory of '.github/workflows'.
[2021/12/05 11:13:42] Created a default CI file of '.github/workflows/main.yml'.

<key> に指定する名前があらかじめ設定された package.json ファイルを作成します。

package.json
{
    "name": "pacname"
}

また、以下の各種テンプレート・ファイルが作成されます。

  • docs/README.md ファイルが作成されます。
  • src/bin/packname.kx というファイルを作成し、パッケージの実行ファイルのためのスクリプト・ファイルが作成されます。
    • 実行ファイルが不要なプロジェクト(ライブラリ提供など)の場合、不要であれば削除してください。
  • src/lib/Packname.kx というファイルを作成し、パッケージのライブラリ・ファイルとして利用できるようにします。
    • 特に決まりはありませんが、ライブラリとして提供する場合、パッケージ・キーと同じだと分かりやすいとの理由です。デフォルトでは中身は空で作成されます。
  • src/etc/packname.json というファイルを作成し、パッケージの設定ファイルとして利用できるようにします。
    • デフォルトでは空の Json オブジェクトが定義されています。
  • .github/workflows/main.yml という GitHub Action のデフォルトワークフロー・ファイルが作成されます。
    • vX.Y.Z というバージョン番号に則したタグを打つと、vX.Y.Z というリリースページを作成し、自動的に package.zip ファイルを作成してアップロードします。必要に応じて [Publish] するだけでリリースパッケージが公開できるようになります。

全体として、以下のようなフォルダツリーが作成されます。

.
│  package.json
├─.git
├─.github
│  └─workflows
│          main.yml
├─docs
│      README.md
└─src
    ├─bin
    │      pacname.kx
    ├─lib
    │      Pacname.kx
    └─etc
            pacname.json
リポジトリ構成とインストール時の構成の対応

リポジトリ上での構成とインストール後のディレクトリ構成の対応を以下に示します。

リポジトリ上の構成 インストール先
src/bin packagekey/ver/bin typesetting/0.0.1/bin
src/lib packagekey/ver/lib typesetting/0.0.1/lib
src/etc packagekey/ver/etc typesetting/0.0.1/etc
docs packagekey/ver/docs typesetting/0.0.1/docs

上記の 「リポジトリ上の構成」 の形でリポジトリ上にディレクトリを作成して配置してください。

src 配下

src 配下に以下のディレクトリを作成します。これは、基本的にはこのままの形でインストールされます。

  • bin ... 主に実行ファイル。後述する「特別な配慮」がなされます。
  • lib ... 主にライブラリファイル。ライブラリとして使われるファイルのルートになります。
  • etc ... 主に設定ファイルや必要な環境ファイルなど。
src/bin 配下における「特別な配慮」

展開した後のディレクトリ構成で、bin 配下は以下の特別な配慮がなされます。

  • bin ディレクトリ直下の xxx.kx ファイルは、Windows の場合 xxx.exe が、Linux の場合 xxx という executable なモジュールが作成され、セットアップされます。
    • 例えば、kxkitty.kx の場合、Windows であれば kxkitty.exe が作られ、Linux では kxkitty という実行コマンドが自動的に作成されます。
    • ここで作成されたモジュールは、アンインストール時に不要となった場合、自動的に削除されます。
  • Linux の場合、bin ディレクトリ配下(直下でなくても良い)の実行モジュール(ELF ファイル、.sh ファイル)は、全て自動的に実行可能フラグを付けることで実際に実行可能な状態にセットアップします。
docs 配下

主にドキュメントを配置します。docs/REAMDME.md ファイルは特別で、kip help <key> コマンドでコンソールに内容が表示される内容となります。Markdown ですが、コンソール用に簡易整形されます。

開発用インストールとデバッグ

開発用のインストールとアンインストールは kip devinst および kip devunint コマンドです。パッケージ・リポジトリのルートでこのコマンドを実行する必要がありますが、代わりにパッケージ名(キー)を指定する必要はありません。

例えば kacc パッケージのリポジトリ・ルートで実行すると以下のように出力されます。開発用バージョンは 99.99.99 で固定です。

$ kip devinst
[2021/12/05 11:55:46] $PKGPATH: /usr/bin/kinxlib/package/kacc/99.99.99
[2021/12/05 11:55:46] Copying a directory : bin
[2021/12/05 11:55:46] Copying a directory : bin/exec
[2021/12/05 11:55:46] Copying a file : bin/exec/kmyacc
[2021/12/05 11:55:46] Copying a file : bin/exec/kmyacc.exe
[2021/12/05 11:55:47] Copying a file : bin/kacc.kx
[2021/12/05 11:55:47] Copying a directory : etc
[2021/12/05 11:55:47] Copying a file : etc/kmyacc.kx.parser
[2021/12/05 11:55:47] Copying a directory : example
[2021/12/05 11:55:47] Copying a file : example/calc.y
[2021/12/05 11:55:47] Copying a file : example/calcjit.y
[2021/12/05 11:55:47] Copying a directory : lib
[2021/12/05 11:55:47] Copying a file : lib/Lexer.kx
[2021/12/05 11:55:47] Copying a directory : docs
[2021/12/05 11:55:47] Copying a file : docs/Lexer.md
[2021/12/05 11:55:47] Copying a file : docs/README.md
[2021/12/05 11:55:47] Made the file of kmyacc executable
[2021/12/05 11:55:47] Generated a command of kacc.exe

インストールパッケージを確認すると、以下のように <development> 表示が追加されます。

$ kip list
kacc: [0.0.1, <development>]
typesetting: [0.0.1]

インストールと同様に、パッケージ・リポジトリのルートで kip devuninst とするとアンインストールできます。

パッケージ・リポジトリでのリリース

リリースも、通常の GitHub 上でのリリースと同様です。リリースページを作成します。その際のルールは以下の通りです。

  • バージョン番号は v0.0.1 といった形で vX.Y.Z とする。
  • Asset として package.zip という名前の ZIP ファイルを登録する。

例えば、kacc のバージョン 0.0.1 のリリースページは以下のようになっています。バージョン番号と package.zip があることを確認してください。

なお、最初に kip init <key> コマンドでボイラープレートを作成した場合、vX.Y.Z の名前でタグをコミットして push すると、自動的に上記のようなページを作成してくれるよう事前に Workflow が定義されています。そのため、必要に応じて [Publish] するだけでパッケージが公開できますので、ぜひご活用ください。

中央リポジトリへの登録

中央リポジトリへの登録は、現在自動的には行えません。以下の内容で GitHub の Issue からリクエストをお願いします。

こちらで確認でき次第、中央リポジトリをアップデートします。

4. パッケージご紹介

現在、サンプルのパッケージとして以下の 2 つのパッケージがあります。

5. Kinx ドキュメント執筆開始

最後に、Kinx を 1 冊にまとめたドキュメントを書いています。これは残念ながらまだ完成していません。ちょっとずつ完成させていきたいと思っています。ドキュメントは以下のリポジトリで作成しています(日英の両方を同時進行で作ってるので割と辛い...)。

本リポジトリは、今後 Kinx 関連のドキュメントを書く際の管理リポジトリとする予定です。その第一弾が「プログラミング言語 Kinx(The Programming Language Kinx)」になります。

おわりに

色々とペースダウンしてますが、まぁこういった作業には波がありますね。Kinx 自体はまだ続きます。もしご興味があるようであれば、ぜひ Kinx を使ったリポジトリを作って何か試してみていただいたり、軽く遊んだりしていただければ光栄です。

以上、ここまで読んでいただき、ありがとうございました。

6
0
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
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?