[루아2.1] Table.c Table.h

4 분 소요

Table.h

루아 구현 내부에서 사용하는 심볼 테이블과 문자열 테이블을 구현하는 파일이 Table.h와 Table.c입니다.

extern Symbol *lua_table;
extern TaggedString **lua_constant;

extern char   *lua_file[];
extern int     lua_nfile;

Table.h 파일에는 함수 선언을 제외하고 익스턴 선언만 있습니다. lua_table은 심볼 테이블을 관리하는 변수입니다. lua_constant는 문자열 리터럴을 관리하는 변수입니다. lua_file은 루아 VM이 해석하는 루아 파일 목록입니다. lua_nfile은 루아 파일이 하나씩 입력될 때마다 증가합니다.

Table.c

#define BUFFER_BLOCK 256

Symbol *lua_table;
static Word lua_ntable = 0;
static Long lua_maxsymbol = 0;

TaggedString **lua_constant;
static Word lua_nconstant = 0;
static Long lua_maxconstant = 0;



#define MAXFILE 	20
char  		       *lua_file[MAXFILE];
int      		lua_nfile;

#define GARBAGE_BLOCK 256
#define MIN_GARBAGE_BLOCK 10

BUFFER_BLOCK은 심볼 테이블을 처음 만들 때 크기로 지정하는 값입니다. 크기가 부족할 때도 이 크기만큼 메모리 할당 크기를 늘립니다. 이후로 심볼 테이블 관련 변수 세 개, 문자열 리터럴 관련 변수 세 개를 선언했습니다. 루아는 파일을 최대 20개까지 열 수 있습니다. GARBAGE_BLOCK과 MIN_GARBAGE_BLOCK는 루아 가비지 컬랙터 관련 값 선언입니다. 심볼이나 문자열 리터럴을 하나씩 새로 만들 때마다 카운터를 하나씩 늘리다가 그 카운터 값이 GARBAGE_BLOCK에 도달하면 가비지 컬랙션을 시작합니다.

/*
** Initialise symbol table with internal functions
*/
static void lua_initsymbol (void)
{
 Word n;
 lua_maxsymbol = BUFFER_BLOCK;
 lua_table = newvector(lua_maxsymbol, Symbol);
 n = luaI_findsymbolbyname("next");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_next;
 n = luaI_findsymbolbyname("dofile");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_internaldofile;
 n = luaI_findsymbolbyname("setglobal");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = setglobal;
 n = luaI_findsymbolbyname("getglobal");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = getglobal;
 n = luaI_findsymbolbyname("nextvar");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_nextvar;
 n = luaI_findsymbolbyname("type"); 
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = luaI_type;
 n = luaI_findsymbolbyname("tonumber");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_obj2number;
 n = luaI_findsymbolbyname("print");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_print;
 n = luaI_findsymbolbyname("dostring");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_internaldostring;
 n = luaI_findsymbolbyname("setfallback");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = luaI_setfallback;
 n = luaI_findsymbolbyname("error");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = luaI_error;
}

추정컨데 빌트인 함수로 보이는 함수들을 미리 심볼 테이블에 초기값으로 넣는 함수입니다. 물론 초기값을 입력하기 전에 메모리를 할당합니다.

/*
** Initialise constant table with pre-defined constants
*/
void lua_initconstant (void)
{
 lua_maxconstant = BUFFER_BLOCK;
 lua_constant = newvector(lua_maxconstant, TaggedString *);
}

루아에서는 문자열 리터럴을 왜 헷갈리게 constant라고 이름 붙였는지 모르겠습니다. 당시만해도 루아 개발자들이 프로그래밍 언어론의 용어를 정확히 몰랐거나 다른 의도적인 이유가 있을텐데 코드만 읽어서는 의도를 파악할 수 없습니다. 아무튼 이름은 constant지만 그 동작은 명확히 문자열 리터럴을 관리하는 것이 lua_constant 변수입니다. 이 문자열 리터럴을 저장하는 변수에 메모리를 할당하는 함수입니다.

** Given a name, search it at symbol table and return its index. If not
** found, allocate it.
*/
Word luaI_findsymbol (TreeNode *t)
{
 if (lua_table == NULL)
  lua_initsymbol(); 
 if (t->varindex == NOT_USED)
 {
  if (lua_ntable == lua_maxsymbol)
  {
   if (lua_maxsymbol >= MAX_WORD)
     lua_error("symbol table overflow");
   lua_maxsymbol *= 2;
   if (lua_maxsymbol >= MAX_WORD)
     lua_maxsymbol = MAX_WORD; 
   lua_table = growvector(lua_table, lua_maxsymbol, Symbol);
  }
  t->varindex = lua_ntable;
  s_tag(lua_ntable) = LUA_T_NIL;
  lua_ntable++;
 }
 return t->varindex;
}

심볼 테이블에서 심볼을 검색하는 함수입니다. lua_table이 null이면 (최초 실행이면) lua_initsymbol() 함수를 호출해서 심볼 테이블에 메모리를 할당하고 초기화합니다. 파라미터 t가 심볼 테이블에 없는 (NOT_USED) 심볼일 때만 심볼 테이블에 추가하는 작업을 진행합니다. 심볼 테이블이 지정된 최대 크기에 도달하면 늘리는 작업을 하고요. 파라미터 t에 현재 심볼 테이블 인덱스를 지정해서 심볼 테이블에 심볼이 들어갔음을 표시합니다. 그런데 상식적이라면 심볼 테이블 오브젝트 엔트리에 심볼 포인터를 저장할 것 같은데 그렇게 안하네요. 딱히 그렇게 안해도 관리가 되서 그런것 같긴한데 그러면 뭐하러 심볼 테이블 배열 인스턴스를 유지하는지 모르겠습니다. 그냥 카운터만 관리해도 될 텐데요.

Word luaI_findsymbolbyname (char *name)
{
  return luaI_findsymbol(lua_constcreate(name));
}

문자열 name을 트리로 보내서 트리 노드에 등록한 다음, 트리 노드 포인터를 다시 심볼 테이블에 등록하는 일을 하는 함수 입니다.

/*
** Given a name, search it at constant table and return its index. If not
** found, allocate it.
** On error, return -1.
*/
Word luaI_findconstant (TreeNode *t)
{
 if (lua_constant == NULL)
  lua_initconstant();
 if (t->constindex == NOT_USED)
 {
  if (lua_nconstant == lua_maxconstant)
  {
   if (lua_maxconstant >= MAX_WORD)
     lua_error("constant table overflow");
   lua_maxconstant *= 2;
   if (lua_maxconstant >= MAX_WORD)
     lua_maxconstant = MAX_WORD;
   lua_constant = growvector(lua_constant, lua_maxconstant, TaggedString *);
  }
  t->constindex = lua_nconstant;
  lua_constant[lua_nconstant] = &(t->ts);
  lua_nconstant++;
 }
 return t->constindex;
}

기본 구조는 심볼 테이블 함수와 같습니다. 이 함수는 문자열 리터럴에 대해서 할당과 삽입을 처리합니다.

/*
** Traverse symbol table objects
*/
void lua_travsymbol (void (*fn)(Object *))
{
 Word i;
 for (i=0; i<lua_ntable; i++)
  fn(&s_object(i));
}

/*
** Mark an object if it is a string or a unmarked array.
*/
void lua_markobject (Object *o)
{
 if (tag(o) == LUA_T_STRING && !tsvalue(o)->marked)
   tsvalue(o)->marked = 1;
 else if (tag(o) == LUA_T_ARRAY)
   lua_hashmark (avalue(o));
}

가비지 컬랙션 할 때 헬퍼 함수 (helper function)로 쓰는 함수입니다. 파라미터 fn에 형식만 갖추면 어떤 함수든 쓸 수 있지만 루아 2.1 소스 코드에서는 가비지 컬랙터 mark 하는 함수만 사용합니다. 나중에 확장을 고려해서 이리 만들었을테지만 현 시점에서는 쓸데없는 코드일 뿐입니다. 바로 다음에 나오는 lua_markobject() 함수를 바로 사용하는 것이 더 효율적이지요.

/*
** Garbage collection. 
** Delete all unused strings and arrays.
*/
void lua_pack (void)
{
  static Long block = GARBAGE_BLOCK; /* when garbage collector will be called */
  static Long nentity = 0;  /* counter of new entities (strings and arrays) */
  Long recovered = 0;
  if (nentity++ < block) return;
  lua_travstack(lua_markobject); /* mark stack objects */
  lua_travsymbol(lua_markobject); /* mark symbol table objects */
  luaI_travlock(lua_markobject); /* mark locked objects */
  recovered += lua_strcollector();
  recovered += lua_hashcollector();
  nentity = 0;				/* reset counter */
  block=(16*block-7*recovered)/12;	/* adapt block size */
  if (block < MIN_GARBAGE_BLOCK) block = MIN_GARBAGE_BLOCK;
} 

심볼이나 문자열 리터럴을 하나씩 만들 때마다 매번 호출합니다. nentity 변수가 static이기 때문에 전체 호출 횟수가 저장되 있습니다. 호출 횟수가 block 값에 도달하면 가비지 컬랙션을 실행합니다.

/*
** Add a file name at file table, checking overflow. This function also set
** the external variable "lua_filename" with the function filename set.
** Return 0 on success or error message on error.
*/
char *lua_addfile (char *fn)
{
 if (lua_nfile >= MAXFILE)
   return "too many files";
 if ((lua_file[lua_nfile++] = luaI_strdup (fn)) == NULL)
   return "not enough memory";
 return NULL;
}

루아 인터프리터로 루아 파일이 입력될 때마다 호출하는 함수입니다. 파일 목록을 관리합니다.

/*
** Delete a file from file stack
*/
int lua_delfile (void)
{
 luaI_free(lua_file[--lua_nfile]); 
 return 1;
}

루아 인터프리터가 루아 파일 실행을 완료하면 호출하는 함수입니다.

댓글남기기