Oxは、 組み込みシステムで多用される、押しボタンによるマンマシンインターフェースを、効率よく、かつ見やすく記述する言語です。
組み込み用機器の押しボタン は、その数を減らすために、モードの切り替えを行い、同じボタンが状態によって別の意味を持つことが通常行われています。専門的に言えば、このボタン操作 は文脈に依存した言語になっていることになります。
デスクトップマシンならば、YACCなどコンパイラーコンパイラーを利用することにより、こうした言語をパーシングして、処理することができますが、組み込み機器のようにハードウエアリソースが少ない場合、YACCでは、RAM, ROMとも大きな領域を必要とするコードを生成してしまい、使用できないことが多々あります。だからといって、これを普通に、switch, case文を多用して書くと大変見通しが悪く、意外に大きなコードになってしまいます。
Oxは、こうした問題を解決するためC Kermitを記述するのに使われたwartにヒントを得て、この規格を大幅に拡張するとともにより効率的なコードを生成するよう、設計しました。
このプログラムはGPL2に従い使用
Sourceを含むアーカイブをここに置きます
8進数、10進数、16進数を切り替えて入力するパーサを例にします。
Oxプロクラムは、
前置部
%%
本体
%%
後置部
という構成になります。前置部は、定義とCのプログラムの内、位置的に本文が展開される前に書きたいものを書き、後置部は本文が展開された後に書きたいプログラムを書きます。早速本文を見ながら説明します。
#include <stdio.h>
static int reg = 0;
/* %name は本体の関数に付けられる名前を指定します。 */
%name base
/* %header は、oxが使う定義を外部でも使いたい場合に生成させるfile名です。 */
%header base.h
/* %states %は、
states dec hex oct
/* %start はプログラム開始時の初期状態を指定します。 */
%start dec
/* %defineはCの#defineと同様、識別子に値を割り当てます。 */
%define K_0 = 0
%define K_1 = 1
%define K_2 = 2
%define K_3 = 3
%define K_4 = 4
%define K_5 = 5
%define K_6 = 6
%define K_7 = 7
%define K_8 = 8
%define K_9 = 9
/* %defineをネストしても構いません。N_DECは、0123456789と同値になります。
また、[^1]は1以外の値を示します。 */
%define N_DEC = ([K_0][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9])
%define N_OCT = ([K_0][K_1][K_2][K_3][K_4][K_5][K_6][K_7])
%define K_a = a
%define K_b = b
%define K_c = c
%define K_d = d
%define K_e = e
%define K_f = f
%define N_HEX = ([K_a][K_b][K_c][K_d][K_e][K_f])
%define K_DEC = D
%define K_HEX = H
%define K_OCT = O
%define K_BASE = ([K_DEC][K_HEX][K_OCT])
%define K_RET = \n
%define K_CLR = C
%define K_QUIT = q
/* 関数 input()はoxコードに値を入力するための必須関数です。
* ここはまだ前置部なので、一般のCの書式で1文字入力する関数を書きます。
* 組み込み機で押しボタンを読み込むときもascii文字を読んだように適当に変換してください
* キーコードの範囲が小さいほうがコードが小さくなります
*/
int input(void)
{
int i;
if ((i = getchar()) == EOF) {
exit(0);
}
else {
return(i);
}
}
/* 前置部はここまでです。%%から後ろは本体となります。 */
%%
*/<dec>[N_DEC] {
/* 「状態”dec”で入力01234567890のいずれかの入力があったら」と言う意味になります。*/
reg *= 10;
reg += (c - '0');
/* cは予約された変数でinput()で入力された値を保持しています。 */
}
<hex> [K_a][K_b][K_c][K_d][K_e][K_f] {
reg *= 16;
reg += (c - ('a'-10));
}
<hex>[N_DEC] {
reg *= 16;
reg += (c - '0');
}
<oct>[N_OCT] {
reg *= 8;
reg += (c - '0');
}
<dec,hex,oct>[K_CLR] {
/* 状態dec,hex,octいずれかでCがおされたらという意味になります。
<dec>[K_CLR] | <dec>[K_CLR] | <dec>[K_CLR] と同値になります。 */
reg = 0;
}
<dec,hex,oct>[K_BASE] {
switch (c) {
case 'D':
BEGIN(dec);
/* BEGIN(状態)で、指定した状態に遷移します。 */
break;
case 'H':
BEGIN(hex);
break;
case 'O':
BEGIN(oct);
break;
}
}
<dec,hex,oct>[K_RET] {
fprintf(stderr, "input = %d, H'%x, O'%o\n", reg, reg, reg);
reg = 0;
}
<dec,hex,oct>[K_QUIT] {
fprintf(stderr, "bye !\n");
exit(0);
}
/* 本体はここまでです。%%の後は後置部となります。 */
%%
/* この例題の後置部は単にoxが生成するbase()を呼ぶだけです。*/
void
KeyFunc()
{
base();
}
これでプログラムは終わりです。この関数を呼んで動作を確認するため、basemain.cが、sample holderにあります。
ox base.x base.c
gcc basemain.c base.c -o base
として、PC上で動作確認が出来ます
$Id: Ox.html,v 1.1 2005/01/11 01:32:00 mac Exp $