คำสั่งในตัว:
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 มีฟังก์ชันตัวช่วยพิเศษสำหรับคลาสอักขระเหล่านี้:
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 lexer ถูกนำไปใช้ในฟังก์ชันเดียว:
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
เป็นเท็จ - การวนซ้ำจะสิ้นสุดเมื่อสตริงสิ้นสุด มิฉะนั้น การวนซ้ำอาจสิ้นสุดเร็วกว่านั้นหากพบข้อผิดพลาดทางไวยากรณ์ อนุญาตให้ "ตรวจสอบ" สตริงอินพุตโดยไม่ต้องประเมินและตรวจจับเมื่อมีการอ่านคำสั่งแบบเต็ม
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()
จะต้องปล่อยอาร์กิวเมนต์ส่วนท้ายให้ว่าง นอกจากนี้ สตริงที่ส่งคืนโดย tcl_string()
ไม่ได้ตั้งใจที่จะกลายพันธุ์หรือแคช
ในรายการการใช้งานเริ่มต้นจะถูกนำไปใช้เป็นสตริงดิบที่เพิ่มการหลบหนี (วงเล็บปีกกา) รอบๆ แต่ละรายการ เป็นวิธีแก้ปัญหาง่ายๆ ที่ลดโค้ดด้วย แต่ในบางกรณีที่แปลกใหม่ การ Escape อาจผิดพลาดและผลลัพธ์ที่ไม่ถูกต้องจะถูกส่งกลับ
ชนิดพิเศษ struct tcl_env
ใช้เพื่อคงสภาพแวดล้อมการประเมินผล (ชุดของฟังก์ชัน) ล่ามจะสร้างสภาพแวดล้อมใหม่สำหรับแต่ละขั้นตอนที่ผู้ใช้กำหนด นอกจากนี้ยังมีสภาพแวดล้อมส่วนกลางหนึ่งสภาพแวดล้อมต่อล่ามหนึ่งคน
มีเพียง 3 ฟังก์ชั่นที่เกี่ยวข้องกับสิ่งแวดล้อม คนหนึ่งสร้างสภาพแวดล้อมใหม่ อีกคนแสวงหาตัวแปร (หรือสร้างขึ้นใหม่) อีกคนทำลายสภาพแวดล้อมและตัวแปรทั้งหมด
ฟังก์ชันเหล่านี้ใช้ 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
ซึ่งเก็บสภาพแวดล้อมปัจจุบัน อาร์เรย์ของคำสั่งที่มีอยู่ และค่าผลลัพธ์สุดท้าย
ตรรกะของล่ามครอบคลุมสองฟังก์ชัน - การประเมินและการทดแทน
การทดแทน:
$
- ให้สร้างคำสั่งชั่วคราว [set name]
และประเมินผล ใน Tcl $foo
เป็นเพียงทางลัดไปยัง [set foo]
ซึ่งส่งคืนค่าของตัวแปร "foo" ในสภาพแวดล้อมปัจจุบัน[
- ให้ประเมินสิ่งที่อยู่ในวงเล็บเหลี่ยมแล้วส่งคืนผลลัพธ์{foo bar}
) ให้ส่งคืนตามที่เป็นอยู่ โดยไม่ต้องใส่เครื่องหมายปีกกาการประเมิน:
TCMD
พิเศษสำหรับคำสั่งเหล่านั้น) จากนั้นค้นหาคำสั่งที่เหมาะสม (คำแรกในรายการ) แล้วเรียกคำสั่งนั้น เอาคำสั่งมาจากไหน? ในตอนแรก Interpeter ของ Partcl จะเริ่มต้นด้วยการไม่มีคำสั่ง แต่อาจมีการเพิ่มคำสั่งโดยการเรียก tcl_register()
แต่ละคำสั่งมีชื่อ arity (จำนวนอาร์กิวเมนต์ที่ต้องใช้ - ล่ามจะตรวจสอบก่อนที่จะเรียกใช้คำสั่ง ใช้ arity เป็นศูนย์สำหรับ varargs) และตัวชี้ฟังก์ชัน C ที่ใช้คำสั่งจริง
"set" - tcl_cmd_set
กำหนดค่าให้กับตัวแปร (ถ้ามี) และส่งคืนค่าตัวแปรปัจจุบัน
"subst" - tcl_cmd_subst
ทำการทดแทนคำสั่งในสตริงอาร์กิวเมนต์
"puts" - tcl_cmd_puts
พิมพ์อาร์กิวเมนต์ไปที่ stdout ตามด้วยการขึ้นบรรทัดใหม่ คำสั่งนี้สามารถปิดใช้งานได้โดยใช้ #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 วนซ้ำ while {cond} {body}
อาจใช้คำว่า "break", "continue" หรือ "return" ภายในลูปเพื่อควบคุมการไหล
การดำเนินการทางคณิตศาสตร์ต่างๆ ถูกนำมาใช้เป็น tcl_cmd_math
แต่สามารถปิดใช้งานได้เช่นกันหากสคริปต์ของคุณไม่ต้องการการดำเนินการดังกล่าว (หากคุณต้องการใช้ Partcl เป็นเชลล์คำสั่ง ไม่ใช่เป็นภาษาการเขียนโปรแกรม)
แหล่งที่มาทั้งหมดอยู่ในไฟล์เดียว tcl.c
สามารถใช้เป็นล่ามแบบสแตนด์อโลนหรือรวมเป็นไลบรารีไฟล์เดียว (คุณอาจต้องการเปลี่ยนชื่อเป็น tcl.h)
การทดสอบดำเนินการด้วยเสียงกราวด์และคำนวณความครอบคลุม เพียงเรียกใช้ "make test" เท่านี้ก็เสร็จสิ้น
โค้ดถูกจัดรูปแบบโดยใช้รูปแบบเสียงดังกราวเพื่อรักษารูปแบบการเขียนโค้ดที่สะอาดและอ่านง่าย โปรดเรียกใช้สำหรับการร้องขอการดึงด้วย
รหัสได้รับการเผยแพร่ภายใต้ใบอนุญาต MIT คุณสามารถนำไปใช้ในโครงการที่เป็นกรรมสิทธิ์ของคุณได้เช่นกัน