[루아2.2] lex.c

2 분 소요

Lua 2.2

루아 2.1 소스 코드를 다 읽고 이제 루아 2.2 소스 코드입니다. 릴리즈 버전 넘버링은 마이너 업데이트입니다. 그래서 지난번에 계획한 대로 마이너 업데이트일 때는 전체 코드를 다 읽는 대신 변경 부분만 집중적으로 읽겠습니다. 시간을 절약하기 위해서죠. 그리고 제가 지치는 것을 막기위한 고육지책이기도 합니다.

그래서 마이너 업데이트 코드를 읽을 때는 이전 버전 소스 코드를 함께 제시해서 비교할 수 있도록 구성하겠습니다.

본격 코드 읽기를 시작하기 전에 README 파일에 있는 2.2의 변경점을 한 번 짚고 가겠습니다.

* Changes since version 2.1  (current version is 2.2)
  + functions now may be declared with any "lvalue" as a name
  + garbage collection of functions
  + support for pipes

이렇다는 군요. 함수를 lvalue 이름으로 선언 할 수 있다는 말이 뭔지 모르겠습니다. 람다 함수 같은게 가능하다는 건가? 그리고 이제 함수도 가비지 컬랙션 대상이라는 군요. 마지막으로 파이프를 지원한답니다.

lex.c

lexical analysis를 하는 lex.c입니다. 언제나처럼 가장 먼저 읽는 파일입니다. 전체적으로 구현은 크게 변한 것 없으나 몇 부분에서 구현 방식이 조금 바뀌었습니다. 순서대로 변경 부분을 읽으면서 의미를 파악하겠습니다.

[2.1]
static int current;
static char yytext[256];
static char *yytextLast;

[2.2]
static int current;
static char *yytext = NULL;
static int textsize = 0;
static char *yytextLast;

루아 2.1에서 정적 배열로 256바이트였던 yytext 문자열 버퍼를 루아 2.2에서는 그냥 포인터로 바꾸고 null로 초기화했습니다. 아마도 동적 할당으로 바꿀건가 봅니다.

[2.1]
void lua_setinput (Input fn)
{
  current = ' ';
  input = fn;
}

[2.2]
void lua_setinput (Input fn)
{
  current = ' ';
  input = fn;
  if (yytext == NULL)
  {
    textsize = MINBUFF;
    yytext = newvector(textsize, char);
  }
}

네, 진짜 yytext를 동적 할당으로 바꿨습니다. 동적 할당으로 바꾸는 것이 어떤 이득이 있을까요? 저는 개인적 의견으로는 이 변수는 그냥 정적 배열로 두는 것이 더 나을것 같습니다.

static void growtext (void)
{
  int size = yytextLast - yytext;
  textsize *= 2;
  yytext = growvector(yytext, textsize, char);
  yytextLast = yytext + size;
}

이 함수는 루아 2.2에 새로 추가한 함수입니다. 이 함수를 보니 왜 yytext에 정적 배열을 포기하고 동적 할당으로 바꿨는지 알 수 있습니다. 루아 개발자들은 루아가 토큰에 대해 길이 제한이 있는 것이 싫었던 모양입니다. 그래서 yytext에 할당한 버퍼 크기가 부족하면 버퍼 크기를 늘리게끔 만들었습니다. 이유가 납득됩니다.

static int read_long_string (void)
{
  int cont = 0;
  int spaceleft = textsize - (yytextLast - yytext);
  while (1)
  {
    if (spaceleft <= 2)  /* may read more than 1 char in one cicle */
    {
      growtext();
      spaceleft = textsize - (yytextLast - yytext);
    }
    switch (current)
    {
      case EOF:
      case 0:
        return WRONGTOKEN;
      case '[':
        save_and_next(); spaceleft--;
        if (current == '[')
        {
          cont++;
          save_and_next(); spaceleft--;
        }
        continue; 
      case ']':
        save_and_next(); spaceleft--;
        if (current == ']')
        {
          if (cont == 0) return STRING;
          cont--;
          save_and_next(); spaceleft--;
        }
        continue; 
      case '\n':
        lua_linenumber++;  /* goes through */
      default:
        save_and_next(); spaceleft--;
    }
  }
}

루아 2.2에 새로 추가한 함수입니다. 뭐하는 함수일까요? 함수 내용만 읽어보면, 대괄호([]) 사이에 있는 모든 입력을 읽어서 대괄호의 열고 닫은 개수가 맞으면 STRING 토큰을 리턴합니다. 버퍼 길이가 모자르면 버퍼를 늘립니다. 대괄호의 열고 닫은 개수가 일치하지 않으면 WRONGTOKEN을 리턴합니다.

      case '\r':  /* CR: to avoid problems with DOS/Windows */

루아 2.2에서 새로 추가한 코드입니다. 아마 루아 2.1까지는 *nix에서만 루아 테스트하다가 루아 2.2부터 윈도우, 도스에서 작성한 루아 코드를 테스트했는가봅니다. 이거 꽤 귀찮죠.

      case '[':
        save_and_next();
        if (current != '[') return '[';
        else
        {
          save_and_next();  /* pass the second '[' */
          if (read_long_string() == WRONGTOKEN)
            return WRONGTOKEN;
          save_and_next();  /* pass the second ']' */
          *(yytextLast-2) = 0;  /* erases ']]' */
          yylval.vWord = luaI_findconstantbyname(yytext+2);
          return STRING;
        }

위에서 읽은 read_long_string() 함수를 사용하는 루아 2.2에 새로 추가한 코드입니다. luaI_findconstantbyname() 함수를 호출하는 것으로 보아 테이블 타입 변수의 아이템을 문자열 키로 읽는 문법을 분석하는 코드로 보입니다.

댓글남기기