6. Text-Cの拡張構文と詳細
walk文
walk文で、テキストの先頭段落から末尾までを段落毎に処理するプログラムが簡単に記述できます。
walk(text) {
printf("%s\n",text);// 段落毎の処理
}
for文では、次の記述になります。
for (jumptop(text);!iscureof(text);jumpnextpara(text)) {
printf("%s\n",text);// 段落毎の処理
}
textにビューがある場合は、選択段落を処理する記述ができます。非選択の場合は、カーソル行の段落に適用されます。
walk(text;select) {
printf("%s\n",text);// 選択段落の処理
}
walkのブロックでは、forと同じくbreakとcontinue文が使えます。break文で、walkのブロックを抜けます。continue文で、次の段落の処理へ進みます。
walk文では、ビューハンドルの指定も可能です。
walk(text.v) {...}
文字単位で処理する記述も可能です。
walk(text;char) {
int c = getcurchar(text);
}
※walkのブロックでは、カーソル位置を移動しないでください。場合によっては無限ループになります。
※walk文には副作用があり、プログラムが意図通りに動かない場合があります。
このときはfor文で、for(jumptop(text);!iscureof(text);jumpnextpara(text)) {...}と記述してください。
※walkのブロックでは、return文やgoto文には対応していませんので、記述できません。
pretext文と複数行の文字列
pretext文で、プログラムにテキストデータを記述できます。pretext文は、関数定義の外部で使用可能です。
次の例では、char*型の変数menu_mainにテキストデータがセットされます。<タブ>はタブ文字(文字コード0x09)で置き換えてください。
pretext {
NEW<タブ>新規作成(&N)
--
REPLY<タブ>返信(&C)
RPLFRM<タブ>差出人へメール(&M)
FWARD<タブ>転送(&W)
} menu_main;
pretextと{は同じ段落に記述してください。次の行から、行頭の}までがテキストデータになります。
WZ6.0.62から関数内部で使用可能な構文を追加しました。\\" から、行頭の"までにテキストデータを記述できます。
func {
int a = 10;
mchar* szstring = \\"
NEW<タブ>新規作成(&N)
--
REPLY<タブ>返信(&C)
RPLFRM<タブ>差出人へメール(&M)
FWARD<タブ>転送(&W)
";
information("%d\n%s",a,szstring);
}
このほか、\"文字列"構文で、文字列内の\をエスケープシーケンスと見なさず通常文字として記述できます。フルパスのファイル名が記述しやすくなります。
mchar* szstring = \"c:\text\main.c";
printf文
printf文は、C言語では標準出力に出力されますが、Text-Cではステータスウィンドウに出力されます。ステータスウィンドウは、WZ画面の下部に表示されるテキストボックスです。
デバッグ用の構文としてprintdbとpdbがあります。printdbはソースのファイル名と位置をprintf文で表示します。pdbはダイアログボックスで表示します。
printfでは次の書式文字列が指定できます。
%d | 数値を表示 |
%.d | 数値を1/10して表示 |
%..d | 数値を1/100して表示 |
%id | 数値を小文字の識別値として表示 |
%ID | 数値を小文字の識別値として表示 |
%u | (unsigned)数値を表示 |
%x | 16進数で小文字で表示 |
%X | 16進数で大文字で表示 |
%zd(36進) | 数値をatozの形式に変換 |
%e | double数値をxxx e xxx形式で表示 |
%f | double数値をxxx.xxx形式で表示 |
%p | ポインタを出力(将来64bitに対応するために数値と分けてあります) |
%/ | \ |
%% | % |
%c | 文字を小文字で表示 |
%C | 文字を大文字で表示 |
%s | 文字列(STR)を表示 |
%cs | カラム文字列(CLM)を表示 |
%ns | 文字を文字数まで表示。printf("%ns","abcd",2)は"ab" |
%ws | UNICODE文字を表示 |
%es | 文字列をエンコードして表示 |
%m | (MESSAGEID*)を表示 |
%t | (UTCFILETIME*)を表示 |
%gt | GMTを表示 |
iprintf文は、printfと同じですが、ステータスウィンドウが非表示の場合はステータスウィンドウの表示をONにします。
textprintf文は、結果を新規作成したテキストに出力して返します。
簡易関数定義
コマンドとして作成する関数は、省略して記述できます。
この記述で定義した関数は、マクロプログラム実行時に、実行するコマンド一覧として表示されます。
main {
...
}
上記の記述は、以下の記述と同等です。
int main(void)
{
...
}
テキストの動作の詳細
テキストの内容は、テキストバッファに入ります。
テキストバッファは拡大縮小がしやすい構造になっています。
テキストバッファ先頭
テキスト(A)
ギャップ先頭
ギャップ終わり
テキスト(B)
テキストバッファ末尾
テキストの内容は、ギャップ(緩衝域)でAとBの2つに分かれています。挿入はギャップから領域を確保、削除はギャップを増やして高速に挿入削除を実行します。ギャップが不足した場合は、テキストバッファを再アロケートします。
ギャップの位置は、必ずテキストAが段落末尾で終わる位置になります。ギャップが段落の内容を分割することは原則としてありません。
段落の長さが64KBを超える場合は、ギャップが段落を分割することがあります。この場合は、かならずシフトJISの2バイト目にならないASCII文字で区切ります。ギャップの先頭には0が入ります。getnextparabuffなどは、この分割部分を段落の区切りとして認識します。
テキストの自動削除の詳細
new()で作成したテキストは自動的に削除され、テキストバッファとして確保したメモリとテキストハンドルのメモリの解放がおこなわれます。
テキストをC言語の自動変数と同様に簡単に使用できます。
テキストの削除は、関数を終えるときに実行されます。
次の例では、return文の直後で削除が実行されます。
int test(void)
{
HTEXT text = new();
...
return 10;
}
delete文で、手動削除も可能です。delete文では、関数内の任意の場所で削除できます。手動削除したテキストの自動削除はおこなわれません。
int test(void)
{
HTEXT text = new();
...
delete(text);
return 10;
}
テキストにビューがある場合は、関数終了時の削除は実行されません。ユーザ操作でウィンドウが閉じられてビューが削除されたときに、テキストの削除が実行されます。
int test(void)
{
HTEXT text = new();// 参照カウント=1
vnew(text,...);// 参照カウント=2
return 10;// ここではtextは削除されない。
}
テキストの削除は、参照カウンタで判断します。new()時点では参照カウンタは1です。new()を呼び出した関数の終了時にカウンタを1つ減らします。0になったときに削除を実行します。
ビューを作成すると、テキストの参照カウンタを1つ増やします。ビューを削除するとデキストの参照カウンタを1つ減らします。
ユニットに、テキストのプロパティをセットした場合にも参照カウンタが増やされることがあります。ユニットの側で、テキストの使用時間が長いときに参照カウンタを増やします。ユニットを削除すると、ユニット側の処理でテキストの参照カウンタを減らします。ユニットを使用する側では特に意識する必要はありません。
new()で作成したテキストを返す関数は、関数の宣言および定義にnewobj修飾子を指定します。
HTEXT newobj test(void)
{
HTEXT text = new();
...
return text;
}
newobj修飾子を付けた関数の終了時に、return文で返すテキストは削除されません。return以外のテキストは自動削除されます。
newobj修飾の関数を呼び出した場合は、返されたテキストは関数終了時に自動削除されます。
void test2(void)
{
HTEXT text = test();
...
return; // textは自動削除される
}
WindowsAPIとDLLの呼び出し
Text-Cは、WindowsAPIの呼び出しに対応しています。
WindowsAPIを使うには拡張子を .c にしてプログラムファイルを作成し、プログラムの先頭で次のincludeをおこなってください。
#include <windows.h>
#include <text.h>
※拡張子を.txcにすると、WindowsAPIの呼び出しなどでコンパイルエラーが発生することがあります。この場合は拡張子を.cに変更してください。
Text-CはWindowsAPIのデータを内蔵していますので、SDKは必要ありません。
Text-Cの制限により一部使えないWindowsAPIもあります。
DLLの関数の呼び出しも簡単にできます。次の記述で、DLL関数を宣言します。
extern "DLL名" {
関数宣言
}
記述例:
extern "test.dll" {
BOOL WINAPI test1func(int arg1,LPVOID arg2);
BOOL WINAPI test2func(int arg1,LPVOID arg2);
}
main {
test1func1(arg1,arg2);
}
文字コードについて
Text-Cでは、シフトJISを拡張したSJIS-EX文字コードを用います。
シフトJISではUnicodeの全文字を表すことができませんが、SJIS-EXではシフトJISに含まれないUnicode文字をシフトJISの未使用領域に割り当て、Unicodeの全文字を表現できるように拡張しました。
シフトJISで表現可能な文字は、すべてシフトJISで表します。Unicode特有の文字は、次の3バイトのデータで表します。
0x80+2バイト
0xA0+2バイト
+2バイトのデータは、それぞれ0x40〜0xFFの範囲となります。
SJIS-EXには以下の特徴があります。
-
シフトJISの文書を変換なしに読み書きできます。
シフトJISにないUnicode文字をすぐに検索できます。
WZ5(2003年)、WZモバイル、WZ6で使用されています。
Text-CではSJIS-EX文字列を、mchar*型(LPBYTEと同じ)で扱います。
SJIS-EXでは一文字のバイト数は、1,2,3,6バイトになります。
1バイトはASCII、2バイトはシフトJIS、3バイトはUnicode特有文字、6バイトはサロゲートペアのUnicode文字です。
Text-CからWindowsAPIへ文字列を渡すには、WindowsAPIのUnicode版を用いてSJIS-EXをUnicodeに変換して渡してください。変換には下記のAPIを用います。
WindowsAPIのANSI版には原則として渡すことはできません。シフトJISの文字列は渡せますが、SJIS-EXの拡張文字は渡せません。
wstrdupAやwstrdupLenAでは、freeの手間がかかります。
文字列をテキストに入れてsjistoとgetdataを使うとfreeの手間がかかりません。
HTEXT text = dup("SJIS-EX文字列");
sjisto(text,ID_UNIB);
MessageBox(NULL,(LPVOID)getdata(text),L"msg",MB_OK);
-
wchar* wstrdupA(mchar* sz);
szのSJIS-EX文字列をUNICODEに変換してmallocしたメモリブロックに入れて返します。末尾の0もセットされます。返されたメモリブロックはfreeする必要があります。
wchar* wstrdupLenA(mchar* sz,int lch);
szのSJIS-EX文字列の先頭からlchバイトまでをUNICODEに変換してmallocしたメモリブロックに入れて返します。末尾の0もセットされます。返されたメモリブロックはfreeする必要があります。
wchar* wdupfn(CLM fn);
fnをUNICODE文字列に変換して、mallocしたバッファに入れて返します。末尾の0もセットされます。返されたメモリブロックはfreeする必要があります。
fnの先頭部が"wz://","wzcfg://"の場合はフルパスに変換してバッファに入れて返します。
mchar* strdupW(wchar* wsz);
wszのUNICODE文字列をSJIS-EXに変換してmallocしたメモリブロックに入れて返します。末尾の0もセットされます。返されたメモリブロックはfreeする必要があります。
mchar* strdupLenW(wchar* wsz,int lch);
wszのUNICODE文字列の先頭からlch文字をSJIS-EXに変換してmallocしたメモリブロックに入れて返します。末尾の0もセットされます。返されたメモリブロックはfreeする必要があります。
BOOL tosjis(HTEXT text,int id);
textの内容の文字コードをidからSJIS-EXに変換します。idにはID_UNI(BOMあり),ID_UNIB(BOMなし),ID_SJIS,ID_JIS,ID_EUC,ID_UTF,ID_ASCIIのどれかの値を指定します。
BOOL sjisto(HTEXT text,int id);
textの内容の文字コードをSJIS-EXからidに変換します。idにはID_UNI(BOMあり),ID_UNIB(BOMなし),ID_SJIS,ID_JIS,ID_EUC,ID_UTF,ID_ASCIIのどれかの値を指定します。
BOOL h_issjisex(int c);
cがSJIS-EXの拡張文字(0x80,0xA0)かどうかを返します。0<=cかつc<=0xFFを仮定しています。
IFILE buffgetcharsize(mchar* p);
p位置の文字のバイト数を返します。SJIS-EX文字列に適用できます。
他のモジュールの呼び出し
他のText-Cプログラムファイルの関数を呼び出すには、次の宣言をおこなってから関数を呼び出します。
extern "\\system\txtool.c" {
void wsplit(void);
}
"\\system\txtool.c"は、「authorがsystemで名前がtxtool.cのText-Cプログラム」を表しています。[マクロ]メニューから[&(admin)プログラムの登録]されたText-Cプログラムはこの記述で呼び出します。この記述は次のように#include文でも使えます。
#include "\\system\test.h"
ファイル名だけを指定することもできます。
extern "txtool.c" {
void wsplit(void);
}
上記の例では、同じフォルダの"txtool.c"ファイルのwsplit関数を宣言しています。
スレッドとスタック
Text-Cでは、マルチスレッドに対応しました。
スレッドの開始ルーチンは返り値なし、引数1つの関数として定義します。
static void start(HTEXT mailer)
{
... // 処理
threaddelete();
}
スレッドを終了時にはthreaddelete APIを呼び出します
スレッドを開始するには、threadnew APIを使います。
HANDLE h = threadnew(start,mailer);
threadnewの引数にスレッドの開始ルーチンとその引数を渡すと、開始したスレッドのWindowsのハンドルを返します。
Text-Cのスタックサイズは32KB固定なので、大きな配列を使うときは自動変数でなく、mallocやテキストを使います。
スタックがオーバーフローすると、エラーメッセージ表示・関数実行の停止・呼び出し元へ0を返します。
暗黙の#include
プログラムのファイル拡張子が".c"以外の場合は、暗黙に#include <text.h>がおこなわれてText-CのAPIが使用可能になります。
拡張子が".c"の場合は、暗黙の#includeはおこなわれません。Text-CのAPIを使うには、#include <text.h>を明示的におこなってください。
標準のC言語との違い
Text-Cには、C言語と比較して以下の違いや制限があります。
-
配列は最大3次元までです。
asm文は無視されます。
constは無視されます。
register宣言は無視されます。
typedef定義はネストできません。
変数名・関数名の長さは63文字までです。
自動変数の内容は0に初期化されています。
文字列、数値に<\8進数>は使えません。<\0>は使えます。
サイズが4バイトを越える構造体の実体を「関数に渡す」「返り値で返す」ことはできません。構造体はポインタで渡します。構造体の代入はできます。
タイプチェックは、ANSI-Cに比べ厳密でC++に近いチェックをおこないます。「未宣言の関数呼び出し」「引数のタイプミスマッチ」はエラーになります。
浮動小数点の違い
Text-Cでは、浮動小数点に対応しました。
浮動小数点の変数は、double型またはfloat型で定義します。Text-Cではfloatはdoubleと同じ精度です。
double型はバイト数8バイトの符号付きで、1.7E +/- 308の範囲で15桁の有効桁数があります。
double型は、C言語と比較して若干の制限があります。関数の引数として「doubleを渡す」、返り値として「doubleを返す」は、C言語と同じく可能です。
-
%=は使えません。double a;a%=10;の記述はできません。+= -= *= /= には対応します。
静的変数のdoubleの初期化はできません。static double a=10.5;の記述はエラーになります。関数内で初期化をおこなってください。(自動変数のdoubleの初期化は可能です)
配列の初期化はできません。double t[3] = {1,2,3};の記述はエラーになります。t[0]=1;のように1つ1つ代入してください。
doubleをメンバに持つ構造体の初期化はできません。プログラム中で明示的に代入してください。
printf/sprintf/textprintfでは、%fや%eでdoubleを数値表示できます。%fや%eには必ずdouble型の変数を渡します。int型の変数を渡すとアプリケーションエラーになります。
使用例:
double x=123.5678;
printf("%f",x); 123.567800
printf("%3.3f",x);123.568
printf("%.f",x); 124
printf("%e",x); 1.235678e+002
printf("%.3e",x); 1.236e+002
-
四則演算( + - * / )、代入( = )、比較( == != >= <= > < )、表示( "%f","%e" )、文字列からの取得( atopf("文字列",pf) )の操作が可能です。sinなどの数学関数には未対応です。
「a ? b : c」文のbとcにdoubleは使用できません。
文字列からの取得は次のAPIで可能です。
BOOL textapi(299) atopf(mchar* p,double* pf);
pから始まる浮動小数点実数をdoubleに変換して*pfに代入します。変換が正常にできたかどうかを返します。
標準のatofはisspace(*p)なら空白をスキップして処理しますが、atopfは0を返します。
modpf APIは、double数値の整数部と小数部を求めて返します。
double専用のスタックのサイズは256KBです。ループで何万回かdoubleを加算するとオーバーフローします。この場合は加算のプログラムを別関数に切り出し、ループ内で関数を呼び出すように変更してください。