관리 메뉴

Value Creator의 IT(프로그래밍 / 전자제품)

#7 [LUA 1.1] lex.c 파일 읽어보기 본문

1. 프로그래밍/2) LUA

#7 [LUA 1.1] lex.c 파일 읽어보기

valuecreatort 2019. 8. 23. 17:43
반응형

LEX 파일 : 프로그래밍 언어 문법이 들어있다. 토큰에 대한 정규 표현식을 기술한다.

 

Lua.lex 파일 대신 lex.c 파일을 사용한다.

 

y.tab.c --> yacc에 대한 내용이 들어있다.(문장을 해석해주는 역할) 파서(Parser?)

 

y.tab.h --> yacc을 실행하고 나면 lex 토큰을 기준으로 문장을 해석한 결과에 대한 헤더파일을 생성한다.

 

기존의 LEX를 사용하면, 속도가 느리다. lex.c를 이용해서 직접 LUA 전용 낱말분석기(lexer)를 만들었더니 2배 이상 빨라졌다고 합니다.

 

아래에 lua의 lex.c 원본 소스코드와 나름의 주석을 달아보았습니다.

 

이곳저곳에서 찾아본 결과입니다.

 

char *rcs_lex = "$Id: lex.c,v 2.1 1994/04/15 19:00:28 celes Exp $";
/*$Log: lex.c,v $
 * Revision 2.1  1994/04/15  19:00:28  celes
 * Retirar chamada da funcao lua_findsymbol associada a cada
 * token NAME. A decisao de chamar lua_findsymbol ou lua_findconstant
 * fica a cargo do modulo "lua.stx".
 -->lua_findsymbol 함수에서 각 NAME 토큰 호출 제거됨.
 -->lua_findsymbol 또는 lua_findconstant는 lua.stx 모듈에서 호출함.
 *
 * Revision 1.3  1993/12/28  16:42:29  roberto
 * "include"s de string.h e stdlib.h para evitar warnings
 --> string.h와 stdlib.h를 include 하였다.
 
 * Revision 1.2  1993/12/22  21:39:15  celes
 * Tratamento do token $debug e $nodebug
 --> $debug 및 $nodebug를 토큰으로 처리하였다.
 
 * Revision 1.1  1993/12/22  21:15:16  roberto
 * Initial revision
 **/

#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "opcode.h"
#include "hash.h"
#include "inout.h"
#include "table.h"
#include "y.tab.h"

//--> 위 include들이 의미하는 바는 추후에 생각하도록 한다.
//--> ctype.h, math.h, stdlib.h, string.h는 기본 C언어에서 쓰는 것과 동일할 것 같다.


#define next() { current = input(); }
#define save(x) { *yytextLast++ = (x); }
#define save_and_next()  { save(current); next(); }
//--> next()는 current로 치환된다.
//--> save()는 yytext로 치환된다.
//--> save_and_next()는 yytextLast로 치환된다.
//--> 바로 아래에 보면 current, yytext, yytextLast가 있다.

static int current;
static char yytext[256];
static char *yytextLast;
// current는 현재 분석하려는 낱말이다.
// yytext는 분석을 완료한 문자열이다.
// yytextLast는 yytext에 1글자씩 넣기 위한 문자열 포인터이다.


static Input input;

void lua_setinput (Input fn)
{
  current = ' ';
  input = fn;
}
// current를 비우고, input에 fn 이라는 함수 포인터를 저장한다.
// fn이라는 값은 lua_open_file()과 lua_openstring()에서 호출한다. -->inout.c 파일 참조. 최하단부 참조.
// lua_setinput()은 1글자씩 입력을 받아 온다.
// Input 함수에 대한 포인터이다.
// lua_openfile()은 fileinput()의 함수 포인터를 이용해서 lua 코드를 받아온다.
// lua_openstring()은 stringinput()의 함수 포인터를 이용해서 lua 코드를 받아온다.

char *lua_lasttext (void)
{
  *yytextLast = 0;
  return yytext;
}
// yytextLast를 0으로 만들고, yytext를 반환한다.?

static struct 
  {
    char *name;
    int token;
  } reserved [] = {
      {"and", AND},
      {"do", DO},
      {"else", ELSE},
      {"elseif", ELSEIF},
      {"end", END},
      {"function", FUNCTION},
      {"if", IF},
      {"local", LOCAL},
      {"nil", NIL},
      {"not", NOT},
      {"or", OR},
      {"repeat", REPEAT},
      {"return", RETURN},
      {"then", THEN},
      {"until", UNTIL},
      {"while", WHILE} };
// 토큰의 종류를 나타내는 정적 전역 변수
// 위 토큰은 예약어라고 부른다.

#define RESERVEDSIZE (sizeof(reserved)/sizeof(reserved[0]))
// 예약어 구조체의 항목 개수를 나타낸다.



// 예약어 구조체에서 예약어를 찾은 후 토큰을 반환하는 함수.
int findReserved (char *name)
{
  int l = 0;
  int h = RESERVEDSIZE - 1;
  while (l <= h)
  {
    int m = (l+h)/2;
    int comp = 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;
}

// yylex() 함수가 핵심이다.
// 토큰을 찾을 때까지 yylex()를 반복한다. 토큰을 찾으면 해당 토큰을 반환한다.
// 즉, yylex()는 예약어 토큰을 찾아서 반환해주는 함수이다.
// lex파일을 lex 툴에 돌려서 C 파일을 만들면, 그 내부에 yylex() 함수를 만든다.
// 그러면 yacc 툴이 만든 yyparse() 함수에서 토큰이 필요할 때마다 yylex() 함수를 호출한다.
// yytext는 256바이트 문자 타입 배열이기 때문에 256자가 넘어가는 토큰 처리 불가.

int yylex ()
{
  while (1)
  {  
    yytextLast = yytext; // yytextLast 변수를 yytext 문자열 배열의 주소로 초기화한다.
    					 // 토큰 값을 초기화한다고 볼 수 있다.
                         // 포인터를 늘리면서 yytextLast 변수에 토큰 값을 넣을 것이다.
    switch (current)
    {
      case '\n': lua_linenumber++; //줄바꿈이 나온 경우 linenumber 1 증가
      case ' ':
      case '\t':
        next();
        continue; //공백이나 탭이 나오면 next()를 호출해서 1글자 입력받고 yytext 초기화

      case '$':
        next();
        while (isalnum(current) || current == '_')
              save_and_next();
            *yytextLast = 0;
        //'$' 다음에 알파벳이나 숫자 혹은 '_'이 나오면 yytext에 저장한다.
        //$abc123debug --> yytext에 abc123을 넣고 debug토큰을 리턴한다.

        if (strcmp(yytext, "debug") == 0)
        {
          yylval.vInt = 1;
          return DEBUG;
            }
        else if (strcmp(yytext, "nodebug") == 0)
        {
          yylval.vInt = 0;
          return DEBUG;
            }
        return WRONGTOKEN;
  
  	  //'-'가 1개면 마이너스를 의미하고, '--'처럼 2개면 주석을 의미한다.
      case '-':
        save_and_next();
        if (current != '-') return '-';
        do { next(); } while (current != '\n' && current != 0);
        continue;
  
	  //LE(Less Equal)인지, LT(Less Than)인지 체크한다.		  		
      case '<':
        save_and_next();
        if (current != '=') return '<';
        else { save_and_next(); return LE; }
  
  	  //GE(Greater Equal)인지, GT(Greater Than)인지 체크한다.
      case '>':
        save_and_next();
        if (current != '=') return '>';
        else { save_and_next(); return GE; }
  
  	  //NE(Not Equal)인지, NOT인지 체크한다.
      case '~':
        save_and_next();
        if (current != '=') return '~';
        else { save_and_next(); return NE; }


 	  //큰 따옴표, 작은 따옴표 모두 문자열로 취급한다.
      //문자열 중간에 개행(\')은 불가하다.
      //개행 문자가 들어가면 문자열의 마지막으로 본다.
      //lua_findconstant() 함수로 yytext를 넘겨서 숫자를 반환받고, STRING 토큰을 반환한다. --> 최하단부 참조
           
      case '"':
      
     
      case '\'':
      {
        int del = current;
        next();  /* skip the delimiter */
        while (current != del) 
        {
          switch (current)
          {
            case 0: 
            case '\n': 
              return WRONGTOKEN;
            case '\\':
              next();  /* do not save the '\' */
              switch (current)
              {
                case 'n': save('\n'); next(); break;
                case 't': save('\t'); next(); break;
                case 'r': save('\r'); next(); break;
                default : save('\\'); break;
              }
              break;
            default: 
              save_and_next();
          }
        }
        next();  /* skip the delimiter */
        *yytextLast = 0;
        yylval.vWord = lua_findconstant (yytext);
        return STRING;
      }
	
      // 알파벳이나 '_'로 시작하는 글자가 오면 일단 NAME 토큰으로 간주하고 검사한다.
      // 알파벳이나 숫자, '_'가 아닐 때까지 계속 글자를 넘기면서 yytext에 저장하다가, 그것이 아닌 글자가 나오면 스캔 중단
      // 스캔 중단 후 yytext가 예약어인지 검사한다.
      // 예약어라면 어떤 예약어인지 인덱스를 리턴한다.
      // yacc에 예약어 토큰의 문자열 포인터를 넘겨주고, NAME 토큰을 리턴한다.
		
      case 'a': case 'b': case 'c': case 'd': case 'e':
      case 'f': case 'g': case 'h': case 'i': case 'j':
      case 'k': case 'l': case 'm': case 'n': case 'o':
      case 'p': case 'q': case 'r': case 's': case 't':
      case 'u': case 'v': case 'w': case 'x': case 'y':
      case 'z':
      case 'A': case 'B': case 'C': case 'D': case 'E':
      case 'F': case 'G': case 'H': case 'I': case 'J':
      case 'K': case 'L': case 'M': case 'N': case 'O':
      case 'P': case 'Q': case 'R': case 'S': case 'T':
      case 'U': case 'V': case 'W': case 'X': case 'Y':
      case 'Z':
      case '_':
      {
        int res;
        do { save_and_next(); } while (isalnum(current) || current == '_');
        *yytextLast = 0;
        res = findReserved(yytext);
        if (res) return res;
        yylval.pChar = yytext;
        return NAME;
      }
   
   
   	  //.이 나오면 소수점인지, 문자열을 연결하는 Concatenate인지 구분한다. CONC 토큰.
      //다음 글자가 숫자가 아니면 .을 리턴하고, 숫자면 fraction 레이블로 점프한다.
      case '.':
        save_and_next();
        if (current == '.') 
        { 
          save_and_next(); 
          return CONC;
        }
        else if (!isdigit(current)) return '.';
        /* current is a digit: goes through to number */
        goto fraction;


	  //소수점, 지수 표기법, 음수 모두 처리한다.
      //숫자를 atof로 바꾼 다음에 yacc로 전달한다.
      //토큰은 NUMBER 이다.
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
      
        do { save_and_next(); } while (isdigit(current));
        if (current == '.') save_and_next();
fraction: while (isdigit(current)) save_and_next();
        if (current == 'e' || current == 'E')
        {
          save_and_next();
          if (current == '+' || current == '-') save_and_next();
          if (!isdigit(current)) return WRONGTOKEN;
          do { save_and_next(); } while (isdigit(current));
        }
        *yytextLast = 0;
        yylval.vFloat = atof(yytext);
        return NUMBER;

      default: 		/* also end of file */
      {
        save_and_next();
        return *yytext;      
      }
    }
  }
}
        
        
        
        
        
        
        
        
        
//---------------------------------------------------------//        
/*참조용 소스코드
int lua_openfile (char *fn)
{
 lua_linenumber = 1;
 lua_setinput (fileinput);
 fp = fopen (fn, "r");
 if (fp == NULL) return 1;
 if (lua_addfile (fn)) return 1;
 return 0;
}

int lua_openstring (char *s)
{
 lua_linenumber = 1;
 lua_setinput (stringinput);
 st = s;
 {
  char sn[64];
  sprintf (sn, "String: %10.10s...", s);
  if (lua_addfile (sn)) return 1;
 }
 return 0;
}

/*
** Function to get the next character from the input file
*/
static int fileinput (void)
{
 int c = fgetc (fp);
 return (c == EOF ? 0 : c);
}

/*
** Function to get the next character from the input string
*/
static int stringinput (void)
{
 st++;
 return (*(st-1));
}

// 즉, 입력 수단에 상관없이 1글자씩 입력 받아 오려고 Input 함수 포인터를 만들었다.
// next() 함수를 사용하면 fileinput() 또는 stringinput() 둘 중 하나가 실행된다.
// current 정적 전역 변수의 입력 데이터에서 1글자씩 읽어서 저장한다.

int lua_findconstant (char *s)
{
 int i;
 for (i=0; i<lua_nconstant; i++)
  if (streq(s,lua_constant[i]))
   return i;
 if (lua_nconstant >= MAXCONSTANT-1)
 {
  lua_error ("lua: constant string table overflow"); 
  return -1;
 }
 {
  char *c = calloc(strlen(s)+2,sizeof(char));
  c++;		/* create mark space */
  lua_constant[lua_nconstant++] = strcpy(c,s);
 }
 return (lua_nconstant-1);
}
// 문자열을 배열에 저장하고 있음.
// 배열에 저장되어 있는 문자열하고 똑같은 문자열이 파라메터로 오면 배열 인덱스를 리턴, 아니면 배열에 새로 추가.
// lua_nconstant는 배열 마지막 인덱스를 기록하는 변수.
// lua_constant는 문자열 배열

//아래는 lua_constant이다.
#ifndef MAXCONSTANT
#define MAXCONSTANT	256
#endif
/* pre-defined constants need garbage collection extra byte */ 
static char tm[] = " mark";
static char ti[] = " nil";
static char tn[] = " number";
static char ts[] = " string";
static char tt[] = " table";
static char tf[] = " function";
static char tc[] = " cfunction";
static char tu[] = " userdata";
static char  	       *constantbuffer[MAXCONSTANT] = {tm+1, ti+1,
						       tn+1, ts+1,
						       tt+1, tf+1,
						       tc+1, tu+1
                                                      };
char  	      	      **lua_constant = constantbuffer;
//문자열은 256개까지만 처리 가능
//기본값으로 문자열 8개를 배열에 미리 넣어둔다.
//mark, nil, number, string, table, function, cfunction, userdata가 들어있다.

*/
반응형
Comments