Perintah bawaan:
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 );
Skrip Tcl terdiri dari perintah-perintah yang dipisahkan oleh titik koma atau simbol baris baru. Perintah pada gilirannya terdiri dari kata-kata yang dipisahkan oleh spasi. Untuk menjadikan spasi sebagai bagian dari kata, seseorang dapat menggunakan tanda kutip ganda atau kurung kurawal.
Bagian penting dari bahasa ini adalah substitusi perintah , ketika hasil perintah di dalam kurung kurawal dikembalikan sebagai bagian dari perintah luar, misalnya puts [+ 1 2]
.
Satu-satunya tipe data bahasa ini adalah string. Meskipun mungkin menyulitkan operasi matematika, ini membuka jalan luas untuk membangun DSL Anda sendiri guna menyempurnakan bahasa.
Simbol apa pun dapat menjadi bagian dari sebuah kata, kecuali simbol khusus berikut:
r
, n
, titik koma atau EOF - digunakan untuk membatasi perintahPartcl memiliki fungsi pembantu khusus untuk kelas char berikut:
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
berperilaku berbeda tergantung pada mode kutipan ( parameter q
). Di dalam tanda kurung kurawal yang dikutip, simbol titik koma dan akhir baris kehilangan makna khususnya dan menjadi karakter biasa yang dapat dicetak.
Partcl lexer diimplementasikan dalam satu fungsi:
int tcl_next(const char *s, size_t n, const char **from, const char **to, int *q);
Fungsi tcl_next
menemukan token berikutnya dalam string s
. from
dan to
diatur untuk menunjuk ke awal/akhir token, q
menunjukkan mode kutipan dan diubah jika "
terpenuhi.
Makro khusus tcl_each(s, len, skip_error)
dapat digunakan untuk mengulangi semua token dalam string. Jika skip_error
salah - loop berakhir ketika string berakhir, jika tidak, loop dapat berakhir lebih awal jika ditemukan kesalahan sintaksis. Hal ini memungkinkan untuk "memvalidasi" string input tanpa mengevaluasinya dan mendeteksi kapan perintah lengkap telah dibaca.
Tcl menggunakan string sebagai tipe data utama. Ketika skrip Tcl dievaluasi, banyak string yang dibuat, dibuang atau dimodifikasi. Dalam sistem tertanam, manajemen memori bisa menjadi rumit, sehingga semua operasi dengan nilai Tcl dipindahkan ke fungsi terisolasi yang dapat dengan mudah ditulis ulang untuk mengoptimalkan bagian-bagian tertentu (misalnya menggunakan kumpulan string, pengalokasi memori khusus, numerik cache, atau nilai daftar untuk meningkatkan kinerja dll).
/* 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);
Perlu diingat, bahwa fungsi ..._append()
harus membebaskan argumen tail. Selain itu, string yang dikembalikan oleh tcl_string()
tidak dimaksudkan untuk dimutasi atau di-cache.
Dalam implementasi default, daftar diimplementasikan sebagai string mentah yang menambahkan beberapa pelolosan (tanda kurung kurawal) di sekitar setiap iterm. Ini adalah solusi sederhana yang juga mengurangi kode, namun dalam beberapa kasus eksotik, pelolosan bisa menjadi salah dan hasil yang tidak valid akan dikembalikan.
Tipe khusus, struct tcl_env
digunakan untuk menjaga lingkungan evaluasi (serangkaian fungsi). Penerjemah menciptakan lingkungan baru untuk setiap prosedur yang ditentukan pengguna, juga terdapat satu lingkungan global per penerjemah.
Hanya ada 3 fungsi yang berhubungan dengan lingkungan hidup. Yang satu menciptakan lingkungan baru, yang lain mencari suatu variabel (atau menciptakan yang baru), yang terakhir menghancurkan lingkungan dan semua variabelnya.
Fungsi-fungsi ini menggunakan malloc/free, tetapi dapat dengan mudah ditulis ulang untuk menggunakan kumpulan memori.
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);
Variabel diimplementasikan sebagai daftar tertaut tunggal, setiap variabel adalah sepasang nilai (nama + nilai) dan penunjuk ke variabel berikutnya.
Penerjemah partcl adalah struktur sederhana struct tcl
yang menjaga lingkungan saat ini, susunan perintah yang tersedia, dan nilai hasil terakhir.
Logika interpreter mencakup dua fungsi - evaluasi dan substitusi.
Substitusi:
$
- buat perintah sementara [set name]
dan evaluasi. Di Tcl $foo
hanyalah jalan pintas ke [set foo]
, yang mengembalikan nilai variabel "foo" di lingkungan saat ini.[
- evaluasi apa yang ada di dalam tanda kurung siku dan kembalikan hasilnya.{foo bar}
) - kembalikan apa adanya, hanya tanpa kurung kurawal.Evaluasi:
TCMD
khusus untuknya) - lalu temukan perintah yang sesuai (kata pertama dalam daftar) dan panggil perintah itu. Dari mana perintah tersebut diambil? Awalnya, interpeter Partcl dimulai tanpa perintah, tetapi seseorang dapat menambahkan perintah dengan memanggil tcl_register()
.
Setiap perintah memiliki nama, arity (berapa banyak argumen yang harus diambil - penerjemah memeriksanya sebelum memanggil perintah, gunakan nol arity untuk varargs) dan penunjuk fungsi C yang benar-benar mengimplementasikan perintah.
"set" - tcl_cmd_set
, memberikan nilai ke variabel (jika ada) dan mengembalikan nilai variabel saat ini.
"subst" - tcl_cmd_subst
, melakukan substitusi perintah dalam string argumen.
"puts" - tcl_cmd_puts
, mencetak argumen ke stdout, diikuti dengan baris baru. Perintah ini dapat dinonaktifkan menggunakan #define TCL_DISABLE_PUTS
, yang berguna untuk sistem tertanam yang tidak memiliki "stdout".
"proc" - tcl_cmd_proc
, membuat perintah baru yang menambahkannya ke daftar perintah juru bahasa saat ini. Begitulah cara perintah yang ditentukan pengguna dibuat.
"if" - tcl_cmd_if
, melakukan if {cond} {then} {cond2} {then2} {else}
sederhana.
"sementara" - tcl_cmd_while
while , menjalankan perulangan while {cond} {body}
. Seseorang dapat menggunakan "break", "continue" atau "return" di dalam loop untuk mengontrol aliran.
Berbagai operasi matematika diimplementasikan sebagai tcl_cmd_math
, tetapi juga dapat dinonaktifkan jika skrip Anda tidak memerlukannya (jika Anda ingin menggunakan Partcl sebagai shell perintah, bukan sebagai bahasa pemrograman).
Semua sumber ada dalam satu file, tcl.c
. Ini dapat digunakan sebagai penerjemah mandiri, atau disertakan sebagai perpustakaan file tunggal (Anda mungkin ingin mengganti namanya menjadi tcl.h).
Tes dijalankan dengan dentang dan cakupan dihitung. Jalankan saja "make test" dan selesai.
Kode diformat menggunakan format dentang untuk menjaga gaya pengkodean tetap bersih dan mudah dibaca. Silakan jalankan untuk permintaan tarik juga.
Kode didistribusikan di bawah lisensi MIT, jangan ragu untuk menggunakannya dalam proyek milik Anda juga.