view paper/chapter/02-perl.tex @ 9:6e33e74d378f

...
author anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp>
date Tue, 26 Jan 2021 21:00:50 +0900
parents 4eb9702d1ed7
children 86c42a4c8a0d
line wrap: on
line source

\chapter{GearsOSのトランスコンパイラ}

GearsOSはCbCで実装を行う。
CbCはC言語よりアセンブラに近い言語であるため、 すべてを純粋なCbCで記述しようとすると記述量が膨大になってしまう。
またノーマルレベルの計算とメタレベルの計算を、全てプログラマが記述する必要が発生してしまう。
メタ計算では値の取り出しなどを行うが、 これはノーマルレベルのCodeGearのAPIが決まれば一意に決定される。
したがってノーマルレベルのみ記述すれば、 機械的にメタ部分の処理は概ね生成可能となる。
また、メタレベルのみ切り替えたいなどの状況が存在する。
ノーマルレベル、メタレベル共に同じコードの場合は記述の変更量が膨大であるが、 メタレベルの作成を分離するとこの問題は解消される。

GearsOSではメタレベルの処理の作成にPerlスクリプトを用いており、 ノーマルレベルで記述されたCbCから、 メタ部分を含むCbCへと変換する。
変換前のCbCをGearsCbCと呼ぶ。

\section{トランスコンパイラ}
プログラミング言語から実行可能ファイルやアセンブラを生成する処理系のことを、一般的にコンパイラと呼ぶ。
特定のプログラミング言語から別のプログラミング言語に変換するコンパイラのことを、 トランスコンパイラと呼ぶ。
トランスコンパイラとしてはJavaScriptを古い規格のJavaScriptに変換するBabel\cite{babel}がある。

またトランスコンパイラは、変換先の言語を拡張した言語の実装としても使われる。
JavaScriptに強い型制約をつけた拡張言語であるTypeScriptは、 TypeScriptから純粋なJavaScriptに変換を行うトランスコンパイラである。
すべてのTypeScriptのコードはJavaScriptにコンパイル可能である。
JavaScriptに静的型の機能を取り込みたい場合に使われる言語であり、 JavaScriptの上位の言語と言える。

GearsOSはCbCを拡張した言語となっている。
ただしこの拡張自体はCbCコンパイラであるgcc、 llvm/clangには搭載されていない。
その為GearsOSの拡張部分を、等価な純粋なCbCの記述に変換する必要がある。
現在のGearsOSでは、 CMakeによるコンパイル時にPerlで記述された\texttt{generate\_stub.pl}と\texttt{generate\_context.pl}の2種類のスクリプトで変換される。


\section{GearsCbCのInterfaceの実装時の問題}

Interfaceとそれを実装するImplの型が決定すると、最低限満たすべきCodeGearのAPIは一意に決定する。
ここで満たすべきCodeGearは、Interfaceで定義したCodeGearと、 Impl側で定義した privateなCodeGearとなる。
例えばStack Interfaceの実装を考えると、各Implで\texttt{pop}, push, shift, isEmptyなどを実装する必要がある。

従来はプログラマが手作業でヘッダーファイルの定義を参照しながら\texttt{.cbc}ファイルを作成していた。
手作業での実装のため、 コンパイル時に次のような問題点が多発した。

\begin{itemize}
  \item CodeGearの入力のフォーマットの不一致
  \item Interfaceの実装のCodeGearの命名規則の不一致
  \item 実装を忘れているCodeGearの発生
\end{itemize}


特にGearsOSの場合はPerlスクリプトによって純粋なCbCに一度変換されてからコンパイルが行われる。
実装の状況とトランスコンパイラの組み合わせによっては、 CbCコンパイラレベルでコンパイルエラーを発生させないケースがある。
この場合は実際に動作させながら、gdb, lldbなどのCデバッガを用いてデバッグをする必要がある。
またCbCコンパイラレベルで検知できても、すでに変換されたコード側でエラーが出てしまうので、トランスコンパイラの挙動をトレースしながらデバッグをする必要がある。
Interfaceの実装が不十分であることのエラーは、 GearsOSレベル、最低でもCbCコンパイラのレベルで完全に検知したい。

\section{Interfaceを満たすコード生成の他言語の対応状況}

Interfaceを機能として所持している言語の場合、これらはコンパイルレベルか実行時レベルで検知される。
例えばJavaの場合はInterfaceを満たしていない場合はコンパイルエラーになる。


InterfaceのAPIを完全に実装するのを促す仕組みとして、Interfceの定義からエディタやツールが満たすべき関数と引数の組を自動生成するツールがある。

Javaでは様々な手法でこのツールを実装している。
Microsoftが提唱しているIDEとプログラミング言語のコンパイラをつなぐプロトコルにLanguage Serverがある。
Language Serverはコーディング中のソースコードをコンパイラ自身でパースし、 型推論やエラーの内容などをIDE側に通知するプロトコルである。
主要なJavaのLanguage Serverの実装であるeclipse.jdt.ls\cite{eclipse.jdt.ls}では、 LanguageServerの機能として未実装のメソッドを検知する機能が実装されている。\cite{eclipse.jdt.pull322}
この機能を応用してvscode上から未実装のメソッドを特定し、 雛形を生成する機能がある。
他にもIntelliJ IDEなどの商用IDEでは、 IDEが独自に未実装のメソッドを検知、雛形を生成する機能を実装している。


golangの場合は主に\texttt{josharian/impl}\cite{golang_impl}が使われている。
これはインストールすると\texttt{impl}コマンドが使用可能になり、 実装したいInterfaceの型と、 Interfaceを実装するImplの型(レシーバ)を与えることで雛形が生成される。
主要なエディタであるvscodeのgolangの公式パッケージである\texttt{vscode-go}\cite{vscode-go}でも導入されており、 vscodeから呼び出すことが可能である。
vscode以外にもvimなどのエディタから呼び出すことや、 シェル上で呼び出して標準出力の結果を利用することが可能である。

\section{GearsOSでのInterfaceを満たすCbCの雛形生成}


GearsOSでも同様のプログラマ支援ツールを導入したい。
LanguageServerの導入も考えられるが、 今回の場合はC言語のLanguageServerをCbC用にまず改良し、 さらにGearsOS用に書き換える必要がある。
現状のGearsOSが持つシンタックスはCbCのシンタックスを拡張しているものではあるが、これはCbCコンパイラ側には組み込まれていない。
LanguageServerをGearsOSに対応する場合、 CbCコンパイラ側にGearsOSの拡張シンタックスを導入する必要がある。
CbCコンパイラ側への機能の実装は、 比較的難易度が高いと考えらる。
CbCコンパイラ側に手をつけず、 Interfaceの入出力の検査は既存のGearsOSのビルドシステム上に組み込みたい。

対してgolangの\texttt{impl}コマンドのように、 シェルから呼び出し標準出力に結果を書き込む形式も考えられる。
この場合は実装が比較的容易かつ、 コマンドを呼び出して標準出力の結果を使えるシェルやエディタなどの各プラットフォームで使用可能となる。
先行事例を参考に、コマンドを実行して雛形ファイルを生成するスクリプトをGearsOSに導入した。


Interfaceでは入力の引数がImplと揃っている必要があるが、 第一引数は実装自身のインスタンスがくる制約となっている。
実装自身の型は、Interface定義時には不定である。
その為、 GearsOSではInterfaceのAPIの宣言時にデフォルト型変数\texttt{Impl}を実装の型として利用する。
デフォルト型\texttt{Impl}を各実装の型に置換することで自動生成が可能となる。


実装すべきCodeGearはInterfaceとImpl側の型を見れば定義されている。
\texttt{\_\_code}で宣言されているものを逐次生成すればよいが、 継続として呼び出されるCodeGearは具体的な実装を持たない。
GearsOSで使われているInterfaceには概ね次の継続である\texttt{next}が登録されている。
\texttt{next}そのものはInterfaceを呼び出す際に、入力として与える。
その為各Interfaceに入力として与えられた\texttt{next}を保存する場所は存在するが、 nextそのものの独自実装は各Interfaceは所持しない。
したがってこれをInterfaceの実装側で明示的に実装することはできない。
雛形生成の際に、入力として与えられるCodeGearを生成してしまうと、プログラマに混乱をもたらしてしまう。

入力として与えられているCodeGearは、Interfaceに定義されているCodeGearの引数として表現されている。
コードに示す例では、\texttt{whenEmpty}は入力して与えられているCodeGearである。
雛形を生成する場合は、入力として与えられたCodeGearを除外して出力を行う。


雛形生成では他にコンストラクタの生成も行う。
GearsOSのInterfaceのコンストラクタは、 メモリの確保及び各変数の初期化を行う。
メモリ上に確保するのは主にInterfaceとImplのそれぞれが基本となっている。
Interfaceによっては別のDataGearを内包しているものがある。
その場合は別のDataGearの初期化もコンストラクタ内で行う必要があるが、 自動生成コマンドではそこまでの解析は行わない。


コンストラクタはデフォルトでは変数は0、またはNULLで初期化される。
このスクリプトで生成されたコンストラクタは\texttt{generate\_stub.pl}内でも自動的に生成することが可能である。
そのため、 オーバーライドする必要がない場合は削除しても問題なく動作するようになっている。