관리 메뉴

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

#8 [LUA 1.1] lua.stx 파일 읽어보기 본문

1. 프로그래밍/2) LUA

#8 [LUA 1.1] lua.stx 파일 읽어보기

valuecreatort 2019. 8. 26. 19:36
반응형

 

 

 

 

 

lua.stx는 lua 문법을 구현한 yacc 파일.

 

 

%{

char *rcs_luastx = "$Id: lua.stx,v 2.4 1994/04/20 16:22:21 celes Exp $";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mm.h"

#include "opcode.h"
#include "hash.h"
#include "inout.h"
#include "table.h"
#include "lua.h" //호스트 프로그램에서 루아를 라이브러리로 임베드 할 때 사용하는 헤더파일

#define LISTING 0 //디버그용 플래그? 프린트 코드?

#ifndef GAPCODE
#define GAPCODE 50 //바이트 코드(byte code)가 들어갈 메모리 공간을 확보할 때 기본값으로 지정하는 메모리 공간 크기?
#endif
static Word   maxcode;
static Word   maxmain;
static Word   maxcurr ;
static Byte  *code = NULL;
static Byte  *initcode; //바이트 코드 초드 위치, lua_parse에서 초기화
static Byte  *basepc; //시작하는 바이트 코드 주소
static Word   maincode;
static Word   pc; //현재 바이트 코드 위치
//Byte는 1바이트, Word는 2바이트. 루아 VM은 2바이트 워드 머신?
//현재 실행 중인 바이트 코드, 지금까지 번역한 바이트 코드의 위치?

#define MAXVAR 32
static long    varbuffer[MAXVAR];    /* variables in an assignment list;
				it's long to store negative Word values */
static int     nvarbuffer=0;	     /* number of variables at a list */

static Word    localvar[STACKGAP];   /* store local variable names */
static int     nlocalvar=0;	     /* number of local variables */
//루아 코드에서 선언한 변수들의 심볼 테이블 인덱스 저장
//var 규칙의 최종 결과를 varbuffer[nvarbuffer]에 넣는다.
/***********아래코드참조
varlist1  :	var			
	  {
	   nvarbuffer = 0; 
           varbuffer[nvarbuffer] = $1; incr_nvarbuffer();
*********************/

#define MAXFIELDS FIELDS_PER_FLUSH*2
static Word    fields[MAXFIELDS];     /* fieldnames to be flushed */
static int     nfields=0;
static int     ntemp;		     /* number of temporary var into stack */
static int     err;		     /* flag to indicate error */

/***********아래코드참조
static void push_field (Word name)
{
  if (nfields < STACKGAP-1)
    fields[nfields++] = name;
  else
  {
   lua_error ("too many fields in a constructor");
   err = 1;
  }
}

push_field()라는 함수에서 씁니다. 함수 내용 자체는 어렵지 않습니다. 그냥 field 배열에 파라메터로 넘어오는 name을 넣고 끝입니다. 그러면 push_field()는 어디서 쓸까요?

ffield      : NAME {$<vWord>$ = lua_findconstant($1);} '=' expr1 
	      { 
	       push_field($<vWord>2);
	      }
           ;

여기서 유일하게 push_field() 함수를 쓰는데요.  
다 파악하려면 ffield 문법을 따라서 다 봐야 할 것 같아서 어차피 나중에 읽을 거니까 일단 넘어가고 루아 문서에서 field라는 용어를 어떻게 쓰는지 봤습니다.
field 뜻 원문 : Variables are places that store values. There are three kinds of variables in Lua: global variables, local variables, and table fields.
루아에 테이블 자료 구조가 있습니다. 연관 배열(associated array) 같은 겁니다. 그래서 아이템 하나가 이름을 가질 수 있습니다.

phone['john'] = '111-222-3333'
phone['ann'] = '123-123-1234'

이런식으로 선언할 수 있습니다. 여기서 ‘john’과 ‘ann’이 필드라는 겁니다. 일종의 변수 이름이기 때문에 따로 관리하는 것이었습니다.
*********************/

/* Internal functions */
//인터널 함수의 종류는 아래의 11개.
/***
    code_byte()
    code_word()
    code_float()
    code_word_at()
    push_field()
    flush_record()
    flush_list()
    incr_ntemp()
    add_nlocalvar()
    incr_nvarbuffer
    code_number()
***/


static void code_byte (Byte c)
{
 if (pc>maxcurr-2)  /* 1 byte free to code HALT of main code */
 {
  maxcurr += GAPCODE; //현재 pc가 (maxcurr-2)를 넘으면 maxcurr를 GAPCODE만큼 증가시킴(+50) 
					  //즉, 50씩 바이트 코드 스택을 증가시킴.
  basepc = (Byte *)realloc(basepc, maxcurr*sizeof(Byte)); //basepc라는 바이트 코드 스택에 바이트 코드 저장.
  if (basepc == NULL)
  {
   lua_error ("not enough memory");
   err = 1;
  }
 }
 basepc[pc++] = c; //바이트 코드 위치는 pc 변수로 저장.
}


//2바이트인 워드를 1바이트씩 끊어서 접근할 수 있도록 함.
//code_byte() 함수를 두 번 호출.
static void code_word (Word n)
{
 CodeWord code;
 code.w = n;
 code_byte(code.m.c1);
 code_byte(code.m.c2);
}

/*****아래 참조
CodeWord라는 새로운 타입이 등장했습니다. Opcode.h에 정의가 있습니다.

typedef union
{
 struct {char c1; char c2;} m;
 Word w;
} CodeWord;

************/

//위의 code_word와 같은 형태.
//4바이트를 1바이트씩 끊어서 접근 하는 공용체.
static void code_float (float n)
{
 CodeFloat code;
 code.f = n;
 code_byte(code.m.c1);
 code_byte(code.m.c2);
 code_byte(code.m.c3);
 code_byte(code.m.c4);
}


//p라는 포인터 위치에 Word 타입 값을 덮어쓰기 하는 함수.
//명령어 문의 Body를 구분.
//바이트 코드를 JMP 하기 위한 함수로 IFFJMP, JMP 관련 있음.
static void code_word_at (Byte *p, Word n)
{
 CodeWord code;
 code.w = n;
 *p++ = code.m.c1;
 *p++ = code.m.c2;
}


static void push_field (Word name)
{
  if (nfields < STACKGAP-1)
    fields[nfields++] = name; //field 배열에 name을 넣는다.
  else
  {
   lua_error ("too many fields in a constructor");
   err = 1;
  }
}


static void flush_record (int n)
{
  int i;
  if (n == 0) return;
  code_byte(STORERECORD); //바이트 코드 스택에 STORERECORD 명령어를 넣는다.
  code_byte(n); 
  for (i=0; i<n; i++) //n만큼 반복하면서 field 배열의 값을 코드 버퍼에 넣는다.
    code_word(fields[--nfields]);
  ntemp -= n;
}

static void flush_list (int m, int n) //STORELIST0 또는 STORELIST 명령어를 바이트 코드 스택에 넣는다.
{
  if (n == 0) return;
  if (m == 0)
    code_byte(STORELIST0); 
  else
  {
    code_byte(STORELIST);
    code_byte(m); //그 다음 m, n 값을 바이트 코드 스택에 넣는다.
  }
  code_byte(n);
  ntemp-=n;
}


static void incr_ntemp (void)
{
 if (ntemp+nlocalvar+MAXVAR+1 < STACKGAP)
  ntemp++; //STACKGAP은 opcode.h에 128로 정의된 상수이다. (ntemp+nlocalvar+MAXVAR+1) 값이 128이 안되면 ntemp증가.
 else
 {
  lua_error ("stack overflow");
  err = 1;
 }
}


static void add_nlocalvar (int n)
{
 if (ntemp+nlocalvar+MAXVAR+n < STACKGAP)
  nlocalvar += n; //STACKGAP은 opcode.h에 128로 정의된 상수이다. (ntemp+nlocalvar+MAXVAR+n) 값이 128이 안되면 ntemp증가.
 else
 {
  lua_error ("too many local variables or expression too complicate");
  err = 1;
 }
}


static void incr_nvarbuffer (void)
{
 if (nvarbuffer < MAXVAR-1)
  nvarbuffer++; //nvarbuffer값이 MAXVAR를 넘지 않으면 nvarbuffer를 1 증가.
 else
 {
  lua_error ("variable buffer overflow");
  err = 1;
 }
}

//루아의 number 타입은 4바이트이다.
//루아는 1바이트 단위 스택 머신이므로 앞자리에 0이 매번 채워질 가능성이 있다.
//매번 4바이트를 다 사용하면 낭비이므로 2바이트 Word인지 먼저 확인 후에 4바이트를 사용할지 결정한다.
static void code_number (float f)
{ Word i = (Word)f; //f가 2바이트인지 확인하려고 Word로 캐스팅함.
  if (f == (float)i)  /* f has an (short) integer value */
  {
   if (i <= 2) code_byte(PUSH0 + i); //PUSHBYTE와0,1,2를 조합하면 2바이트를 차지한다.
									 //PUSH0, PUSH1, PUSH2 단일명령어로 하면 1바이트 차지한다.
                                     //성능을 높이고 스택 크기를 줄이는 전략.
   else if (i <= 255)
   {
    code_byte(PUSHBYTE);
    code_byte(i);
   }
   else
   {
    code_byte(PUSHWORD);
    code_word(i);
   }
  }
  else
  {
   code_byte(PUSHFLOAT);
   code_float(f);
  }
  incr_ntemp();
}

%}
//여기까지 인터널 함수 구현.


//아래부터는 yacc 토큰 부분.
//%union은 C 공용체 같은 것. 선언한 타입으로 토큰과 규칙의 최종값을 캐스팅해서 사용한다.
//%token으로 사용할 토큰 종류를 정한다.
//y.tab.h에 토큰이 선언된다.
//%left는 연산자 우선순위를 결정한 것이다.
//%type은 해당 문법 규칙을 전개하면 어떤 타입으로 처리해야 하는지 정한 것.
//yacc에서 문법을 다 해석하고 나면 최종 결과를 어떤 타입으로 상위 문법으로 전달할지 지정.
//expr 문법은 문법 파싱을 다 끝낸 후에 상위 문법으로 결과를 전달(int 타입으로 전달)
%union 
{
 int   vInt;
 long  vLong;
 float vFloat;
 char *pChar;
 Word  vWord;
 Byte *pByte;
}

%start functionlist

%token WRONGTOKEN
%token NIL
%token IF THEN ELSE ELSEIF WHILE DO REPEAT UNTIL END
%token RETURN
%token LOCAL
%token <vFloat> NUMBER
%token <vWord>  FUNCTION STRING
%token <pChar>   NAME 
%token <vInt>   DEBUG

%type <vWord> PrepJump
%type <vInt>  expr, exprlist, exprlist1, varlist1, typeconstructor
%type <vInt>  fieldlist, localdeclist
%type <vInt>  ffieldlist, ffieldlist1
%type <vInt>  lfieldlist, lfieldlist1
%type <vLong> var, objectname


%left AND OR
%left '=' NE '>' '<' LE GE
%left CONC
%left '+' '-'
%left '*' '/'
%left UNARY NOT


%% /* beginning of rules section */

//형태
//function 이름 (파라메터) 
//block
//END


functionlist : /* empty */
	     | functionlist 
	        {
	  	  pc=maincode; basepc=initcode; maxcurr=maxmain;
		  nlocalvar=0;
	        }
	       stat sc 
		{
		  maincode=pc; initcode=basepc; maxmain=maxcurr;
		}
	     | functionlist function
	     | functionlist setdebug
	     ;



//함수 이름을 파싱하면 바이트 코드 스택에 메모리 할당
function     : FUNCTION NAME 
	       {
		if (code == NULL)	/* first function */
		{
		 code = (Byte *) calloc(GAPCODE, sizeof(Byte));
		 if (code == NULL)
		 {
		  lua_error("not enough memory");
		  err = 1;
		 }
		 maxcode = GAPCODE;
		}
		pc=0; basepc=code; maxcurr=maxcode; 
		nlocalvar=0;
		$<vWord>$ = lua_findsymbol($2); 
	       }
	       '(' parlist ')' 
	       {
	        if (lua_debug)
		{
	         code_byte(SETFUNCTION); 
                 code_word(lua_nfile-1);
		 code_word($<vWord>3);
		}
	        lua_codeadjust (0); //파라메터 리스트를 파싱하면 lua_codeadjust()에 0을 넘김.
								//최하단부 lua_codeadjust() 참조
	       }
               block //함수 본체는 block 규칙으로 처리.
               END   //END 토큰으로 함수 끝 표시.
	       { 
                if (lua_debug) code_byte(RESET); 
	        code_byte(RETCODE); //함수 끝 파싱하고 나면 RETCODE 명령어를 바이트 코드 스택에 넣음.
            code_byte(nlocalvar); //로컬 변수 개수를 바이트 코드 스택에 넣음. 
	        s_tag($<vWord>3) = T_FUNCTION; //$3은 $3을 vWord 타입으로 캐스팅한다는 뜻.
            							   //$3은 문법 코드대로라면 parlist
                                           //parlist 규칙은 최종적으로 object 타입으로 파싱이 끝남.
	        s_bvalue($<vWord>3) = calloc (pc, sizeof(Byte));
		if (s_bvalue($<vWord>3) == NULL)
		{
		 lua_error("not enough memory");
		 err = 1;
		}
	        memcpy (s_bvalue($<vWord>3), basepc, pc*sizeof(Byte)); //바이트 스택 코드에 있는 내용을 object 타입으로 할당한 메모리에 복사
		code = basepc; maxcode=maxcurr; //code 변수를 다시 앞으로 당김(basepc)
#if LISTING
PrintCode(code,code+pc);
#endif
	       }
	       ;


//stat과 sc과 반복되는 규칙.
//stat 규칙은 그대로 stat1 규칙으로 넘어간다. 
//stat은 stat1 규칙을 전개하기 전에 처리할 C언어 코드를 넣으려고 만든 규칙 같음.
statlist : /* empty */
	 | statlist stat sc
	 ;

stat	 : {
            ntemp = 0; 
            if (lua_debug)
            {
             code_byte(SETLINE); code_word(lua_linenumber);
            }
	   }
	   stat1

//sc는 세미콜론인가? lua 에서는 ;이 있는 것과 없는 것 둘 다 허용함.
sc	 : /* empty */ | ';' ;



//stat1은 statement에 해당하는 문법 규칙이며, 4가지 statemnet가 있다.
//문법규칙 4가지 : if, while, repeat, assignment
/*******아래 참조
stat1은 프로그래밍 언어 이론에서 말하는 statement에 해당하는 문법 규칙입니다.
중요 문법을 다 여기에 정의합니다. 흔히 구문이라는 용어로 번역하는 바로 그것입니다.
1. 첫번째 statement 문법은 조건문입니다. 루아는 조건문의 조건절에서 expression을 허용하네요.
   else 구문이 있으면 JMP 명령을 추가해서 else 구문이 있는 코드 주소로 점프하는 코드를 삽입합니다. 
   그리고 IFFJMP 명령을 바이트 코드 스택에 넣습니다. 문법을 파싱하면서 컴파일을 바로 바로 하네요.
   보면 볼 수록 파싱과 컴파일이 분리되지 않은 코드라 정돈이 되지 않은 느낌입니다.
   
2. 두번째 statement 문법은 while 반복문입니다.
   IFFJMP와 UPJMP가 왜 바이트 코드 스택에 들어가는지는 알 것 같습니다.
   전형적인 while 반복문 구현의 형태.

3. 세번째 statement 문법은 repeat 반복문입니다.
   C 언어의 do-while 문법하고 같은 동작을 하는 문법입니다.
   IFFUPJMP라는 명령어를 썼습니다. 스택값이나 변수값을 확인해서 어디론가 점프하는 뭐 그런류의 명령어.

4. 파이썬이나 루아의 특징적인 문법이 네 번째 statement 문법인데 변수 assignement 문법입니다.
   C 언어는 변수 한 개에 값 한 개 식으로 한 줄에 코딩하는데 파이썬이나 루아는 변수 여러개에 값 여러개를 한 줄에 한 번에 코딩할 수 있습니다.
   이어지는 함수 호출 문법, 타입 생성자, 로컬 변수 선언 모두 lua_codeadjust() 함수를 호출해서 파싱, 컴파일을 하는데요.    
***************/
stat1  : IF expr1 THEN PrepJump block PrepJump elsepart END
       {
        {
	 Word elseinit = $6+sizeof(Word)+1;
	 if (pc - elseinit == 0)		/* no else */
	 {
	  pc -= sizeof(Word)+1;
	  elseinit = pc;
	 }
	 else
	 {
	  basepc[$6] = JMP; //else 구문이 있으면 JMP 명령을 추가해서 else 구문이 있는 코드 주소로 점프하는 코드 삽입.
	  code_word_at(basepc+$6+1, pc - elseinit);
	 }
	 basepc[$4] = IFFJMP; //IFFJMP 명령을 바이트 코드 스택에 넣는다.
	 code_word_at(basepc+$4+1,elseinit-($4+sizeof(Word)+1));
	}
		//문법을 파싱하면서 곧바로 컴파일.
		//파싱과 컴파일이 분리되지 않음.
       }
     
     
       //전형적인 while 반복문 구조의 형태.
       | WHILE {$<vWord>$=pc;} expr1 DO PrepJump block PrepJump END
     	
       {
        basepc[$5] = IFFJMP;
	code_word_at(basepc+$5+1, pc - ($5 + sizeof(Word)+1));
        
        basepc[$7] = UPJMP;
	code_word_at(basepc+$7+1, pc - ($<vWord>2));
       }
       
       
       
       
       //repeat는 C언어의 do-while문법하고 같은 동작을 한다.
       | REPEAT {$<vWord>$=pc;} block UNTIL expr1 PrepJump
     	
       {
        basepc[$6] = IFFUPJMP;
	code_word_at(basepc+$6+1, pc - ($<vWord>2));
       }
	   
       
		//statement 문법. 
		//C언어는 변수 1개에 값 1개씩 한 줄에 코딩하는 것과 달리, 루아는 여러 변수를 한 라인에 assign 할 수 있다.
		//즉, 파이썬이나 루아에서는 변수 여러 개에 값 여러 개를 한 줄에, 한 번에 코딩 가능.
       | varlist1 '=' exprlist1
       {
        {
         int i;
         if ($3 == 0 || nvarbuffer != ntemp - $1 * 2)
	  lua_codeadjust ($1 * 2 + nvarbuffer);
	 for (i=nvarbuffer-1; i>=0; i--)
	  lua_codestore (i);
	 if ($1 > 1 || ($1 == 1 && varbuffer[0] != 0))
	  lua_codeadjust (0);
	}
       } 
       | functioncall			{ lua_codeadjust (0); }
       | typeconstructor                { lua_codeadjust (0); }
       | LOCAL localdeclist decinit   { add_nlocalvar($2); lua_codeadjust (0); }
       ;

//elseif-else 문법.
//else 문법을 쓸 때마다 조건에 따라 점프해야 할 주소를 계산한다.
//JMP, IFFJMP 명령어를 바이트 코드 스택에 넣는다.

elsepart : /* empty */
	 | ELSE block
         | ELSEIF expr1 THEN PrepJump block PrepJump elsepart
         {
          {
  	   Word elseinit = $6+sizeof(Word)+1;
  	   if (pc - elseinit == 0)		/* no else */
  	   {
  	    pc -= sizeof(Word)+1;
	    elseinit = pc;
	   }
	   else
	   {
	    basepc[$6] = JMP;
	    code_word_at(basepc+$6+1, pc - elseinit);
	   }
	   basepc[$4] = IFFJMP;
	   code_word_at(basepc+$4+1, elseinit - ($4 + sizeof(Word)+1));
	  }  
         }   
         ;


//block은 C언어로 치면 {와 } 사이 코드를 뜻한다.
//nlocalvar 변수 값으로 전개 결과를 돌려 보낸다. 
/****참조
statlist 문법에 로컬 변수 선언 문법이 포함되 있기 때문에 statlist 문법을 전개하기 전에 nlocalvar 변수값을 읽는 것은 직전 블록의 nlocalvar 변수값이거든요. 코드 흐름상의 이유로 이렇게 만들었나 봅니다. 루프 돌다 보면 이렇게 (A)가 end of (A-1)의 값을 가지고 있어야 하는 경우가 생기거든요. 이 코드도 그런 이유 아닐까 생각합니다.
*******/
block    : {$<vInt>$ = nlocalvar;} statlist {ntemp = 0;} ret 
         {
	  if (nlocalvar != $<vInt>1)
	  {
           nlocalvar = $<vInt>1;
	   lua_codeadjust (0);
	  }
         }
         ;


//루아는 함수 리턴에서 변수 여러개 리턴 가능.
//exprlist가 리턴 문법에 나옴.
ret	: /* empty */
        | { if (lua_debug){code_byte(SETLINE);code_word(lua_linenumber);}}
          RETURN  exprlist sc 	
          { 
           if (lua_debug) code_byte(RESET); 
           code_byte(RETCODE); code_byte(nlocalvar);
          }
	;

//C 언어 코드를 삽입하려는 목적으로 만든 문법.
//현재 pc 주소값을 규칙 전개 결과로 돌려보냄.
PrepJump : /* empty */
	 { 
	  $$ = pc;
	  code_byte(0);		/* open space */
	  code_word (0);
         }


//expr 문법은 프로그래밍 언어론에서 말하는 expression 문법.
//사칙 연산, 조건문 등을 계산한다.
//조건식, 사칙연산, 단항 연산자. 변수이름, 숫자, 문자열은 assignment 구문의 우항으로 사용한다.
//함수 호출, NOT으로 결과 반전하는 것도 마찬가지이다.
//조건문에서 AND, OR로 boolean 연산도 expression 문법으로 처리한다.
//expr1 문법도 expr 문법으로 바로 넘어간다. expr 문법을 전개한 다음에 lua_codeadjust() 함수와 incr_ntemp() 함수를 호출하려고 만든 문법 규칙이다.

expr1	 : expr { if ($1 == 0) {lua_codeadjust (ntemp+1); incr_ntemp();}}
	 ;
				
expr :	'(' expr ')'    { $$ = $2; }
     |	expr1 '=' expr1	{ code_byte(EQOP);   $$ = 1; ntemp--;}
     |	expr1 '<' expr1	{ code_byte(LTOP);   $$ = 1; ntemp--;}
     |	expr1 '>' expr1	{ code_byte(LEOP); code_byte(NOTOP); $$ = 1; ntemp--;}
     |	expr1 NE  expr1	{ code_byte(EQOP); code_byte(NOTOP); $$ = 1; ntemp--;}
     |	expr1 LE  expr1	{ code_byte(LEOP);   $$ = 1; ntemp--;}
     |	expr1 GE  expr1	{ code_byte(LTOP); code_byte(NOTOP); $$ = 1; ntemp--;}
     |	expr1 '+' expr1 { code_byte(ADDOP);  $$ = 1; ntemp--;}
     |	expr1 '-' expr1 { code_byte(SUBOP);  $$ = 1; ntemp--;}
     |	expr1 '*' expr1 { code_byte(MULTOP); $$ = 1; ntemp--;}
     |	expr1 '/' expr1 { code_byte(DIVOP);  $$ = 1; ntemp--;}
     |	expr1 CONC expr1 { code_byte(CONCOP);  $$ = 1; ntemp--;}
     |	'+' expr1 %prec UNARY	{ $$ = 1; }
     |	'-' expr1 %prec UNARY	{ code_byte(MINUSOP); $$ = 1;}
     | typeconstructor { $$ = $1; }
     |  '@' '(' dimension ')'
     { 
      code_byte(CREATEARRAY);
      $$ = 1;
     }
     |	var             { lua_pushvar ($1); $$ = 1;}
     |	NUMBER          { code_number($1); $$ = 1; }
     |	STRING
     {
      code_byte(PUSHSTRING);
      code_word($1);
      $$ = 1;
      incr_ntemp();
     }
     |	NIL		{code_byte(PUSHNIL); $$ = 1; incr_ntemp();}
     |	functioncall
     {
      $$ = 0;
      if (lua_debug)
      {
       code_byte(SETLINE); code_word(lua_linenumber);
      }
     }
     |	NOT expr1	{ code_byte(NOTOP);  $$ = 1;}
     |	expr1 AND PrepJump {code_byte(POP); ntemp--;} expr1
     { 
      basepc[$3] = ONFJMP;
      code_word_at(basepc+$3+1, pc - ($3 + sizeof(Word)+1));
      $$ = 1;
     }
     |	expr1 OR PrepJump {code_byte(POP); ntemp--;} expr1	
     { 
      basepc[$3] = ONTJMP;
      code_word_at(basepc+$3+1, pc - ($3 + sizeof(Word)+1));
      $$ = 1;
     }
     ;


//루아 문법에서 테이블을 선언할 때 생성자 함수 포인터를 지정하는 문법이다.
typeconstructor: '@'  
     {
      code_byte(PUSHBYTE);
      $<vWord>$ = pc; code_byte(0);
      incr_ntemp();
      code_byte(CREATEARRAY);
     }
      objectname fieldlist 
     {
      basepc[$<vWord>2] = $4; 
      if ($3 < 0)	/* there is no function to be called */
      {
       $$ = 1;
      }
      else
      {
       lua_pushvar ($3+1);
       code_byte(PUSHMARK);
       incr_ntemp();
       code_byte(PUSHOBJECT);
       incr_ntemp();
       code_byte(CALLFUNC); 
       ntemp -= 4;
       $$ = 0;
       if (lua_debug)
       {
        code_byte(SETLINE); code_word(lua_linenumber);
       }
      }
     }
         ;

dimension    : /* empty */	{ code_byte(PUSHNIL); incr_ntemp();}
	     | expr1
	     ;
	     
functioncall : functionvalue  {code_byte(PUSHMARK); $<vInt>$ = ntemp; incr_ntemp();}
                '(' exprlist ')' { code_byte(CALLFUNC); ntemp = $<vInt>2-1;}

functionvalue : var {lua_pushvar ($1); } 
	      ;
		
exprlist  :	/* empty */		{ $$ = 1; }
	  |	exprlist1		{ $$ = $1; }
	  ;
		
exprlist1 :	expr			{ $$ = $1; }
	  |	exprlist1 ',' {if (!$1){lua_codeadjust (ntemp+1); incr_ntemp();}} 
                 expr {$$ = $4;}
	  ;

//비어 있거나 콤마로 구분한 녀수 이름 나열
parlist  :	/* empty */
	  |	parlist1
	  ;
		
parlist1 :	NAME		  
		{
		 localvar[nlocalvar]=lua_findsymbol($1); 
		 add_nlocalvar(1);
		}
	  |	parlist1 ',' NAME 
		{
		 localvar[nlocalvar]=lua_findsymbol($3); 
		 add_nlocalvar(1);
		}
	  ;
	
//비어 있거나 변수 이름 1개.
//루아 테이블 자료 구조 선언할 때 생성자 이름을 지정하거나 생략할 때 사용
objectname :	/* empty */ 	{$$=-1;}
	   |	NAME		{$$=lua_findsymbol($1);}
	   ;


//field는 루아 테이블 자료구조의 아이템.
//중괄호{}나 대괄호[]로 묶은 블럭 안에 여러개 선언 가능.
//비어 있는 테이블도 선언 가능.
//각 필드는 콤마로 구분.
//필드는 "변수명 = 수식" 형태.
//이름 없는 값으로 수식을 연달아 쓸 수도 있으며 이 경우에는 C 언어의 배열처럼 사용.
fieldlist  : '{' ffieldlist '}'  
	      { 
	       flush_record($2%FIELDS_PER_FLUSH); 
	       $$ = $2;
	      }
           | '[' lfieldlist ']'  
	      { 
	       flush_list($2/FIELDS_PER_FLUSH, $2%FIELDS_PER_FLUSH);
	       $$ = $2;
     	      }
	   ;

ffieldlist : /* empty */   { $$ = 0; }
           | ffieldlist1   { $$ = $1; }
           ;

ffieldlist1 : ffield			{$$=1;}
	   | ffieldlist1 ',' ffield	
		{
		  $$=$1+1;
		  if ($$%FIELDS_PER_FLUSH == 0) flush_record(FIELDS_PER_FLUSH);
		}
	   ; 

ffield      : NAME {$<vWord>$ = lua_findconstant($1);} '=' expr1 
	      { 
	       push_field($<vWord>2);
	      }
           ;

lfieldlist : /* empty */   { $$ = 0; }
           | lfieldlist1   { $$ = $1; }
           ;

lfieldlist1 : expr1  {$$=1;}
	    | lfieldlist1 ',' expr1
		{
		  $$=$1+1;
		  if ($$%FIELDS_PER_FLUSH == 0) 
		    flush_list($$/FIELDS_PER_FLUSH - 1, FIELDS_PER_FLUSH);
		}
            ;



//varlist는 변수를 연속해서 기술하는 규칙.
//콤마로 구분해서 연속으로 기술함.
//변수 자체는 var 문법으로 표현
//변수 이름 자체를 쓰거나 C언어의 배열 표시처럼 대괄호로 인덱스를 지정. 인덱스는 수식으로 계산 가능. 인덱스를 점으로 구분 가능. A.idx처럼 사용.

varlist1  :	var			
	  {
	   nvarbuffer = 0; 
           varbuffer[nvarbuffer] = $1; incr_nvarbuffer();
	   $$ = ($1 == 0) ? 1 : 0;
	  }
	  |	varlist1 ',' var	
	  { 
           varbuffer[nvarbuffer] = $3; incr_nvarbuffer();
	   $$ = ($3 == 0) ? $1 + 1 : $1;
	  }
	  ;
		
var	  :	NAME
	  {
	   Word s = lua_findsymbol($1);
	   int local = lua_localname (s);
	   if (local == -1)	/* global var */
	    $$ = s + 1;		/* return positive value */
           else
	    $$ = -(local+1);		/* return negative value */
	  }
	  
	  |	var {lua_pushvar ($1);} '[' expr1 ']' 
	  {
	   $$ = 0;		/* indexed variable */
	  }
	  |	var {lua_pushvar ($1);} '.' NAME
	  {
	   code_byte(PUSHSTRING);
	   code_word(lua_findconstant($4)); incr_ntemp();
	   $$ = 0;		/* indexed variable */
	  }
	  ;


//LOCAL 토큰 뒤에 오는 문법 규칙.
//NAME 토큰이 콤마로 연속해서 나오는 다른 문법과 표현은 같지만, 해석할 때 값을 가져오는 변수가 localvar 배열이기 때문에 문법 분리.
localdeclist  : NAME {localvar[nlocalvar]=lua_findsymbol($1); $$ = 1;}
     	  | localdeclist ',' NAME 
	    {
	     localvar[nlocalvar+$1]=lua_findsymbol($3); 
	     $$ = $1+1;
	    }
	  ;
		
decinit	  : /* empty */
	  | '=' exprlist1
	  ;
	  
setdebug  : DEBUG {lua_debug = $1;}

%%

/*
** Search a local name and if find return its index. If do not find return -1
*/
static int lua_localname (Word n)
{
 int i;
 for (i=nlocalvar-1; i >= 0; i--)
  if (n == localvar[i]) return i;	/* local var */
 return -1;		        /* global var */
}

/*
** Push a variable given a number. If number is positive, push global variable
** indexed by (number -1). If negative, push local indexed by ABS(number)-1.
** Otherwise, if zero, push indexed variable (record).
*/

//lua_pushvar() 함수의 주석에 루아 내부 구현에서 전역 변수와 지역 변수를 구분하는 방법을 설명합니다. 
//number 파라메터 값이 0보다 크면 전역 변수 작으면 지역 변수입니다.
//number가 0보다 큰 경우 GLOBAL 변수로 varbuffer 인덱스 찾아서 tablebuffer[인덱스] 값을 끄집어내고 현재 값을 stack 버퍼에 옮겨 놓기를 하는 push 함수.
//number가 0보다 작은 경우 LOCAL 연산으로 STACK에다 넣는 일을 수행.
static void lua_pushvar (long number)
{ 
 if (number > 0)	/* global var */
 {
  code_byte(PUSHGLOBAL);
  code_word(number-1);
  incr_ntemp();
 }
 else if (number < 0)	/* local var */
 {
  number = (-number) - 1;
  if (number < 10) code_byte(PUSHLOCAL0 + number);
  else
  {
   code_byte(PUSHLOCAL);
   code_byte(number);
  }
  incr_ntemp();
 }
 else
 {
  code_byte(PUSHINDEXED);
  ntemp--;
 }
}


//lua_codeadjust() 함수는 이전 글에서도 한 번 나왔습니다. ADJUST 명령어 의미를 모르기 때문에 나중에 다른 소스 코드를 읽어야 이해 할 수 있는 함수 입니다. lua_codestore() 함수는 절반이 lua_pushvar()와 같은 구현입니다. 
static void lua_codeadjust (int n)
{
 code_byte(ADJUST);
 code_byte(n + nlocalvar);
}



//지역변수와 전역변수를 구분.
//number 파라메터 값이 0보다 크면 전역 변수 작으면 지역 변수입니다.
//number가 0보다 큰 경우 GLOBAL 변수로 varbuffer 인덱스 찾아서 STACK에 있는 버퍼의 값을 tablebuffer[인덱스] 값으로 변경하는 opcode를 실행.
//현재 STACK 버퍼 연산값을 변수 버퍼에 저장.
static void lua_codestore (int i)
{
 if (varbuffer[i] > 0)		/* global var */
 {
  code_byte(STOREGLOBAL);
  code_word(varbuffer[i]-1);
 }
 else if (varbuffer[i] < 0)      /* local var */
 {
  int number = (-varbuffer[i]) - 1;
  if (number < 10) code_byte(STORELOCAL0 + number);
  else
  {
   code_byte(STORELOCAL);
   code_byte(number);
  }
 }
 else				  /* indexed var */
 {
  int j;
  int upper=0;     	/* number of indexed variables upper */
  int param;		/* number of itens until indexed expression */
  for (j=i+1; j <nvarbuffer; j++)
   if (varbuffer[j] == 0) upper++;
  param = upper*2 + i;
  if (param == 0)
   code_byte(STOREINDEXED0);
  else
  {
   code_byte(STOREINDEXED);
   code_byte(param);
  }
 }
}

//yyerror() 함수와 yywrap() 함수는 yacc가 호출하는 함수입니다. lua_parse() 파일은 루아 API에서 호출하는 코드 파싱을 시작하는 함수입니다.
void yyerror (char *s)
{
 static char msg[256];
 sprintf (msg,"%s near \"%s\" at line %d in file \"%s\"",
          s, lua_lasttext (), lua_linenumber, lua_filename());
 lua_error (msg);
 err = 1;
}

int yywrap (void)
{
 return 1;
}


/*
** Parse LUA code and execute global statement.
** Return 0 on success or 1 on error.
*/
int lua_parse (void)
{
 Byte *init = initcode = (Byte *) calloc(GAPCODE, sizeof(Byte)); //바이트 코드 초기화
 maincode = 0;  //초기화
 maxmain = GAPCODE; //GAPCODE 만큼 메모리 확보
 if (init == NULL)
 {
  lua_error("not enough memory");
  return 1;
 }
 err = 0;
 if (yyparse () || (err==1)) return 1; //yyparse()에서 지속적으로 바이트 코드 생성
 initcode[maincode++] = HALT; //끝나는 지점에 HALT 코드를 넣고 끝내는 바이트 코드의 끝을 지정.
 init = initcode;
#if LISTING
 PrintCode(init,init+maincode);
#endif
 if (lua_execute (init)) return 1;
 free(init);
 return 0;
}


#if LISTING

static void PrintCode (Byte *code, Byte *end)
{
 Byte *p = code;
 printf ("\n\nCODE\n");
 while (p != end)
 {
  switch ((OpCode)*p)
  {
   case PUSHNIL:	printf ("%d    PUSHNIL\n", (p++)-code); break;
   case PUSH0: case PUSH1: case PUSH2:
    			printf ("%d    PUSH%c\n", p-code, *p-PUSH0+'0');
    			p++;
   			break;
   case PUSHBYTE:
    			printf ("%d    PUSHBYTE   %d\n", p-code, *(++p));
    			p++;
   			break;
   case PUSHWORD:
                        {
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    PUSHWORD   %d\n", n, c.w);
			}
   			break;
   case PUSHFLOAT:
			{
			 CodeFloat c;
			 int n = p-code;
			 p++;
			 get_float(c,p);
    			 printf ("%d    PUSHFLOAT  %f\n", n, c.f);
			}
   			break;
   case PUSHSTRING:
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    PUSHSTRING   %d\n", n, c.w);
			}
   			break;
   case PUSHLOCAL0: case PUSHLOCAL1: case PUSHLOCAL2: case PUSHLOCAL3:
   case PUSHLOCAL4: case PUSHLOCAL5: case PUSHLOCAL6: case PUSHLOCAL7:
   case PUSHLOCAL8: case PUSHLOCAL9:
    			printf ("%d    PUSHLOCAL%c\n", p-code, *p-PUSHLOCAL0+'0');
    			p++;
   			break;
   case PUSHLOCAL:	printf ("%d    PUSHLOCAL   %d\n", p-code, *(++p));
    			p++;
   			break;
   case PUSHGLOBAL:
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    PUSHGLOBAL   %d\n", n, c.w);
			}
   			break;
   case PUSHINDEXED:    printf ("%d    PUSHINDEXED\n", (p++)-code); break;
   case PUSHMARK:       printf ("%d    PUSHMARK\n", (p++)-code); break;
   case PUSHOBJECT:     printf ("%d    PUSHOBJECT\n", (p++)-code); break;
   case STORELOCAL0: case STORELOCAL1: case STORELOCAL2: case STORELOCAL3:
   case STORELOCAL4: case STORELOCAL5: case STORELOCAL6: case STORELOCAL7:
   case STORELOCAL8: case STORELOCAL9:
    			printf ("%d    STORELOCAL%c\n", p-code, *p-STORELOCAL0+'0');
    			p++;
   			break;
   case STORELOCAL:
    			printf ("%d    STORELOCAL   %d\n", p-code, *(++p));
    			p++;
   			break;
   case STOREGLOBAL:
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    STOREGLOBAL   %d\n", n, c.w);
			}
   			break;
   case STOREINDEXED0:  printf ("%d    STOREINDEXED0\n", (p++)-code); break;
   case STOREINDEXED:   printf ("%d    STOREINDEXED   %d\n", p-code, *(++p));
    			p++;
   			break;
   case STORELIST0:
	printf("%d      STORELIST0  %d\n", p-code, *(++p));
        p++;
        break;
   case STORELIST:
	printf("%d      STORELIST  %d %d\n", p-code, *(p+1), *(p+2));
        p+=3;
        break;
   case STORERECORD:
       printf("%d      STORERECORD  %d\n", p-code, *(++p));
       p += *p*sizeof(Word) + 1;
       break;
   case ADJUST:
    			printf ("%d    ADJUST   %d\n", p-code, *(++p));
    			p++;
   			break;
   case CREATEARRAY:	printf ("%d    CREATEARRAY\n", (p++)-code); break;
   case EQOP:       	printf ("%d    EQOP\n", (p++)-code); break;
   case LTOP:       	printf ("%d    LTOP\n", (p++)-code); break;
   case LEOP:       	printf ("%d    LEOP\n", (p++)-code); break;
   case ADDOP:       	printf ("%d    ADDOP\n", (p++)-code); break;
   case SUBOP:       	printf ("%d    SUBOP\n", (p++)-code); break;
   case MULTOP:      	printf ("%d    MULTOP\n", (p++)-code); break;
   case DIVOP:       	printf ("%d    DIVOP\n", (p++)-code); break;
   case CONCOP:       	printf ("%d    CONCOP\n", (p++)-code); break;
   case MINUSOP:       	printf ("%d    MINUSOP\n", (p++)-code); break;
   case NOTOP:       	printf ("%d    NOTOP\n", (p++)-code); break;
   case ONTJMP:	   
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    ONTJMP  %d\n", n, c.w);
			}
   			break;
   case ONFJMP:	   
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    ONFJMP  %d\n", n, c.w);
			}
   			break;
   case JMP:	   
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    JMP  %d\n", n, c.w);
			}
   			break;
   case UPJMP:
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    UPJMP  %d\n", n, c.w);
			}
   			break;
   case IFFJMP:
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    IFFJMP  %d\n", n, c.w);
			}
   			break;
   case IFFUPJMP:
    			{
			 CodeWord c;
			 int n = p-code;
			 p++;
			 get_word(c,p);
    			 printf ("%d    IFFUPJMP  %d\n", n, c.w);
			}
   			break;
   case POP:       	printf ("%d    POP\n", (p++)-code); break;
   case CALLFUNC:	printf ("%d    CALLFUNC\n", (p++)-code); break;
   case RETCODE:
    			printf ("%d    RETCODE   %d\n", p-code, *(++p));
    			p++;
   			break;
   case HALT:		printf ("%d    HALT\n", (p++)-code); break;
   case SETFUNCTION:
                        {
                         CodeWord c1, c2;
                         int n = p-code;
                         p++;
                         get_word(c1,p);
                         get_word(c2,p);
                         printf ("%d    SETFUNCTION  %d  %d\n", n, c1.w, c2.w);
                        }
                        break;
   case SETLINE:
                        {
                         CodeWord c;
                         int n = p-code;
                         p++;
                         get_word(c,p);
                         printf ("%d    SETLINE  %d\n", n, c.w);
                        }
                        break;

   case RESET:		printf ("%d    RESET\n", (p++)-code); break;
   default:		printf ("%d    Cannot happen: code %d\n", (p++)-code, *(p-1)); break;
  }
 }
}
#endif






/******참조용 코드
static void lua_codeadjust (int n)
{
 code_byte(ADJUST);
 code_byte(n + nlocalvar);
}
***************/

 

반응형
Comments