내장 명령:
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 스크립트는 세미콜론이나 개행 기호로 구분된 명령 으로 구성됩니다. Commnad는 공백으로 구분된 단어 로 구성됩니다. 공백을 단어의 일부로 만들려면 큰따옴표나 중괄호를 사용할 수 있습니다.
언어의 중요한 부분은 대괄호 안의 명령 결과가 외부 명령의 일부로 반환될 때 명령 대체 입니다(예: 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 어휘 분석기는 하나의 함수로 구현됩니다.
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
평가 환경(함수 세트)을 유지하는 데 사용됩니다. 인터프리터는 각 사용자 정의 프로시저에 대해 새로운 환경을 생성하며, 인터프리터당 하나의 전역 환경도 있습니다.
환경과 관련된 기능은 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
현재 환경에서 "foo" 변수의 값을 반환하는 [set foo]
에 대한 바로가기일 뿐입니다.[
로 시작하는 경우 - 대괄호 안의 내용을 평가하고 결과를 반환합니다.{foo bar}
)인 경우 중괄호 없이 그대로 반환합니다.평가:
TCMD
가짐) 적절한 명령(목록의 첫 번째 단어)을 찾아서 호출합니다. 명령은 어디서 가져오나요? 처음에는 Partcl 인터피터가 명령 없이 시작되지만 tcl_register()
호출하여 명령을 추가할 수 있습니다.
각 명령에는 이름, 인수 개수(인수 개수가 얼마나 필요한지 - 통역사가 명령을 호출하기 전에 확인하고 가변 인수에 대해 인수 개수 0개 사용)와 실제로 명령을 구현하는 C 함수 포인터가 있습니다.
"set" - tcl_cmd_set
, 변수(있는 경우)에 값을 할당하고 현재 변수 값을 반환합니다.
"subst" - tcl_cmd_subst
, 인수 문자열에서 명령 대체를 수행합니다.
"puts" - tcl_cmd_puts
, stdout에 인수를 인쇄하고 그 뒤에 개행 문자가 옵니다. 이 명령은 "stdout"이 없는 임베디드 시스템에 편리한 #define TCL_DISABLE_PUTS
사용하여 비활성화할 수 있습니다.
"proc" - tcl_cmd_proc
, 현재 인터프리터 명령 목록에 추가하는 새 명령을 만듭니다. 이것이 사용자 정의 명령이 작성되는 방식입니다.
"if" - tcl_cmd_if
, 간단한 if {cond} {then} {cond2} {then2} {else}
를 수행합니다.
"while" - tcl_cmd_while
, while {cond} {body}
루프를 실행합니다. 흐름을 제어하기 위해 루프 내부에서 "break", "continue" 또는 "return"을 사용할 수 있습니다.
다양한 수학 연산은 tcl_cmd_math
로 구현되지만 스크립트에 필요하지 않은 경우(Partcl을 프로그래밍 언어가 아닌 명령 셸로 사용하려는 경우) 비활성화할 수도 있습니다.
모든 소스는 하나의 파일인 tcl.c
에 있습니다. 독립형 인터프리터로 사용하거나 단일 파일 라이브러리로 포함될 수 있습니다(그런 다음 이름을 tcl.h로 바꿀 수 있습니다).
테스트는 clang으로 실행되고 적용 범위가 계산됩니다. "make test"를 실행하면 완료됩니다.
깔끔하고 읽기 쉬운 코딩 스타일을 유지하기 위해 코드 형식은 clang-format을 사용하여 지정됩니다. 풀 요청(Pull Request)에도 실행해 보세요.
코드는 MIT 라이센스에 따라 배포되므로 독점 프로젝트에서도 자유롭게 사용할 수 있습니다.