[루아2.4] lex.c

3 분 소요

Lua 2.4

이제 네 번째 릴리즈입니다. 루아 2.2에서 루아 2.4로 버전이 올라갔습니다. 바뀐점이 뭔지 볼까요?

  • external compiler creates portable binary files that can be loaded faster
  • interface for debugging and profiling
  • new “getglobal” fallback
  • new functions for handling references to Lua objects
  • new functions in standard lib
  • only one copy of each string is stored
  • expanded documentation, with more examples

이렇다고 합니다. 대충 번역해보면 이렇습니다. 별도 컴파일러로 바이너리 파일 생성가능하다고 합니다. 그리고 디버깅과 프로파일링하는 인터페이스도 만들었다고 합니다. 폴백에 getglobal도 만들었다고 하네요. 대체 폴백이 뭘까요. 루아 오브젝트 레퍼런스를 핸들링하는 함수를 추가했다고 합니다. 표준 라이브러리에 함수도 추가했다고 하고요. 저장된 문자열은 한 개만 유지한다고 합니다. 그리고 문서를 보강했다고 하네요.

마이너 업데이트 치고는 변경 사항이 많습니다. 루아 버전 규칙을 몰라서 작은 숫자 올린 것을 마이너 업데이트라고 간주했는데, 아닌건가?하는 생각이 듭니다. 그래도 뭐 처음에 결정했던 대로 그냥 마이너 업데이트라고 치고 이번 릴리즈 읽기를 하겠습니다. 어차피 버전 3.0으로 가면 전체 읽기를 할 테니 부족한 부분은 그때가서 보충하겠습니다.

lex.c

가장 먼저 눈에 띄는 것은 제가 계속 왜 자꾸 중복 코딩하는지 모르겠다고 지적했던 문자열 비교 매크로를 삭제한 것입니다. 역시 이상한 코드는 결국 처리를 하게 됩니다.

이어서 읽으면 루아 2.2까지 예약어 검사하는 findReserved() 함수를 없앴습니다.

[2.2]
static int findReserved (char *name)
{
  int l = 0;
  int h = RESERVEDSIZE - 1;
  while (l <= h)
  {
    int m = (l+h)/2;
    int comp = lua_strcmp(name, reserved[m].name);
    if (comp < 0)
      h = m-1;
    else if (comp == 0)
      return reserved[m].token;
    else
      l = m+1;
  }
  return 0;
}

[2.4]
void luaI_addReserved (void)
{
  int i;
  for (i=0; i<RESERVEDSIZE; i++)
  {
    TaggedString *ts = lua_createstring(reserved[i].name);
    ts->marked = reserved[i].token;  /* reserved word  (always > 255) */
  }
}

예약어를 검사할 때, findReserved() 함수를 호출해서 바이너리 서치를 하는 대신 아예 초기화할 때 가비지 컬랙션 용 마킹하는 marked 변수에 토큰 값을 넣습니다. 어차피 marked 변수 값은 0이거나 1일 때만 의미 있기 때문에 토큰 값을 2 이상 값으로 설정하고 의미를 다르게 쓰면 더 빠르게 처리 가능할 겁니다. 그런데 저렇게 쓰면 하나의 이름으로 역할이 두 개기 때문에 그다지 좋은 테크닉은 아닙니다. 저라면 같은 메모리 공간을 쓰면서 이름은 다르게 해서 의미를 명확히 할것 같습니다. marked 변수 자리를 공용체로 선언해서 의미를 명확하게 하는 것이죠.

[2.2]
int yylex (void)
{
  float a;
  static int linelasttoken = 0;
  if (lua_debug)
    luaI_codedebugline(linelasttoken);
  linelasttoken = lua_linenumber;
  while (1) 
:
:
후략

[2.4]
int luaY_lex (void)
{
  double a;
  static int linelasttoken = 0;
  if (lua_debug)
    luaI_codedebugline(linelasttoken);
  linelasttoken = lua_linenumber;
  while (1)
:
:
후략

yylex() 함수 이름을 luaY_lex()로 바꿨습니다. 왜 이랬을까요? yylex()는 yacc이 자동으로 만드는 코드에서 호출하는 이름이라서 함수 이름을 바꾸면 자동 생성 코드를 수정해야 하는데 뭐하러 그런 쓸데 없는 추가 작업을 사서하는 걸까요? 제가 보기에는 쓸데 없는 변경으로 여겨집니다.

[2.2]
	if (lua_strcmp(yytext, "debug") == 0)
	{
	  yylval.vInt = 1;
	  return DEBUG;
        }
	else if (lua_strcmp(yytext, "nodebug") == 0)
	{
	  yylval.vInt = 0;
	  return DEBUG;
        }

[2.4]
	if (strcmp(yytext, "debug") == 0)
	{
	  luaY_lval.vInt = 1;
	  return DEBUG;
        }
	else if (strcmp(yytext, "nodebug") == 0)
	{
	  luaY_lval.vInt = 0;
	  return DEBUG;
        }

제가 지난 읽기에서 지적했던 대로 따로 만든 문자열 비교 매크로는 그냥 strcmp() 함수를 쓰는 것에 비해 아무런 이득이 없습니다. 그래서 2.4 버전부터는 그냥 strcmp() 함수를 쓰네요. 루아 개발자들도 알았나봅니다. 그리고 또 하나 눈에 띄는 것이 yylval 변수 이름도 luaY_lval로 바꿨다는 것입니다. 정말 쓸데없어 보이는 변경입니다. 이유를 모르겠군요.

[2.2]
      {
        Word res;
        do { save_and_next(); } while (isalnum(current) || current == '_');
        *yytextLast = 0;
        res = findReserved(yytext);
        if (res) return res;
        yylval.pNode = lua_constcreate(yytext);
        return NAME;
      }

[2.4]
      {
        TaggedString *ts;
        do { save_and_next(); } while (isalnum(current) || current == '_');
        *yytextLast = 0;
        ts = lua_createstring(yytext);
        if (ts->marked > 2)
          return ts->marked;  /* reserved word */
        luaY_lval.pTStr = ts;
        ts->marked = 2;  /* avoid GC */
        return NAME;
      }

lexical analysis에서 NAME 토큰을 찾으면 일단 예약어인지 확인해야 합니다. 루아 2.2까지는 findReserved() 함수를 호출했는데, 루아 2.4에서는 그냥 marked 변수 값을 봐서 2 이상이면 예약어로 간주합니다. 하나의 변수 이름에 두 의미를 담은 것이 좀 아쉬운 코딩이네요.

그리고 루아 2.2까지 lex.c 파일의 switch-case 문 아래쪽에 있었던, 제가 왜 있는지 모르겠다고 언급했던 코드가 삭제됐습니다. 제 눈이 영 틀리진 않았군요.

댓글남기기