반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 수도권주택공급
- file write
- FILE TRANSFER
- Lua
- 중소규모택지
- 엑스퍼트2주년
- 엑스퍼트생일축하해
- 프리미어 영상저장
- lua interpreter
- TCP/IP
- 월세
- 청량리역한양수자인192
- QT TCP
- 등록임대주택
- lua install
- C API
- 찾다죽는줄
- lua setup
- QTcpServer
- #신혼부부 #결혼준비 #신혼부부희망타운신혼부부특별공급
- C++ API
- 프리미어 영상변환
- lua for windows
- 국토교통부
- #부동산전자거래 #부동산전자계약 #부동산계약 #부동산전자계약방법 #부동산전자계약하는법 #부동산계약방법 #부동산중개수수료 #부동산중개수수료아끼기 #부동산복비아끼기
- object
- 티몬삼겹살데이
- file open
- meta table
- file read
Archives
- Today
- Total
Value Creator의 IT(프로그래밍 / 전자제품)
#11 [lua 1.1] opcode.h, opcode.c 파일 읽기 본문
반응형
opcode는 루아 VM(Virtual Machine) 을 구현한 코드.
(스크롤 압박 주의
opcode.h
/*
** TeCGraf - PUC-Rio
** $Id: opcode.h,v 2.1 1994/04/20 22:07:57 celes Exp $
*/
#ifndef opcode_h
#define opcode_h
#ifndef STACKGAP
#define STACKGAP 128
#endif
#ifndef real
#define real float
#endif
#define FIELDS_PER_FLUSH 40
typedef unsigned char Byte;
typedef unsigned short Word;
typedef union
{
struct {char c1; char c2;} m;
Word w;
} CodeWord;
typedef union
{
struct {char c1; char c2; char c3; char c4;} m;
float f;
} CodeFloat;
typedef enum
{
PUSHNIL,
PUSH0, PUSH1, PUSH2,
PUSHBYTE,
PUSHWORD,
PUSHFLOAT,
PUSHSTRING,
PUSHLOCAL0, PUSHLOCAL1, PUSHLOCAL2, PUSHLOCAL3, PUSHLOCAL4,
PUSHLOCAL5, PUSHLOCAL6, PUSHLOCAL7, PUSHLOCAL8, PUSHLOCAL9,
PUSHLOCAL,
PUSHGLOBAL,
PUSHINDEXED,
PUSHMARK,
PUSHOBJECT,
STORELOCAL0, STORELOCAL1, STORELOCAL2, STORELOCAL3, STORELOCAL4,
STORELOCAL5, STORELOCAL6, STORELOCAL7, STORELOCAL8, STORELOCAL9,
STORELOCAL,
STOREGLOBAL,
STOREINDEXED0,
STOREINDEXED,
STORELIST0,
STORELIST,
STORERECORD,
ADJUST,
CREATEARRAY,
EQOP,
LTOP,
LEOP,
ADDOP,
SUBOP,
MULTOP,
DIVOP,
CONCOP,
MINUSOP,
NOTOP,
ONTJMP,
ONFJMP,
JMP,
UPJMP,
IFFJMP,
IFFUPJMP,
POP,
CALLFUNC,
RETCODE,
HALT,
SETFUNCTION,
SETLINE,
RESET
} OpCode;
typedef enum
{
T_MARK,
T_NIL,
T_NUMBER,
T_STRING,
T_ARRAY,
T_FUNCTION,
T_CFUNCTION,
T_USERDATA
} Type;
typedef void (*Cfunction) (void);
typedef int (*Input) (void);
typedef union
{
Cfunction f; //루아의 Cfunction
real n; //number
char *s; //string
Byte *b; //바이트 저장
struct Hash *a; //array
void *u; //Userdata
} Value;
typedef struct Object
{
Type tag;
Value value;
} Object;
typedef struct
{
char *name;
Object object;
} Symbol;
/* Macros to access structure members */
#define tag(o) ((o)->tag)
#define nvalue(o) ((o)->value.n)
#define svalue(o) ((o)->value.s)
#define bvalue(o) ((o)->value.b)
#define avalue(o) ((o)->value.a)
#define fvalue(o) ((o)->value.f)
#define uvalue(o) ((o)->value.u)
/* Macros to access symbol table */
#define s_name(i) (lua_table[i].name)
#define s_object(i) (lua_table[i].object)
#define s_tag(i) (tag(&s_object(i)))
#define s_nvalue(i) (nvalue(&s_object(i)))
#define s_svalue(i) (svalue(&s_object(i)))
#define s_bvalue(i) (bvalue(&s_object(i)))
#define s_avalue(i) (avalue(&s_object(i)))
#define s_fvalue(i) (fvalue(&s_object(i)))
#define s_uvalue(i) (uvalue(&s_object(i)))
//현재 바이트 코드값을 word로 가져가거나
//float로 가져가는 define 구문
#define get_word(code,pc) {code.m.c1 = *pc++; code.m.c2 = *pc++;}
#define get_float(code,pc) {code.m.c1 = *pc++; code.m.c2 = *pc++;\
code.m.c3 = *pc++; code.m.c4 = *pc++;}
/* Exported functions */
int lua_execute (Byte *pc);
void lua_markstack (void);
char *lua_strdup (char *l);
void lua_setinput (Input fn); /* from "lex.c" module */
char *lua_lasttext (void); /* from "lex.c" module */
int lua_parse (void); /* from "lua.stx" module */
void lua_type (void);
void lua_obj2number (void);
void lua_print (void);
void lua_internaldofile (void);
void lua_internaldostring (void);
void lua_travstack (void (*fn)(Object *));
#endif
opcode.c
/*
** opcode.c
** TecCGraf - PUC-Rio
*/
char *rcs_opcode="$Id: opcode.c,v 2.1 1994/04/20 22:07:57 celes Exp $";
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* stdlib.h does not have this in SunOS */
extern double strtod(const char *, char **);
#include "mm.h"
#include "opcode.h"
#include "hash.h"
#include "inout.h"
#include "table.h"
#include "lua.h"
//tonumber() 매크로는 루아 number 타입으로 컨버팅하는 것이고
//tostring() 매크로는 루아 string 타입으로 컨버팅하는 것으로 보입니다.
//lua_tonumber() 함수와 lua_tostring() 함수는 Opcode.c 파일에 있음.
#define tonumber(o) ((tag(o) != T_NUMBER) && (lua_tonumber(o) != 0))
#define tostring(o) ((tag(o) != T_STRING) && (lua_tostring(o) != 0))
//루아 VM은 스택 기반입니다. 그래서 일단 스택 공간을 확보하고 시작합니다.
//스택 크기는 Object 타입으로 256개입니다.
#ifndef MAXSTACK
#define MAXSTACK 256
#endif
static Object stack[MAXSTACK] = {{T_MARK, {NULL}}}; //스택 맨 아래에는 {T_MARK, {NULL}}로 값을 넣고 시작합니다. 바닥을 표시하는 용도인가 봅니다.
static Object *top=stack+1, *base=stack+1; //그리고 스택 top 포인터와 base 포인터를 같은 값으로 지정합니다. 아직 스택에 아무 것도 없으니까요. 아마 스택에 값을 넣으면 top은 증가하겠지요.
/*
** Concatenate two given string, creating a mark space at the beginning.
** Return the new string pointer.
*/
//문자열 두 개를 합치는 함수
static char *lua_strconc (char *l, char *r)
{
char *s = calloc (strlen(l)+strlen(r)+2, sizeof(char));
if (s == NULL)
{
lua_error ("not enough memory");
return NULL;
}
*s++ = 0; /* create mark space */
return strcat(strcpy(s,l),r);
}
/*****아래 참조
case CONCOP:
{
Object *l = top-2;
Object *r = top-1;
if (tostring(r) || tostring(l))
return 1;
svalue(l) = lua_createstring (lua_strconc(svalue(l),svalue(r)));
if (svalue(l) == NULL)
return 1;
--top;
}
break;
CONCOP 명령어를 처리할 때 호출하는 함수입니다. CONCOP 명령어는 스택에서 두 개를 빼서 합치고 다시 스택에 넣습니다. 그래서 전체적으로 스택은 하나가 줄어드네요.
expr :
... 중략 ...
| expr1 CONC expr1 { code_byte(CONCOP); $$ = 1; ntemp--;}
... 후략 ...
앞서 읽었던 Lua.stx에 expr 문법 중 CONC 토큰(점 두 개 연산자)을 파싱할 때 CONCOP 명령을 명령어 스택에 넣습니다.
*************/
/*
** Duplicate a string, creating a mark space at the beginning.
** Return the new string pointer.
*/
//문자열 복사하는 함수입니다.
//앞서와 마찬가지로 첫 바이트를 0으로 비워 놓습니다.
//lua_strdup() 함수는 여러 군데서 호출됩니다.
//공통적으로 lua_createstring(lua_strdup(s)) 형태로 호출됩니다.
//lua_createstring() 함수는 Table.c를 읽을 때 읽었던 함수입니다.
//문자열 테이블에서 검사해서 있으면 리턴하고 없으면 문자열 테이블에 새 문자열을 추가하는 함수지요.
char *lua_strdup (char *l)
{
char *s = calloc (strlen(l)+2, sizeof(char));
if (s == NULL)
{
lua_error ("not enough memory");
return NULL;
}
*s++ = 0; /* create mark space */
return strcpy(s,l);
}
/*
** Convert, if possible, to a number tag.
** Return 0 in success or not 0 on error.
*/
//lua_tonumber() 함수는 Opcode.c 파일 시작에 나오는 tonumber() 매크로에서 호출하는 함수입니다.
//이름은 tonumber면서 실제로는 string 타입만 number로 바꿀 수 있네요.
//from string to number
static int lua_tonumber (Object *obj)
{
char *ptr;
if (tag(obj) != T_STRING)
{
lua_reportbug ("unexpected type at conversion to number");
return 1;
}
nvalue(obj) = strtod(svalue(obj), &ptr);
if (*ptr)
{
lua_reportbug ("string to number convertion failed");
return 2;
}
tag(obj) = T_NUMBER;
return 0;
}
/*
** Test if is possible to convert an object to a number one.
** If possible, return the converted object, otherwise return nil object.
*/
//실질적으로 동작 자체는 lua_tonumber()와 동일합니다.
//이 함수는 opcode 처리등에 관여하는 함수가 아니라, Table.c에 있는 tablebuffer 배열에 연결되어 있습니다.
static Object *lua_convtonumber (Object *obj)
{
static Object cvt;
if (tag(obj) == T_NUMBER)
{
cvt = *obj;
return &cvt;
}
tag(&cvt) = T_NIL;
if (tag(obj) == T_STRING)
{
char *ptr;
nvalue(&cvt) = strtod(svalue(obj), &ptr);
if (*ptr == 0)
tag(&cvt) = T_NUMBER;
}
return &cvt;
}
/*
** Convert, if possible, to a string tag
** Return 0 in success or not 0 on error.
*/
//lua_tostring() 함수도 lua_tonumber() 함수처럼 tostring() 매크로에서 호출합니다.
//이 함수 역시 루아 number 타입만 루아 string 타입으로 바꿉니다.
//문자열로 바꿀 때 조금 전 읽었던 lua_createstring() 함수를 호출해서 문자열 테이블을 검사하네요.
static int lua_tostring (Object *obj)
{
static char s[256];
if (tag(obj) != T_NUMBER)
{
lua_reportbug ("unexpected type at conversion to string");
return 1;
}
if ((int) nvalue(obj) == nvalue(obj))
sprintf (s, "%d", (int) nvalue(obj));
else
sprintf (s, "%g", nvalue(obj));
svalue(obj) = lua_createstring(lua_strdup(s));
if (svalue(obj) == NULL)
return 1;
tag(obj) = T_STRING;
return 0;
}
/*
** Execute the given opcode. Return 0 in success or 1 on error.
*/
//루아 VM 명령어 구현이 커다란 switch-case 구문으로 들어가 있는 함수.
//lua_execute() 함수가 루아 VM 구현 자체인것으로 보입니다.
int lua_execute (Byte *pc)
{
Object *oldbase = base;
base = top;
while (1)
{
OpCode opcode;
switch (opcode = (OpCode)*pc++)
//시작 코드입니다. pc 배열은 Lua.stx에서 yacc 문법을 파싱하면서 만든 바이트 스택입니다.
//명령어와 데이터가 코드 문법 파싱 순서와 규칙에 따라 쭉 들어가 있지요.
//이것을 하나씩 읽으면서 동작을 처리하고 그 결과를 Object 스택에 넣는 방식으로 루아 VM이 동작하는가 봅니다.
{
//PUSH 시리즈 명령어 처리 부분입니다.
//PUSHNIL, PUSH0, PUSH1, PUSH2는 명령어 자체에 값을 스택에 넣으라고 지시하고 있습니다. 그래서 pc를 변경하지 않고 바로 스택에 nil, 0, 1, 2를 넣습니다.
case PUSHNIL: tag(top++) = T_NIL; break;
case PUSH0: tag(top) = T_NUMBER; nvalue(top++) = 0; break;
case PUSH1: tag(top) = T_NUMBER; nvalue(top++) = 1; break;
case PUSH2: tag(top) = T_NUMBER; nvalue(top++) = 2; break;
//PUSHBYTE는 명령어를 처리하는데 2 바이트가 필요합니다.
//명령어 자체와 스택에 넣을 값, 이렇게 2 바이트가 필요하죠.
//명령어 자체는 루프 돌면서 switch 문에서 처리했고 case 문에서는 데이터를 바이트 스택에서 빼야 합니다.
//그래서 nvalue(top++) = *pc++로 pc를 하나 증가하는 코드를 코딩한 것입니다.
case PUSHBYTE: tag(top) = T_NUMBER; nvalue(top++) = *pc++; break;
//PUSHWORD 명령어는 opcode 자체를 제외하고 2 바이트가 더 필요합니다. 그러므로 pc를 두 번 증가시켜야 하지요.
case PUSHWORD:
{
CodeWord code;
get_word(code,pc); //get_word() 매크로 구현을 보면 pc가 두 번 증가합니다.
tag(top) = T_NUMBER; nvalue(top++) = code.w;
}
break;
//PUSHFLOAT는 4바이트이므로 opcode 자체를 제외하고 4바이트가 더 필요합니다. 그러므로 pc를 네 번 증가시켜야 합니다.
case PUSHFLOAT:
{
CodeFloat code;
get_float(code,pc); //case 구문에서 호출하는 get_float() 매크로를 보면 pc가 네 번 증가하는 코드가 있습니다.
tag(top) = T_NUMBER; nvalue(top++) = code.f;
}
break;
/***아래 참조
#define get_float(code,pc) {code.m.c1 = *pc++; code.m.c2 = *pc++;\
code.m.c3 = *pc++; code.m.c4 = *pc++;}
***********/
//루아 문법을 파싱할 때 문자열은 문자열 테이블에 저장하고 바이트 스택엔 문자열 테이블 인덱스를 저장합니다.
//Lex.c 파일에서 STRING 토큰을 처리하는 코드를 다시 보면 알 수 있습니다.
//그러므로 바이트 스택에서 2바이트 word 크기 데이터를 읽어서 그 값을 인덱스로 lua_constant 배열에서 문자열 포인터를 받아 옵니다.
//이 포인터 값이 Object 스택에 루아 string 타입으로 저장됩니다.
case PUSHSTRING:
{
CodeWord code;
get_word(code,pc);
tag(top) = T_STRING; svalue(top++) = lua_constant[code.w];
}
break;
//PUSHLOCAL 계열 명령어를 처리하는 코드입니다.
//스택 베이스 포인터 위치를 기준으로 N 칸 (단위가 Object 자료형 크기라서 ‘칸’이라는 표현이 제일 적당해 보입니다.) 위에 있는 값을 읽어서 스택에 넣는 동작을 합니다.
//이 동작과 PUSHLOCAL이라는 이름이 머릿속에서 잘 어울리지 않네요.
//PUSHLOCAL은 스택에 있는 값을 읽어서 다시 스택에 넣는 명령.
//PUSHGLOBAL은 심볼 테이블에 있는 심볼값을 읽어서 스택에 넣는 명령.
case PUSHLOCAL0: case PUSHLOCAL1: case PUSHLOCAL2:
case PUSHLOCAL3: case PUSHLOCAL4: case PUSHLOCAL5:
case PUSHLOCAL6: case PUSHLOCAL7: case PUSHLOCAL8:
case PUSHLOCAL9: *top++ = *(base + (int)(opcode-PUSHLOCAL0)); break;
case PUSHLOCAL: *top++ = *(base + (*pc++)); break;
//그 다음에는 PUSHGLOBAL입니다. 이름이 PUSHLOCAL과 반대네요.
case PUSHGLOBAL:
{
CodeWord code;
get_word(code,pc);
*top++ = s_object(code.w);
}
break;
/*****아래 참조
get_word() 매크로는 앞에서 읽었던 매크로입니다. s_object() 매크로 구현을 보죠.
#define s_object(i) (lua_table[i].object)
get_word() 매크로로 바이트 코드 스택에서 2바이트 값을 읽습니다.
이 값은 lua_table 배열의 인덱스로 들어가서 lua_table의 object 맴버 값을 읽습니다.
Object 객체의 포인터 값이겠죠.
종합해보면 PUSHLOCAL은 스택에 있는 값을 읽어서 다시 스택에 넣는 것이고 PUSHGLOBAL은 심볼 테이블에 있는 심볼값을 읽어서 스택에 넣는 명령입니다.
************/
//PUSHINDEXED 명령어는 동작 추적이 쉽지 않습니다. 사용되는 곳은 lua_pushvar() 함수 한 곳입니다.
case PUSHINDEXED:
--top;
if (tag(top-1) != T_ARRAY)
{
lua_reportbug ("indexed expression not a table");
return 1;
}
{
Object *h = lua_hashdefine (avalue(top-1), top);
if (h == NULL) return 1;
*(top-1) = *h;
}
break;
/*****아래 참조
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--;
}
}
number가 0일 때만 PUSHINDEXED 명령어를 바이트 코드 스택에 넣거든요.
그런데 문법 코드만 봐서는 number가 0이 되는 경우가 어떤 상황인지 알 수가 없습니다.
************/
case PUSHMARK: tag(top++) = T_MARK; break;
case PUSHOBJECT: *top = *(top-3); top++; break;
//위에 있는 값들을 STACK에서 뺀 다음
//STORELOCAL은 STACK에 넣기.
//STOREGLOBAL은 STACK의 값을 심볼테이블에 있는 심볼값에 넣기.
//STORELOCAL 시리즈 명령어와 STOREGLOBAL 명령어는 PUSHLOCAL/PUSHGLOBAL 명령어와 동작이 반대입니다.
case STORELOCAL0: case STORELOCAL1: case STORELOCAL2:
case STORELOCAL3: case STORELOCAL4: case STORELOCAL5:
case STORELOCAL6: case STORELOCAL7: case STORELOCAL8:
case STORELOCAL9: *(base + (int)(opcode-STORELOCAL0)) = *(--top); break;
case STORELOCAL: *(base + (*pc++)) = *(--top); break;
case STOREGLOBAL:
{
CodeWord code;
get_word(code,pc);
s_object(code.w) = *(--top);
}
break;
//INDEXED라고 붙은 명령어들은 루아 테이블 타입 변수를 다루는 명령어 인것 같습니다.
//루아 테이블 타입 변수의 인덱스를 받아서 값을 읽거나 쓰는 동작
case STOREINDEXED0:
if (tag(top-3) != T_ARRAY)
{
lua_reportbug ("indexed expression not a table");
return 1;
}
{
Object *h = lua_hashdefine (avalue(top-3), top-2);
if (h == NULL) return 1;
*h = *(top-1);
}
top -= 3;
break;
case STOREINDEXED:
{
int n = *pc++;
if (tag(top-3-n) != T_ARRAY)
{
lua_reportbug ("indexed expression not a table");
return 1;
}
{
Object *h = lua_hashdefine (avalue(top-3-n), top-2-n);
if (h == NULL) return 1;
*h = *(top-1);
}
top--;
}
break;
case STORELIST0:
case STORELIST:
{
int m, n;
Object *arr;
if (opcode == STORELIST0) m = 0;
else m = *(pc++) * FIELDS_PER_FLUSH;
n = *(pc++);
arr = top-n-1;
if (tag(arr) != T_ARRAY)
{
lua_reportbug ("internal error - table expected");
return 1;
}
while (n)
{
tag(top) = T_NUMBER; nvalue(top) = n+m;
*(lua_hashdefine (avalue(arr), top)) = *(top-1);
top--;
n--;
}
}
break;
case STORERECORD:
{
int n = *(pc++);
Object *arr = top-n-1;
if (tag(arr) != T_ARRAY)
{
lua_reportbug ("internal error - table expected");
return 1;
}
while (n)
{
CodeWord code;
get_word(code,pc);
tag(top) = T_STRING; svalue(top) = lua_constant[code.w];
*(lua_hashdefine (avalue(arr), top)) = *(top-1);
top--;
n--;
}
}
break;
//STACK의 top을 조정.
//newtop을 base에서 N만큼 더한 위치.
//base는 STACK 바닥이므로 top을 newtop으로 바꾼다는 말은 STACK 꼭대기를 아래로 내린다는 뜻.
//코드만 보면 스택 top을 조정하는 명령어군요. 그런데 코드 내용을 보면 newtop을 base에서 N만큼 더한 위치로 잡습니다.
//base는 스택 바닥이므로 top을 newtop으로 바꾼다는 말은 스택 꼭대기를 아래로 내린다는 뜻
case ADJUST:
{
Object *newtop = base + *(pc++);
while (top < newtop) tag(top++) = T_NIL;
top = newtop; /* top could be bigger than newtop */
}
break;
//명령어 이름이 CREATEARRAY이니, 배열을 만드는 명령어라는걸 미루어 짐작할 수 있습니다.
//스택에서는 배열 크기를 받아 오는 듯 합니다.
//배열 크기를 받아야 하는데 그 값이 nil이면 배열을 만들 수 없는데 101을 넣습니다.
//101을 넣은 채로 lua_createarray() 함수를 호출하는 것 보니 101은 에러 코드 같은 것이 아니라 기본값으로 지정하는 배열 크기인듯 합니다.
case CREATEARRAY:
if (tag(top-1) == T_NIL)
nvalue(top-1) = 101;
else
{
if (tonumber(top-1)) return 1;
if (nvalue(top-1) <= 0) nvalue(top-1) = 101;
}
avalue(top-1) = lua_createarray(nvalue(top-1));
if (avalue(top-1) == NULL)
return 1;
tag(top-1) = T_ARRAY;
break;
//EQOP 명령어는 “같다” 연산자를 처리하는 명령어입니다.
//일단 비교하는 두 값의 타입이 다르면 비교를 안하네요.
//타입이 같으면 문자열을 제외하고 나머지는 C 언어 기준으로 같음을 비교합니다.
//즉, 루아 입장에서는 레퍼런스의 값이 같아야 같은 것으로 처리됩니다.
//예를 들어 루아 배열 같은 경우, 배열에 소속된 값이 모두 같더라도 두 배열이 서로 다른 레퍼런스라면 (두 배열이 서로 다른 메모리 위치에 할당되 있다면) 다른 배열로 취급한다는 겁니다.
//문자열은 문자열 내용을 비교합니다.
//같으면 1과 TYPE을 설정한다.
//다른 경우 Type = T_NIL로 설정한다.
case EQOP:
{
Object *l = top-2;
Object *r = top-1;
--top;
if (tag(l) != tag(r))
tag(top-1) = T_NIL;
else
{
switch (tag(l))
{
case T_NIL: tag(top-1) = T_NUMBER; break;
case T_NUMBER: tag(top-1) = (nvalue(l) == nvalue(r)) ? T_NUMBER : T_NIL; break;
case T_ARRAY: tag(top-1) = (avalue(l) == avalue(r)) ? T_NUMBER : T_NIL; break;
case T_FUNCTION: tag(top-1) = (bvalue(l) == bvalue(r)) ? T_NUMBER : T_NIL; break;
case T_CFUNCTION: tag(top-1) = (fvalue(l) == fvalue(r)) ? T_NUMBER : T_NIL; break;
case T_USERDATA: tag(top-1) = (uvalue(l) == uvalue(r)) ? T_NUMBER : T_NIL; break;
case T_STRING: tag(top-1) = (strcmp (svalue(l), svalue(r)) == 0) ? T_NUMBER : T_NIL; break;
case T_MARK: return 1;
}
}
nvalue(top-1) = 1;
}
break;
//LTOP와 LEOP 명령어 처리 코드는 거의 같습니다.
//“작다”와 “작거나 같다”를 처리하는 명령어 구현이라 그렇습니다. 연산자 말고는 다를게 없지요.
//일단 number 타입은 바로 비교해서 결과를 스택에 넣습니다. number 타입이 아닐 때는 문자열로 변경하는데, 문자열로 변경을 실패하면 비교를 안하네요.
case LTOP:
{
Object *l = top-2;
Object *r = top-1;
--top;
if (tag(l) == T_NUMBER && tag(r) == T_NUMBER)
tag(top-1) = (nvalue(l) < nvalue(r)) ? T_NUMBER : T_NIL;
else
{
if (tostring(l) || tostring(r))
return 1;
tag(top-1) = (strcmp (svalue(l), svalue(r)) < 0) ? T_NUMBER : T_NIL;
}
nvalue(top-1) = 1;
}
break;
case LEOP:
{
Object *l = top-2;
Object *r = top-1;
--top;
if (tag(l) == T_NUMBER && tag(r) == T_NUMBER)
tag(top-1) = (nvalue(l) <= nvalue(r)) ? T_NUMBER : T_NIL;
else
{
if (tostring(l) || tostring(r))
return 1;
tag(top-1) = (strcmp (svalue(l), svalue(r)) <= 0) ? T_NUMBER : T_NIL;
}
nvalue(top-1) = 1;
}
break;
//ADD, SUB, MULTIPLE, DIVIDE 사칙연산 구현.
//STACK의 두 값을 더해서 STACK(top-1)에 넣는다.
case ADDOP:
{
Object *l = top-2;
Object *r = top-1;
if (tonumber(r) || tonumber(l))
return 1;
nvalue(l) += nvalue(r);
--top;
}
break;
//STACK의 두 값을 빼서 STACK(top-1)에 넣는다.
case SUBOP:
{
Object *l = top-2;
Object *r = top-1;
if (tonumber(r) || tonumber(l))
return 1;
nvalue(l) -= nvalue(r);
--top;
}
break;
//STACK의 두 값을 곱해서 STACK(top-1)에 넣는다.
case MULTOP:
{
Object *l = top-2;
Object *r = top-1;
if (tonumber(r) || tonumber(l))
return 1;
nvalue(l) *= nvalue(r);
--top;
}
break;
//STACK의 두 값을 나눠서 STACK(top-1)에 넣는다.
//0으로 나뉘는 것은 고려 안하나?
case DIVOP:
{
Object *l = top-2;
Object *r = top-1;
if (tonumber(r) || tonumber(l))
return 1;
nvalue(l) /= nvalue(r);
--top;
}
break;
//문자열 두 개 붙이는 구현.
case CONCOP:
{
Object *l = top-2;
Object *r = top-1;
if (tostring(r) || tostring(l))
return 1;
svalue(l) = lua_createstring (lua_strconc(svalue(l),svalue(r)));
if (svalue(l) == NULL)
return 1;
--top;
}
break;
//숫자나 변수 앞에 마이너스(-)를 붙이면 역수로 바꿉니다.
//역시 기본적인 수학 연산자 구현입니다.
case MINUSOP:
if (tonumber(top-1))
return 1;
nvalue(top-1) = - nvalue(top-1);
break;
//NOTOP는 코드 구현만 보면 스택 꼭대기에 있는 값이 nil이면 number로 바꾸고 아니면 nil로 바꾸는 동작을 합니다.
//동작을 보니 C언어의 not 연산자(~ 연산자)는 아닌 것 같네요.
//그렇다면 흔히 nonop라고 많이 부르는 아무일 안하는 연산자로 볼 수도 있는데 그러기엔 타입 변경을 하네요.
case NOTOP:
tag(top-1) = tag(top-1) == T_NIL ? T_NUMBER : T_NIL;
break;
// ONTJMP는 스택 꼭대기 값이 nil이 아닐 때 점프합니다.
case ONTJMP:
{
CodeWord code;
get_word(code,pc);
if (tag(top-1) != T_NIL) pc += code.w;
}
break;
// ONFJMP는 스택 꼭대기 값이 nil일 때 점프합니다.
case ONFJMP:
{
CodeWord code;
get_word(code,pc);
if (tag(top-1) == T_NIL) pc += code.w;
}
break;
//JMP는 무조건 점프합니다.
case JMP:
{
CodeWord code;
get_word(code,pc);
pc += code.w;
}
break;
// UPJMP는 바이트 코드 스택을 거슬러서 (역방향) 점프합니다.
case UPJMP:
{
CodeWord code;
get_word(code,pc);
pc -= code.w;
}
break;
//IFFJMP는 기본적으로 ONFJMP와 같은 동작을 하지만 스택 top 인덱스를 하나 내립니다.
case IFFJMP:
{
CodeWord code;
get_word(code,pc);
top--;
if (tag(top) == T_NIL) pc += code.w;
}
break;
// IFFUPJMP는 스택 top 인덱스를 하나 내리면서 역방향으로 점프합니다.
case IFFUPJMP:
{
CodeWord code;
get_word(code,pc);
top--;
if (tag(top) == T_NIL) pc -= code.w;
}
break;
case POP: --top; break; //그냥 스택 top을 하나 내립니다.
/************아래 참조
명령어 이름이 동작을 설명한다.
CALLFUNC는 함수를 호출하는 명령어입니다. 크게 두 부분으로 나눴습니다.
하나는 루아 함수 호출이고 다른 하나는 C 함수 호출입니다.
루아 함수를 호출하면 스택에서 Object 타입 인스턴스를 하나 꺼냅니다.
이 인스턴스는 함수 자체의 바이트 코드 스택 위치를 포인터로 가지고 있지요.
이 포인터에서 함수 바이트 코드를 가져와서 현재 프로그램 카운터를 옮깁니다.
C 함수 호출 할 때는 일단 함수 포인터로 바로 점프해서 C 함수를 내부적으로 호출하는 코드가 보입니다.
그리고 C 함수에서 보낸 리턴값을 스택에서 정리하는 코드가 나옵니다.
*******************/
case CALLFUNC:
{
Byte *newpc;
Object *b = top-1;
while (tag(b) != T_MARK) b--;
if (tag(b-1) == T_FUNCTION)
{
lua_debugline = 0; /* always reset debug flag */
newpc = bvalue(b-1);
bvalue(b-1) = pc; /* store return code */
nvalue(b) = (base-stack); /* store base value */
base = b+1;
pc = newpc;
if (MAXSTACK-(base-stack) < STACKGAP)
{
lua_error ("stack overflow");
return 1;
}
}
else if (tag(b-1) == T_CFUNCTION)
{
int nparam;
lua_debugline = 0; /* always reset debug flag */
nvalue(b) = (base-stack); /* store base value */
base = b+1;
nparam = top-base; /* number of parameters */
(fvalue(b-1))(); /* call C function */
/* shift returned values */
{
int i;
int nretval = top - base - nparam;
top = base - 2;
base = stack + (int) nvalue(base-1);
for (i=0; i<nretval; i++)
{
*top = *(top+nparam+2);
++top;
}
}
}
else
{
lua_reportbug ("call expression not a function");
return 1;
}
}
break;
//RETCODE는 함수 리턴값을 처리하는 명령어 구현입니다.
//루아는 값을 여러개 리턴할 수 있습니다.(다중 리턴 가능. 즉, 다중입력된 값에 다중출력이 가능하다.)
//f(a,b) -> return c,d => a에 c가 들어가고, b에 d가 들어간다.
//그래서 for 문을 돌면서 스택에 리턴값을 쌓는 코드가 보입니다.
//바이트 코드와 스택 사이에서 리턴값을 어떻게 핸들링하는지는 바로 파악이 안됩니다.
//HALT는 말 그대로 그냥 종료입니다.
case RETCODE:
{
int i;
int shift = *pc++;
int nretval = top - base - shift;
top = base - 2;
pc = bvalue(base-2);
base = stack + (int) nvalue(base-1);
for (i=0; i<nretval; i++) //여러개의 리턴 for문을 돌면서 스택에 리턴값을 쌓는다.
{
*top = *(top+shift+2);
++top;
}
}
break;
case HALT:
base = oldbase;
return 0; /* success */
//SETFUNCTION는 루아 디버깅 정보 출력용 명령어입니다.
//함수를 선언할 때 함수 정보를 루아 디버깅 정보 기록용 배열에 넣습니다.
case SETFUNCTION:
{
CodeWord file, func;
get_word(file,pc);
get_word(func,pc);
if (lua_pushfunction (file.w, func.w))
return 1;
}
break;
//SETLINE 명령어도 마찬가지로 디버깅 정보 출력용입니다.
case SETLINE:
{
CodeWord code;
get_word(code,pc);
lua_debugline = code.w;
}
break;
//RESET도 디버깅 정보 처리용입니다. 함수 동작이 끝날 때 디버깅 정보 배열에서 마지막 함수 정보를 제거합니다.
case RESET:
lua_popfunction ();
break;
default:
lua_error ("internal error - opcode didn't match");
return 1;
}
}
}
/*
** Traverse all objects on stack
*/
//Opcode.c 파일은 크게 두 부분으로 나뉩니다.
//전반부는 루아 VM 구현이고 후반부는 루아 외부 API 구현입니다.
//호스트 프로그램에서 Lua.h를 통해 호출하는 루아 API들을 구현한 것이죠.
//가비지 컬랙션을 할 때 lua_pack() 함수에서 부르는 함수입니다.
//스택에 있는 Object 인스턴스들에 대해 마킹하는데 동원됩니다.
//확장하면 여러 용도로 쓸 수 있겠으나 루아 1.1에서는 일단 마킹하는데만 씁니다.
void lua_travstack (void (*fn)(Object *))
{
Object *o;
for (o = top-1; o >= stack; o--)
fn (o);
}
/*
** Open file, generate opcode and execute global statement. Return 0 on
** success or 1 on error.
*/
//아마 호스트 프로그램에서 루아 코드를 돌리고 싶을때 호출하는 게이트웨이 함수가 아닐까 싶습니다.
//루아 파일을 읽어서 루아를 돌릴 땐 lua_dofile() 함수를 호출하고 호스트 프로그램에서 문자열로 루아 코드를 넘겨서 실행합니다.
int lua_dofile (char *filename)
{
if (lua_openfile (filename)) return 1;
if (lua_parse ()) { lua_closefile (); return 1; }
lua_closefile ();
return 0;
}
/*
** Generate opcode stored on string and execute global statement. Return 0 on
** success or 1 on error.
*/
int lua_dostring (char *string)
{
if (lua_openstring (string)) return 1;
if (lua_parse ()) return 1;
lua_closestring();
return 0;
}
/*
** Execute the given function. Return 0 on success or 1 on error.
*/
//루아 함수를 별도로 실행할 때 호출하는 함수입니다.
//파라메터로 넘어오는 함수 이름으로 심볼 테이블에서 함수 객체를 가져옵니다.
//파라메터 개수만큼 스택을 조정하고 스택에 함수 객체를 넣습니다.
//그리고 VM에서는 CALLFUNC 명령어를 실행합니다.
int lua_call (char *functionname, int nparam)
{
static Byte startcode[] = {CALLFUNC, HALT};
int i;
Object func = s_object(lua_findsymbol(functionname));
if (tag(&func) != T_FUNCTION) return 1;
for (i=1; i<=nparam; i++)
*(top-i+2) = *(top-i);
top += 2;
tag(top-nparam-1) = T_MARK;
*(top-nparam-2) = func;
return (lua_execute (startcode));
}
/*
** Get a parameter, returning the object handle or NULL on error.
** 'number' must be 1 to get the first parameter.
*/
//코드 패턴이 모두 같아서 한 번에 읽어보겠습니다.
//함수 이름도 getxxx()로 아마 호스트 프로그램에서 루아 오브젝트 객체에서 필요한 자료형 값을 받는 함수입니다.
Object *lua_getparam (int number)
{
if (number <= 0 || number > top-base) return NULL;
return (base+number-1);
}
/*
** Given an object handle, return its number value. On error, return 0.0.
*/
real lua_getnumber (Object *object)
{
if (object == NULL || tag(object) == T_NIL) return 0.0;
if (tonumber (object)) return 0.0;
else return (nvalue(object));
}
/*
** Given an object handle, return its string pointer. On error, return NULL.
*/
char *lua_getstring (Object *object)
{
if (object == NULL || tag(object) == T_NIL) return NULL;
if (tostring (object)) return NULL;
else return (svalue(object));
}
/*
** Given an object handle, return a copy of its string. On error, return NULL.
*/
char *lua_copystring (Object *object)
{
if (object == NULL || tag(object) == T_NIL) return NULL;
if (tostring (object)) return NULL;
else return (strdup(svalue(object)));
}
/*
** Given an object handle, return its cfuntion pointer. On error, return NULL.
*/
lua_CFunction lua_getcfunction (Object *object)
{
if (object == NULL) return NULL;
if (tag(object) != T_CFUNCTION) return NULL;
else return (fvalue(object));
}
/*
** Given an object handle, return its user data. On error, return NULL.
*/
void *lua_getuserdata (Object *object)
{
if (object == NULL) return NULL;
if (tag(object) != T_USERDATA) return NULL;
else return (uvalue(object));
}
/*
** Given an object handle and a field name, return its field object.
** On error, return NULL.
*/
Object *lua_getfield (Object *object, char *field)
{
if (object == NULL) return NULL;
if (tag(object) != T_ARRAY)
return NULL;
else
{
Object ref;
tag(&ref) = T_STRING;
svalue(&ref) = lua_createstring(lua_strdup(field));
return (lua_hashdefine(avalue(object), &ref));
}
}
/*
** Given an object handle and an index, return its indexed object.
** On error, return NULL.
*/
//lua_getindexed() 함수와 lua_getglobal() 함수도 getxxx 계열 함수입니다만 코드 패턴이 조금 다릅니다.
//lua_getindexed() 함수는 루아 오브젝트 인스턴스가 배열일 경우에 index 위치의 아이템 오브젝트 인스턴스를 찾아서 포인터를 리턴합니다.
//lua_getglobal() 함수는 심볼 이름으로 심볼 테이블에서 오브젝트 인스턴스 포인터를 받아서 리턴합니다.
Object *lua_getindexed (Object *object, float index)
{
if (object == NULL) return NULL;
if (tag(object) != T_ARRAY)
return NULL;
else
{
Object ref;
tag(&ref) = T_NUMBER;
nvalue(&ref) = index;
return (lua_hashdefine(avalue(object), &ref));
}
}
/*
** Get a global object. Return the object handle or NULL on error.
*/
Object *lua_getglobal (char *name)
{
int n = lua_findsymbol(name);
if (n < 0) return NULL;
return &s_object(n);
}
/*
** Pop and return an object
*/
//lua_pop() 함수는 스택 팝하는 함수입니다.
//나머지 push 계열 함수들은 스택에 필요한 값을 넣는 함수입니다.
//호스트 프로그램에서 루아 VM 스택에 값을 직접 넣을 수 있도록 제공하는 API인가봅니다.
Object *lua_pop (void)
{
if (top <= base) return NULL;
top--;
return top;
}
/*
** Push a nil object
*/
int lua_pushnil (void)
{
if ((top-stack) >= MAXSTACK-1)
{
lua_error ("stack overflow");
return 1;
}
tag(top) = T_NIL;
return 0;
}
/*
** Push an object (tag=number) to stack. Return 0 on success or 1 on error.
*/
int lua_pushnumber (real n)
{
if ((top-stack) >= MAXSTACK-1)
{
lua_error ("stack overflow");
return 1;
}
tag(top) = T_NUMBER; nvalue(top++) = n;
return 0;
}
/*
** Push an object (tag=string) to stack. Return 0 on success or 1 on error.
*/
int lua_pushstring (char *s)
{
if ((top-stack) >= MAXSTACK-1)
{
lua_error ("stack overflow");
return 1;
}
tag(top) = T_STRING;
svalue(top++) = lua_createstring(lua_strdup(s));
return 0;
}
/*
** Push an object (tag=cfunction) to stack. Return 0 on success or 1 on error.
*/
int lua_pushcfunction (lua_CFunction fn)
{
if ((top-stack) >= MAXSTACK-1)
{
lua_error ("stack overflow");
return 1;
}
tag(top) = T_CFUNCTION; fvalue(top++) = fn;
return 0;
}
/*
** Push an object (tag=userdata) to stack. Return 0 on success or 1 on error.
*/
int lua_pushuserdata (void *u)
{
if ((top-stack) >= MAXSTACK-1)
{
lua_error ("stack overflow");
return 1;
}
tag(top) = T_USERDATA; uvalue(top++) = u;
return 0;
}
/*
** Push an object to stack.
*/
int lua_pushobject (Object *o)
{
if ((top-stack) >= MAXSTACK-1)
{
lua_error ("stack overflow");
return 1;
}
*top++ = *o;
return 0;
}
/*
** Store top of the stack at a global variable array field.
** Return 1 on error, 0 on success.
*/
//store 계열 함수는 스택에서 값을 읽는 함수입니다. 스택 자체도 조정하네요.
int lua_storeglobal (char *name)
{
int n = lua_findsymbol (name);
if (n < 0) return 1;
if (tag(top-1) == T_MARK) return 1;
s_object(n) = *(--top);
return 0;
}
/*
** Store top of the stack at an array field. Return 1 on error, 0 on success.
*/
int lua_storefield (lua_Object object, char *field)
{
if (tag(object) != T_ARRAY)
return 1;
else
{
Object ref, *h;
tag(&ref) = T_STRING;
svalue(&ref) = lua_createstring(lua_strdup(field));
h = lua_hashdefine(avalue(object), &ref);
if (h == NULL) return 1;
if (tag(top-1) == T_MARK) return 1;
*h = *(--top);
}
return 0;
}
/*
** Store top of the stack at an array index. Return 1 on error, 0 on success.
*/
int lua_storeindexed (lua_Object object, float index)
{
if (tag(object) != T_ARRAY)
return 1;
else
{
Object ref, *h;
tag(&ref) = T_NUMBER;
nvalue(&ref) = index;
h = lua_hashdefine(avalue(object), &ref);
if (h == NULL) return 1;
if (tag(top-1) == T_MARK) return 1;
*h = *(--top);
}
return 0;
}
/*
** Given an object handle, return if it is nil.
*/
//is계열 함수는 파라메터로 전달한 오브젝트 인스턴스의 루아 자료형을 판단합니다.
//역시 호스트 프로그램에서 루아 객체 포인터가 어떤 타입인지 알고 싶을 때 쓰는 API 함수입니다.
int lua_isnil (Object *object)
{
return (object != NULL && tag(object) == T_NIL);
}
/*
** Given an object handle, return if it is a number one.
*/
int lua_isnumber (Object *object)
{
return (object != NULL && tag(object) == T_NUMBER);
}
/*
** Given an object handle, return if it is a string one.
*/
int lua_isstring (Object *object)
{
return (object != NULL && tag(object) == T_STRING);
}
/*
** Given an object handle, return if it is an array one.
*/
int lua_istable (Object *object)
{
return (object != NULL && tag(object) == T_ARRAY);
}
/*
** Given an object handle, return if it is a cfunction one.
*/
int lua_iscfunction (Object *object)
{
return (object != NULL && tag(object) == T_CFUNCTION);
}
/*
** Given an object handle, return if it is an user data one.
*/
int lua_isuserdata (Object *object)
{
return (object != NULL && tag(object) == T_USERDATA);
}
/*
** Internal function: return an object type.
*/
void lua_type (void)
{
Object *o = lua_getparam(1);
lua_pushstring (lua_constant[tag(o)]);
}
/*
** Internal function: convert an object to a number
*/
void lua_obj2number (void)
{
Object *o = lua_getparam(1);
lua_pushobject (lua_convtonumber(o));
}
/*
** Internal function: print object values
*/
void lua_print (void)
{
int i=1;
void *obj;
while ((obj=lua_getparam (i++)) != NULL)
{
if (lua_isnumber(obj)) printf("%g\n",lua_getnumber (obj));
else if (lua_isstring(obj)) printf("%s\n",lua_getstring (obj));
else if (lua_iscfunction(obj)) printf("cfunction: %p\n",lua_getcfunction (obj));
else if (lua_isuserdata(obj)) printf("userdata: %p\n",lua_getuserdata (obj));
else if (lua_istable(obj)) printf("table: %p\n",obj);
else if (lua_isnil(obj)) printf("nil\n");
else printf("invalid value to print\n");
}
}
/*
** Internal function: do a file
*/
void lua_internaldofile (void)
{
lua_Object obj = lua_getparam (1);
if (lua_isstring(obj) && !lua_dofile(lua_getstring(obj)))
lua_pushnumber(1);
else
lua_pushnil();
}
/*
** Internal function: do a string
*/
void lua_internaldostring (void)
{
lua_Object obj = lua_getparam (1);
if (lua_isstring(obj) && !lua_dostring(lua_getstring(obj)))
lua_pushnumber(1);
else
lua_pushnil();
}
반응형
'1. 프로그래밍 > 2) LUA' 카테고리의 다른 글
#9 [Lua 1.1] hash.h, hash.c 파일 읽기 (0) | 2019.08.27 |
---|---|
#8 [LUA 1.1] lua.stx 파일 읽어보기 (0) | 2019.08.26 |
#7 [LUA 1.1] lex.c 파일 읽어보기 (0) | 2019.08.23 |
#6 [lua 1.1] 개요 - Lua 내부 코드 읽어보기 (0) | 2019.08.21 |
#5. [LUA] Lua 설치하기(Lua For Windows) (0) | 2019.04.19 |
Comments