組み込みコマンド:
subst arg
set var ?val?
while cond loop
if cond branch ?cond? ?branch? ?other?
proc name args body
return
break
continue
+, -, *, /, <, >, <=, >=, ==, !=
struct tcl tcl ;
const char * s = "set x 4; puts [+ [* $x 10] 2]" ;
tcl_init ( & tcl );
if ( tcl_eval ( & tcl , s , strlen ( s )) != FERROR ) {
printf ( "%.*sn" , tcl_length ( tcl . result ), tcl_string ( tcl . result ));
}
tcl_destroy ( & tcl );
Tcl スクリプトは、セミコロンまたは改行記号で区切られたコマンドで構成されます。コマンドは、空白で区切られた単語で構成されます。空白を単語の一部にするには、二重引用符または中括弧を使用します。
この言語の重要な部分は、角括弧内のコマンドの結果が外側のコマンドの一部として返されるときのコマンド置換です (例: puts [+ 1 2]
。
この言語の唯一のデータ型は文字列です。数学的演算は複雑になる可能性がありますが、独自の DSL を構築して言語を強化する幅広い道が開かれます。
次の特殊記号を除き、任意の記号を単語の一部にすることができます。
r
、 n
、セミコロン、または EOF - コマンドを区切るために使用されますPartcl には、次の char クラス用の特別なヘルパー関数があります。
static int tcl_is_space(char c);
static int tcl_is_end(char c);
static int tcl_is_special(char c, int q);
tcl_is_special
、引用モード ( q
パラメーター) に応じて動作が異なります。引用符で囲まれた文字列の中括弧、セミコロン、および行末記号は特別な意味を失い、通常の印刷可能な文字になります。
Partcl レクサーは 1 つの関数で実装されています。
int tcl_next(const char *s, size_t n, const char **from, const char **to, int *q);
tcl_next
関数は、文字列s
内の次のトークンを検索します。 from
とto
トークンの開始/終了を指すように設定され、 q
引用モードを示し、 "
が満たされた場合に変更されます。
特別なマクロtcl_each(s, len, skip_error)
使用して、文字列内のすべてのトークンを反復処理できます。 skip_error
が false の場合、文字列の終了時にループが終了します。それ以外の場合は、構文エラーが見つかった場合にループがより早く終了する可能性があります。これにより、入力文字列を評価せずに「検証」し、完全なコマンドが読み取られたことを検出できます。
Tcl は主要なデータ型として文字列を使用します。 Tcl スクリプトが評価されると、多くの文字列が作成、破棄、または変更されます。組み込みシステムではメモリ管理が複雑になる可能性があるため、Tcl 値を使用するすべての操作は、特定の部分を最適化するために簡単に書き換えることができる分離された関数に移動されます (たとえば、文字列のプール、カスタム メモリ アロケータ、キャッシュ数値またはリスト値を使用して増加させるため)パフォーマンスなど)。
/* Raw string values */
tcl_value_t *tcl_alloc(const char *s, size_t len);
tcl_value_t *tcl_dup(tcl_value_t *v);
tcl_value_t *tcl_append(tcl_value_t *v, tcl_value_t *tail);
int tcl_length(tcl_value_t *v);
void tcl_free(tcl_value_t *v);
/* Helpers to access raw string or numeric value */
int tcl_int(tcl_value_t *v);
const char *tcl_string(tcl_value_t *v);
/* List values */
tcl_value_t *tcl_list_alloc();
tcl_value_t *tcl_list_append(tcl_value_t *v, tcl_value_t *tail);
tcl_value_t *tcl_list_at(tcl_value_t *v, int index);
int tcl_list_length(tcl_value_t *v);
void tcl_list_free(tcl_value_t *v);
..._append()
関数は tail 引数を解放する必要があることに注意してください。また、 tcl_string()
によって返される文字列は、変更またはキャッシュされることを意図したものではありません。
デフォルトの実装では、リストは各 iterm の前後にいくつかのエスケープ (中括弧) を追加する生の文字列として実装されます。これはコードも削減できる簡単な解決策ですが、場合によってはエスケープが間違っており、無効な結果が返されることがあります。
特別なタイプのstruct tcl_env
、評価環境 (関数のセット) を保持するために使用されます。インタプリタは、ユーザー定義のプロシージャごとに新しい環境を作成します。また、インタプリタごとに 1 つのグローバル環境が存在します。
環境に関する機能は 3 つだけです。 1 つは新しい環境を作成し、もう 1 つは変数を検索 (または新しい環境を作成) し、最後のものは環境とそのすべての変数を破棄します。
これらの関数は malloc/free を使用しますが、代わりにメモリ プールを使用するように簡単に書き換えることができます。
static struct tcl_env *tcl_env_alloc(struct tcl_env *parent);
static struct tcl_var *tcl_env_var(struct tcl_env *env, tcl_value_t *name);
static struct tcl_env *tcl_env_free(struct tcl_env *env);
変数は単一リンク リストとして実装され、各変数は値のペア (名前 + 値) と次の変数へのポインターです。
Partcl インタープリタは、現在の環境、使用可能なコマンドの配列、および最後の結果の値を保持する単純な構造のstruct tcl
です。
インタプリタ ロジックは、評価と置換という 2 つの関数にラップされています。
置換:
$
で始まる場合 - 一時コマンド[set name]
を作成し、それを評価します。 Tcl では$foo
[set foo]
への単なるショートカットであり、現在の環境の "foo" 変数の値を返します。[
で始まる場合は、角かっこ内の内容を評価し、結果を返します。{foo bar}
) の場合は、中括弧を付けずにそのまま返します。評価:
TCMD
があります)、適切なコマンド (リストの最初の単語) を見つけて呼び出します。コマンドはどこから取得されたのでしょうか?最初、Partcl インターピーターはコマンドなしで開始されますが、 tcl_register()
呼び出すことでコマンドを追加できます。
各コマンドには、名前、引数 (引数の数 - コマンドを呼び出す前にインタプリタがチェックし、可変引数にはゼロの引数を使用します)、および実際にコマンドを実装する C 関数ポインタがあります。
"set" - tcl_cmd_set
、変数 (存在する場合) に値を代入し、現在の変数値を返します。
"subst" - tcl_cmd_subst
、引数文字列でコマンド置換を行います。
"puts" - tcl_cmd_puts
は、引数を標準出力に出力し、その後に改行を出力します。このコマンドは#define TCL_DISABLE_PUTS
を使用して無効にできます。これは、「stdout」を持たない組み込みシステムに便利です。
"proc" - tcl_cmd_proc
、現在のインタープリタ コマンドのリストに追加する新しいコマンドを作成します。これがユーザー定義コマンドの構築方法です。
「if」 - tcl_cmd_if
は、単純なif {cond} {then} {cond2} {then2} {else}
を実行します。
「while」 - tcl_cmd_while
、 while ループwhile {cond} {body}
を実行します。ループ内で「中断」、「続行」、または「戻る」を使用してフローを制御できます。
さまざまな数学演算はtcl_cmd_math
として実装されていますが、スクリプトでそれらが必要ない場合 (Partcl をプログラミング言語としてではなくコマンド シェルとして使用する場合)、無効にすることもできます。
すべてのソースは 1 つのファイルtcl.c
にあります。これはスタンドアロンのインタープリターとして使用することも、単一ファイルのライブラリとして含めることもできます (名前を tcl.h に変更するとよいでしょう)。
テストは Clang で実行され、カバレッジが計算されます。 「make test」を実行するだけで完了です。
コードは、クリーンで読みやすいコーディング スタイルを維持するために、clang 形式を使用してフォーマットされます。プルリクエストでも実行してください。
コードは MIT ライセンスに基づいて配布されているため、独自のプロジェクトでも自由に使用できます。