최상단 광고

2012년 4월 11일 수요일

[Lua] 루아 5.0 레퍼런스 매뉴얼 - 번역


[Lua] 루아 5.0 레퍼런스 매뉴얼 - 번역

1 - Introduction

루아는 일반적인 절차적 프로그래밍을 지원하기 위해 데이터 기술 기능(data description facilities)을 포함하여 설계된 확장 프로그래밍 언어이다. 그리고 루아는 object-oriented 프로그래밍, functional 프로그래밍, data-driven 프로그래밍을 위한 지원을 제공하기도 한다. 루아는 강력하고 가벼운 configuration 언어를 필요로 하는 프로그램을 위해 설계되었다. 루아는 라이브러리로서 구현되었으며 clean C(즉, ANSI C 와 C++의 공통 부분집합)로 작성되었다
확장 언어이기 대문에 루아는 "main"프로그램에 대한 개념을 가지고 있지 않다 : 그것은 단지 호스트 클라이언트에 내장되어(embedded) 작동할 뿐이며, embedding program으로 불리거나 간단히 host라고 불리기도 한다. 이 호스트 프로그램은 루아 코드의 일부를 실행하기 위한 함수를 호출할 수도 있으며, 루아 변수에 값을 쓰거나 변수에서 값을 읽어들일 수도 있다. 비록 C 함수를 사용하고 있지만 루아는 서로 다른 분야(domains)의 광범위한 영역에 적용될 수 있도록 확장될 수 있으며, 결국 의미구조적인(syntactical) 프레임워크를 공유하는 사용자 정의(customized) 프로그래밍 언어를 만들어 내게 되는 것이다.
루아 배포판은 독립(stand-alone) embedding program인 lua를 포함하며, 그것은 온전한 루아 인터프리터를 제공하기 위해 루아 라이브러리를 사용한다.
루아는 무료 소프트웨어이며, 저작권 정보에 기술되어 있는 것 처럼 개런티를 받지 않고 제공된다. 이 매뉴얼에 기술되어 있는 구현들은 루아의 공식 웹 사이트인 www.lua.org에서 이용 가능하다.
다른 참조 매뉴얼과 마찬가지로 이 문서도 딱딱하게 기술되었다. 루아 설계의 이면에 있는 여러 가지 논의들에 대해 알고자 하면 루아 웹사이트에서 아래의 문서를 참조하라.
  • R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. Lua---an extensible extension language.Software: Practice & Experience 26 #6 (1996) 635-652.
  • L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. The design and implementation of a language for extending applications. Proceedings of XXI Brazilian Seminar on Software and Hardware (1994) 273-283.
  • L. H. de Figueiredo, R. Ierusalimschy, and W. Celes. Lua: an extensible embedded language.Dr. Dobb's Journal 21 #12 (Dec 1996) 26-33.
  • R. Ierusalimschy, L. H. de Figueiredo, and W. Celes. The evolution of an extension language: a history of Lua, Proceedings of V Brazilian Symposium on Programming Languages (2001) B-14-B-28.
루아는 포르투갈어로 "달"을 의미하며, 루-아(LOO-ah) 라고 발음한다.

2 - The Language

이 섹션은 루아의 어휘론(lexis), 구문론(syntax), 의미론(semantics)를 설명한다. 다시 말하면 이 섹션은 어떠한 토큰이 유효한지, 어떻게 그것들이 결합되는지, 그것들의 조합이 무엇을 의미하는지를 설명하는 것이다.
언어 구조는 일반적인 확장 BNF를 사용해 설명될 것이다. 확장 BNF에서 {a} 는 0이나 a가 의미하는 것 이상, 그리고 [a] 는 선택적인 a이다. non-terminal은 italics로 표시하며, 키워드는 bold로 표시하며, 다른 terminal 심볼들은 작은 따옴표로 묶인 typewriter폰트로 표시한다.

2.1 - Lexical Conventions

루아에서 식별자는 문자열, 십진수, 언더바일 수 있는데 숫자가 제일 앞에 나와서는 안 된다. 이것은 대부분의 언어에서 식별자를 정의할 때 공통된 부분이다. (문자의 정의는 현재 언어설정에 의존한다 : 현재 언어설정에 의해 알파벳으로 인식되는 모든 문자는 식별자로 사용될 수 있다.)
다음 키워드 들이 예약되어 있으며 식별자로 사용될 수 없다 : and break do else elseif end false for function if in local nil not or repeat return then true until while
루아는 대소문자를 구별하는 언어이다 : and는 예약되어 있는 단어지만 And나 AND는 둘 다 서로 다른 유효한 식별자이다. 언더바로 시작하고 대문자가 뒤에 따라오는 식별자는(예를 들면 _VERSION) 루아에 의해서 내부 변수를 위해 예약되어 있다.
다음 문자열들은 토큰이다 : + - * / ^ = ~= <= >= < > == ( ) { } [ ] ; : , . .. ...
문자열 상수는 작은 따옴표나 큰 따옴표를 씌움으로써 범위가 결정되며, 다음과 같은 C 와 유사한 escape sequence를 포함할 수 있다 :
  • \a --- bell
  • \b --- backspace
  • \f --- form feed
  • \n --- newline
  • \r --- carriage return
  • \t --- horizontal tab
  • \v --- vertical tab
  • \\ --- backslash
  • \" --- quotation mark
  • \' --- apostrophe
  • \[ --- left square bracket
  • \] --- right square bracket
더우기 '\newline' (즉 실제 newline 앞의 백슬래쉬)는 문자열 안에 newline을 삽입하게 된다. 문자열 안의 문자(character)는 escape sequence '\ddd'을 사용하는 숫자값에 의재 지정될 수도 있는데, ddd은 3자리의 십진값까지의 sequence이다. 루아에서 문자열은 8비트 값을 가지는데, 내포된 0을 포함하며, 이는 '\0'으로 식별될 수 있다. (역주 : C/C++에서의 null 종료 문자열)
literal 문자열은 이중 사각 괄호 [[ · · · ]] 로 범위를 결정할 수도 있다. 이 괄호로 둘러 싸인 폼에서 문자열은 여러 줄을 위해 실행될 수 있으며, 내포된 [[ · · · ]] 쌍을 포함할 수도 있으며, 어떠한 escape sequence도 해석하지 않는다. 편의를 위해 [[을 열고 나서 바로 다음에 newline이 오면 이 newline은 문자열에 포함되지 않는다. 예를 들어서 (`a´는 97로 되어 있고, newline은 10으로, `1´은 49로 되어 있는) ASCII를 사용하는 시스템에서, 아래의 네 literal은 모두 같은 문자열로 인식된다 : (1) "alo\n123\"" (2) '\97lo\10\04923"' (3) [[alo 123"]] (4) [[ alo 123"]]
수치 상수 는 선택적인 십진 부분(part) 및 선택적인 십진 지수(exponent)와 함께 작성된다. 유효한 numerical constant의 예는 다음과 같다. 3 3.0 3.1416 314.16e-2 0.31416E1
주석은 문자열 바깥에서는 어디서든 시작할 수 있으며 두 개의 하이픈(--)과 함께 사용된다. -- 바로 다음에 오는 텍스트가 [[에 있는 텍스트와 다른 점이 있다면, 이 주석은 짧은 주석이라는 것이며, 이는 라인의 끝까지만 실행된다는 것이다. 반면에 긴 주석은 ]]가 올 때까지 실행된다. 긴 주석은 몇 줄에 걸쳐 실행되며, 내포된 [[ · · · ]] 쌍을 가지는 것도 가능하다.
편의를 위해 청크의 첫 번째 라인은 #로 시작된다면 무시된다. 이 기능은 유닉스 시스템에서 스크립트 인터프리터로서의 루아의 사용을 허용한다. (6을 참조하라)

2.2 - Values and Types

루아는 동적으로 형이 결정되는 언어이다. 그것은 변수들이 형을 가지지 않는다는 것을 의미한다; 단지 값만이 형을 가진다. 이 언어에는 형정의가 존재하지 않는다. 모든 값들은 자신만의 유형을 가지고 있다.
루아에는 8가지 기본형이 있다 : nilbooleannumberstringfunctionuserdatathread, and table.Nil은 nil 값의 유형이며, 그것의 주요 목적은 다른 값들의 그것과는 다르다; 보통 그것은 유용한 값이 존재하지 않음을 표현한다. Boolean 은 false 및 true 값을 표현한다. 루아에서 nil 과 false 는 false 상태를 만든다; 다른 값들은 true를 만든다. Number 는 실제( 배정밀도 부동 소수점 소수) 숫자를 표현한다. (숫자에 대한 단일 정밀도 소수점 수나 long integer와 같은 다른 내부 표현을 사용하는 루아 인터프리터를 빌드하는 것은 쉽다.) String은 문자의 배열을 표현한다.
루아는 8비트 정렬이다 : 문자열들은 8비트 문자를 포함하는데, 내포된 0('\0') 을 포함한다(2.1를 참조하라).
함수는 루아에서 최상위 값이다. 그것은 함수가 값으로 저장될 수 있음을 의미하며, 다른 함수에 인자로서 넘겨질 수 있음을 의미하고, 결과로 반환될 수 있음을 의미한다. 루아는 루아로 작성된 함수 및 C로 작성된 함수를 호출(또는 조작)할 수 있다. (a href="#functioncall">2.5.7를 참조하라).
userdata 유형은 임의의 C 데이터가 루아 변수로 저장되는 것을 허용하기 위해 제공된다. 이 유형은 물리 메모리 블록에 해당하며, 할당과 식별 검사(identity test)를 제외하고는 루아에서 기정의된 연산을 가지고 있지 않다. 그러나 메타테이블을 사용함으로써 프로그래머는 userdata 값을 위한 연산을 정의할 수 있다. (2.8를 참조하라). userdata 값은 루아에서 생성되거나 수정될 수 없으며, 단지 C API를 통해서만 가능하다. 이것은 호스트 프로그램에 의해 소유된 데이터의 무결성을 보장한다.
스레드 유형은 실행에 독립적인 스레드를 표현하며, 그것은 coroutine을 구현하기 위해 사용된다.
테이블은 연관 배열을 구현한다. 즉 숫자뿐만 아니라 (nil을 제외한) 특정 값에 의해 인덱싱될 수 있는 배열을 의미한다. 더우기 테이블은 구성이 이질적일 수가 있다. 즉 그것들은 (nil을 제외한) 모든 유형의 값을 포함할 수 있다는 것이다. 테이블은 루아의 유일한 데이터 구조화 메커니즘이다 : 그것들은 정규 배열, 심볼 테이블, 집합(set), 레코드, 그래프, 트리 등을 표현하는 데 사용될 수 있을 것이다. 레코드를 표현하기 위해 루아는 인덱스로서 필드 이름을 사용한다. 이 언어는 a["name"]에 대한 구문론적 대체물로서 a.name를 제공함으로써 이러한 구현을 지원한다. 루아에서 테이블을 생성하는 데는 몇 가지 제약이 있다(2.5.6를 참조하라).
인덱스와 유사하게 테이블 필드의 값은 (nil을 제외한) 어떠한 유형이라도 될 수 있다. 특별히 함수는 최상위 클래스 값이기 때문에 테이블 필드는 함수를 포함할 수도 있다. 결국 테이블은 메서드도 포함할 수 있다 (2.5.8를 참조하라).
테이블, 함수, userdata 값은 objects이다 : 변수들은 실제로 이러한 값들을 포함하지 않으며, 단지 참조할 뿐이다. 할당, 인자 전달, 그리고 함수 반환은 항상 그러한 값들에 대한 참조를 조작한다; 이러한 연산들은 어떠한 종류의 복제도 내포하지 않는다.
라이브러리 함수 type 은 주어진 값의 유형을 기술하는 문자열을 반환한다. (5.1을 참조하라).

2.2.1 - Coercion

루아는 실행시간에 문자열과 숫자 값에 대한 자동 형변환을 제공한다. 문자열에 적용된 모든 수학 연산은 다음과 같은 일반적인 규칙을 가지고 그 문자열을 숫자로 바꾸려고 시도할 것이다. 반대로 문자열이 요구되는 곳에 숫자가 사용되면, 숫자가 적절한 형식으로 문자열로 변환된다는 것이다. 얼마나 많은 숫자들이 문자열로 변환되는 지를 제어하기 위해서 string 라이브러리로부터 format 함수를 사용한다(5.3 참조하라).

2.3 - Variables

변수는 값을 저장하는 공간이다. 루아에는 세 가지 종류의 변수가 있다 : 전역 변수, 지역 변수, 테이블 필드.
단일 이름은 전역 변수나 지역 변수로 구분지어질 수 있다 (또는 지역 변수의 특별한 형태인 함수의 형식(formal)인자일 수 있다): var ::= Name

변수는 명시적으로 local로 선언되지 않으면 전역으로 간주된다 (2.4.7 참조하라). 지역 변수는 어휘적(lexically)으로 범위가 구분지어진다 : 함수에 의해 자유롭게 접근 가능한 지역변수는 그들(함수)만의 영역 안에서 정의된다 (2.6 참조하라).
변수에 첫 번째 할당이 일어나기 전에 그것의 값은 nil이다.
사각 괄호는 테이블을 인덱싱하기 위해 사용된다 : var ::= prefixexp `[´ exp `]´

첫 번째 식(prefixexp)는 테이블 값을 결과로 산출한다; 두 번째 식(exp)은 그 테이블 내부의 특정 엔트리를 지시한다. 인덱싱되기 위한 테이블을 표시하는 식은 제한된 구문을 가지고 있다. 세부적인 사항을 위해서는2.5를 참조하라.
var.NAME 구문은 단지 var["NAME"]의 대체 구문일 뿐이다 : var ::= prefixexp `.´ Name
전역 변수와 테이블 필드에 대한 접근의 의미는 메타테이블을 통해 변경될 수 있다. 색인화된 변수 t[i]에 대한 접근은 gettable_event(t, i)와 동일하다. (gettable_event 함수에 대한 전체 면세를 원한다면 2.8을 참조하라. 이 함수는 루아에서 정의되거나 호출되지 않는다. 우리는 설명의 목적으로만 여기서 그것을 사용한다.)
모든 전역 변수들은 정규 루아 테이블의 필드로써 존재하는데, 이는 environment tables 혹은 간단히environments라고 불린다. C에서 작성되어 루아로 외포된 함수(C functions)들은 모두 공통 global environment를 공유한다. 루아로 작성된 각 함수(C functions)들은 environment에 대한 자신만의 참조를 소유한다. 그러므로 그 함수 안의 모든 전역 변수들은 그 environment 테이블을 참조할 것이다. 함수가 생성될 때, 그것은 자신을 생성한 함수로부터 environment를 상속한다. 루아 함수의 environment 테이블을 변경하거나 획득하기 위해서는, setfenv를 호출하거나 getfenv를 호출하라(5.1를 참조하라).
전역 변수 x에 대한 접근은 _env.x와 동일하며, 그것은 또 다음과 같다. gettable_event(_env, "x")

여기에서_env는 실행중인 함수의 environment이다.(_env 변수는 루아에 정의되어 있지 않다. 우리는 여기에서 설명의 목적으로만 그것을 사용한다.)

2.4 - Statements

루아는 대부분의 전통적인 문장 집합을 지원하는데 Pascal이나 C에서의 그것들과 유사하다. 이러한 집합들은 할당, 제어 구조, 절차적 호출, 테이블 생성자, 변수 선언을 포함한다.

2.4.1 - Chunks

루아의 실행 단위는 청크라고 불린다. 청크는 간단히 말하면 일련의 문장들인데, 이 문장들은 순차적으로 실행된다. 각 문장 뒤에는 선택적으로 세미콜론(;)이 올 수 있다 : chunk ::= {stat [`;´]}
루아는 무명(anonymous) 함수의 몸체로서 청크를 다룬다(2.5.8을 참조하라). 그렇기 때문에 청크는 지역변수를 정의하고 값을 반환할 수 있다.
청크는 파일이나 호스트 프로그램 안의 문자열에 저장될 수 있다. 청크가 실행될 때, 먼저 그것은 가상머신에 대한 목적코드(opcode)로 미리 컴파일된다. 그리고 나서 컴파일된 코드는 가상머신에 대한 인터프리터에 의해 실행된다.
청크는 바이너리 형식으로 미리 컴파일될 수도 있다; 세부 사항을 알기 위해서는 luac 프로그램을 참조하라. 소스에 있는 프로그램이나 컴파일된 형식은 교환가능하다; 루아는 자동적으로 파일 유형을 검사하고, 적절하게 행동한다.

2.4.2 - Blocks

블록은 문장의 리스트이다. 구문론적으로 구문론적으로 문장의 청크와 동일하다 : block ::= chunk
단일 문장을 생성하기 위해서 블록의 경계가 경계가 명시적으로 정해질 수 있다 : stat ::= do block end
명시적인 블록은 변수 선언의 영역을 제어하는데 유용하다. 또한 명시적 블록은 종종 return 이나 break 문장을 다른 블록의 중간에 추가하기 위해서 사용되기도 한다(2.4.4를 참조하라).

2.4.3 - Assignment

루아는 다중 할당문을 허용한다. 결국 할당 구문은 왼쪽에 변수의 리스트를 정의하고 오른쪽에 식의 리스트를 정의한다. 두 리스트의 요소들은 콤마(,)에 의해 구분된다 : stat ::= varlist1 `=´ explist1 varlist1 ::= var {`,´ var} explist1 ::= exp {`,´ exp}
할당 이전에 값의 리스트들은 변수의 리스트의 길이에 맞게 조정된다. 만약 필요한 것보다 많은 값들이 존재한다면, 초과하는 값들은 버려진다. 만약 필요한 것보다 적은 값들이 존재한다면, 그 리스트는 필요한 만큼nil로 확장된다. 만약 표현식의 리스트가 함수 호출로 끝난다면, 그 함수에 의해 반환되는 모든 값들이 조정(adjustment) 이전에 값의 리스트에 들어가게 된다. (그 호출이 괄호로 닫혀 있을 때는 예외이다; 2.5를 참조하라).
할당 문은 먼저 그것의 표현식들을 모두 평가하며, 그리고 나서 단지 할당만이 수행된다. 즉 이 코드 i = 3 i, a[i] = i+1, 20

는 a[3]을 20으로 설정하는데 a[4]에 영향을 미치지 않는다. 왜냐하면 a[i]에서의 i는 4가 할당되기 이전에 (3으로) 평가되기 때문이다. 이와 유사하게 다음 라인은
x, y = y, x
x와 y의 값을 교체한다.
전역변수와 테이블 필드에 대한 할당의 의미는 메타테이블을 통해 변경될 수 있다. 색인화된 변수 t[i] = val에 대한 할당은 settable_event(t,i,val)와 동일하다. (settable_event 함수에 대한 전체 명세를 원한다면 2.8을 참조하라. 이 함수는 루아에서 호출하거나 정의하지 않는다. 우리는 단지 여기서 설명의 목적으로만 사용한 것이다.)
전역변수 x = val에 대한 할당은 _env.x = val와 동일하며, 그것은 다시 아래의 코드와 동일하다. settable_event(_env, "x", val)
여기에서 _env 는 실행 함수의 environment이다. (_env 변수는 루아에서 정의되어 있지 ㅇ낳다. 우리는 여기에서 단지 설명의 목적으로만 그것을 사용한다.)

2.4.4 - Control Structures

제어 구조 ifwhilerepeat는 일반적 의미이며 (역주 : 다른 언어와) 유사한 구문을 가진다 : stat ::=while exp do block end stat ::= repeat block until exp stat ::= if exp then block {elseif exp thenblock} [else block] end
루아는 두 가지 특징을 가진 for문도 가지고 있다(2.4.5를 참조하라).
제어 구조의 상태 표현식 exp은 특정 값을 반환할 것이다. false 와 nil 모두 거짓인 상태를 나타낸다. 이를 제외한 다른 값들은 참인 상태를 나타낸다 (특히 숫자 0과 비어 있는 문자열 역시 참인 상태를 나타낸다).
return 문은 함수나 청크로부터 값을 반환하는데 사용된다. 함수와 청크는 하나 이상의 값을 반환할 수 있다. 그래서 return문을 위한 구문은 다음과 같다. stat ::= return [explist1]
break문은 whilerepeatfor 문의 실행을 중단하기 위해 사용된다. 루프 다음의 문장으로 이동하게 된다. stat ::= break
break는 가장 깊은 닫혀진 루프를 끝낸다.(역주 : 자신이 속한 닫혀진 루프를 종료한다는 의미)
구문론적인 이유로 return 및 break문은 단지 블록의 마지막 문장으로만 작성될 수 있다. 만약 블록의 중간에서 return 및 break가 필요하다면, 명시적인 내부 블록이 사용될 수 있는데, `do return end´ 와 `do break end´ 관용구(idioms)와 같이 표현될 수 있다. 왜냐하면 이제 return 과 break가 그들의 (내부) 블록의 마지막 문장이기 때문이다. 경험상 그 관용구들은 단지 디버깅 동안에만 사용된다.

2.4.5 - For Statement

for문은 두 가지 형태를 가진다 : 하나는 numeric이고 하나는 generic이다.
numeric for루프는 제어 변수가 산술 처리를 통해 실행될 동안만 코드의 블록을 반봅한다. 그것은 다음과 같은 구문을 가진다 : stat ::= for Name `=´ exp `,´ exp [`,´ exp] do block end
block은 name을 위해 첫 번째 exp의 값에서 시작해, 세 번째 exp에 의해 한 단계 씩 두 번째 exp로 넘겨질 때까지 반복한다. 좀더 정확하게 이야기하자면 for문은 다음과 같다. for var = e1, e2, e3 do block end
위의 문장은 다음 코드와 동일하다 : do local var, _limit, _step = tonumber(e1), tonumber(e2), tonumber(e3) if not (var and _limit and _step) then error() end while (_step>0 and var<=_limit) or (_step<=0 and var>=_limit) do block var = var + _step end end
아래 사항에 주의하라 :
  • 세 가지 제어 표현식 모두 루프가 시작되기 전에 단지 한 번만 평가된다. 그것들은 반드시 숫자로 값을 산출해야 한다.
  • _limit 와 _step 은 비가시적인 변수이다. 여기에서 그 이름은 설명의 목적을 위해서만 사용된다.
  • 블록안에 var를 할당하지 않는다면, 행위(behavior)는 정의되지 않는다
  • 세 번째 표현식(step)이 존재하지 않으면 1 단계씩 증가가 사용된다.
  • break를 사용해 for루프를 벗어날 수 있다.
  • 루프 변수 var는 문장에 대해 지역적이다 ; 그 값을 for문이 끝나거나 break된 이후에 사용할 수 없다. 만약 루프 변수 var를 사용할 필요가 있다면, 그것을 루프를 나가거나 break하기 전에 다른 변수에 할당하라.
generic for 문은 iterators라 불리는 함수 상에서 작동한다. 각각의 반복을 위해 그것은 자신의 iterator 함수를 호출해 새로운 값을 생성하는데, 새로운 값이 nil이 되면 중지된다. generic for 루프는 다음과 같은 구문을 가지고 있다 : stat ::= for Name {`,´ Name} in explist1 do block end
for문은 다음과 같다. for var_1, ..., var_n in explist do block end
이것은 다음 코드와 동일하다 : do local _f, _s, var_1 = explist local var_2, ... , var_n while true do var_1, ..., var_n = _f(_s, var_1) if var_1 == nil then break end block end end
다음을 주의하라 :
  • explist는 한 번만 평가된다. 그것의 결과는 iterator 함수, 상태, 첫 번째 iterator variable를 위한 초기값이다.
  • _f 와 _s 는 비가시적인 변수이다. 그 이름들은 설명의 목적으로만 여기에서 사용된다.
  • 만약 블록 안에 있는 var_1를 할당하지 않는다면 행위(behavior)는 정의되지 않는다.
  • break를 사용해 for 루프를 벗어날 수 있다.
  • 루프 변수 var_i 는 문장에 대해 지역적이다; 그것들의 변수를 for가 끝난 다음에 사용할 수 없다. 이러한 값들을 사용하고자 한다면, 루프를 떠나거나 break하기 이전에 그것들을 다른 변수에 할당하라.

2.4.6 - Function Calls as Statements

가능한 부수효과(side-effects)를 허용하기 위해 함수 호출은 문장으로서 실행가능하다 : stat ::= functioncall
이 경우 모든 반환값은 버려진다. 함수 호출은 2.5.7 에 설명되어 있다.

2.4.7 - Local Declarations

지역 변수는 블록 안의 어디서든 선언될 수 있다. 그 선언은 초기 할당을 포함할 수 있다 : stat ::= localnamelist [`=´ explist1] namelist ::= Name {`,´ Name}
만약 초기할당을 하게 되면 초기 할당은 다중 할당과 동일한 의미구조를 가진다(2.4.3를 참조하라). 그렇지 않으면 모든 변수는 nil로 초기화된다.
청크 또한 블록이다(2.4.1를 참조하라). 그래서 지역 변수는 특정 명시적 블록 바깥에 있는 청크에서 선언될 수 있다. 그러한 지역 변수들은 청크가 끝날 때 소멸된다.
지역 변수에 대한 가시성 규칙은 2.6에 설명되어 있다.

2.5 - Expressions

루아에서의 기본 표현식은 다음과 같다 : exp ::= prefixexp exp ::= nil | false | true exp ::= Number exp ::= Literal exp ::= function exp ::= tableconstructor prefixexp ::= var | functioncall | `(´ exp `)´
Numbers 와 literal strings 는 2.1에 설명되어 있다 ; variables 은 2.3에 설명되어 있다 ; function definitions 은 2.5.8에 설명되어 있다 ; function calls 2.5.7에 설명되어 있다 ; table constructors 2.5.6에 설명되어 있다.
괄호로 묶여 있는 표현식은 항상 하나의 값만을 산출한다. 즉, (f(x,y,z)) 는 항상 단일 값이다. 그렇지만f는 다중 값을 반환한다. ((f(x,y,z))의 값은 f에 의해 반환되는 첫 번째 값이거나 f가 값을 반환하지 않는다면 nil값이다.)
표현식은 항상 산술 연산자, 관계 연산자, 논리 연산자를 내장하며, 이들 모두는 아래에 설명되어 있다.

2.5.1 - Arithmetic Operators

루아는 일반적인 산술 연산자를 지원한다 : 이진 + (덧셈), - (뺄셈), * (곱셈), / (나눗셈), and ^ (지수); 그리고 일진- (부정). 만약 피연산자가 숫자이거나 숫자로 바뀔 수 있는 문자열(2.2.1를 참조)이라면 지수 연산을 제외한 모든 연산은 일반적인 의미를 가진다. 지수연산은 전역 함수 __pow를 호출한다; 그렇지 않으면 적절한 메타메서드가 호출된다(2.8를 참조하라). 표준 수학적 라이브러리는 함수 __pow를 정의하는데, 우리가 생각하는 의미의 지수연산을 제공한다(5.5를 참조하라).

2.5.2 - Relational Operators

루아에서의 관계 연산자는 다음과 같다.  == ~= < > <= >=
이러한 연산자들은 항상 false나 true를 산출한다.
동등비교연산자(==)는 먼저 피연산자의 유형을 비교한다. 만약 서로 다른 형이라면 결과는 false이다. 그렇지 않으면 피연산자의 값이 비교된다. 숫자와 문자열은 일반적인 방식으로 비교된다. 오브젝트(테이블, userdata, 스레드, 함수)들은 참조에 의해 비교된다 : 두 오브젝트는 같은 오브젝트일 경우에만 동등한 것으로 판단된다. 당신이 새로운 오브젝트(테이블, userdata, 함수)를 생성할 때 마다, 이 새로운 오브젝트들은 기존에 존재하고 있던 오브젝트와는 다르다.
루아가 테이블과 userdata를 비교하는 방식을 변경할 수 있는데, "eq" 메타메서드를 사용하면 된다(a href="#metatable">2.8를 참조하라).
2.2.1의 형변환 규칙은 동등비교를 적용하지 않는다. 즉 "0"==0는 false로 평가되며, t[0] 와 t["0"] 는 테이블의 다른 엔트리로 구분된다.
order 연산자는 아래와 같이 작동한다. 만약 두 개의 인자가 숫자일 때, 그것들은 그 순서대로 비교된다. 그렇지 않고 두 개 모두 문자열일 때, 그것들의 값은 현재 언어설정에 관련해 비교된다. 그렇지 않다면 루아는 "lt"이나 "le" 메타메서드를 호출하려 시도할 것이다(a href="#metatable">2.8를 참조하라).

2.5.3 - Logical Operators

루아에서 논리 연산자는 다음과 같다. and or not
제어 구조(2.4.4 참조)와 마찬가지로 모든 논리 연산자들은 false 와 nil을 거짓으로, 다른 것들은 참으로 판단한다.
not 연산자는 항상 false 나 true를 반환한다.
결합 연산자 and 는 만약 첫 번째 인자의 값이 false 나 nil이라면 첫 번째 인자를 반환한다; 그렇지 않다면and는 그것의 두 번째 인자를 반환한다. 비결합 연산자 or 는 그것의 첫 번째 인자가 nil 이나 false가 아닐 때 그것의 첫 번째 인자를 반환한다; 그렇지 않다면 or는 그것의 두 번째 인자를 반환한다. and 과or 모두 단축 평가를 사용한다. 즉 두 번째 인자는 필요할 때만 평가된다는 것이다. 예를 들면 다음과 같다. 10 or error() -> 10 nil or "a" -> "a" nil and 10 -> nil false and error() -> false false and nil -> false false or nil -> nil 10 and 20 -> 20

2.5.4 - Concatenation

루아에서 문자열 결합 연산자는 두 개의 마침표(`..´)로 표현된다. 만약 두 피연산자가 문자열과 숫자라면, 그것들은 2.2.1에서 언급된 적절한 규칙에 의해 적절히 문자열로 변환된다. 그렇지 않으면 "concat" 메타메서드가 호출된다(2.8를 참조하라).

2.5.5 - Precedence

루아에서 연산자 우선순위는 아래의 테이블을 따르는데, 낮은 것부터 높은 것의 순으로 되어 있다 : or and < > <= >= ~= == .. + - * / not - (unary) ^
표현식에서 우선순위를 바꾸기 위해 괄효를 사용할 수 있다. 문자열 결합 (`..´) 과 지수연산 (`^´) 연산자는 오른쪽에서 결합된다. 다른 이진 연산자들은 왼쪽에서 결합된다.

2.5.6 - Table Constructors

테이블 생성자는 테이블을 생성하는 표현식이다. 생성자가 평가될 때마다 새로운 테이블이 생성된다. 생성자는 비어 있는 테이블을 생성하거나 테이블을 생성하거나 그것의 필드를 초기화할 수도 있다. 생성자의 일반 구문은 다음과 같다. tableconstructor ::= `{´ [fieldlist] `}´ fieldlist ::= field {fieldsep field} [fieldsep] field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp fieldsep ::= `,´ | `;´
[exp1] = exp2 형식의 각 필드는 새로운 테이블에 exp1 키와 exp2 값을 추가한다. name = exp형식의 필드는["name"] = exp와 동일하다. 마지막으로 exp 형식의 필드는 [i] = exp와 동일한데, i는 1로 시작하는 연속적인 숫자 정수값이다. 다른 형식의 필드들은 이 카운트에 영향을 미치지 않는다. 예를 들어 보자. a = {[f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45} do local temp = {} temp[f(1)] = g temp[1] = "x" -- 1st exp temp[2] = "y" -- 2nd exp temp.x = 1 -- temp["x"] = 1 temp[3] = f(x) -- 3rd exp temp[30] = 23 temp[4] = 45 -- 4th exp a = temp end
만약 리스트의 마지막 필드가 exp 형식을 가지고 있고, 이 표현식이 함수 호출이라면, 그 호출에 의해 반환되는 모든 값들이 리스트에 연속적으로 들어가게 될 것이다(2.5.7를 참조하라). 이를 피하기 위해서는 함수 호출을 괄호로 닫라라(2.5를 참조하라).
필드 리스트는 선택적인 trailing 구분자를 가지고 있을 수 있는데, 기계 생성(machine-generated) 코드를 위한 편의이다.

2.5.7 - Function Calls

루아에서의 함수 호출은 아래와 같은 구문을 가진다 : functioncall ::= prefixexp args
함수 호출에서, 첫 번째 prefixexp 와 args가 평가된다. 만약 prefixexp의 값이 function유형을 가진다면, 함수가 주어진 인자와 함께 호출된다. 그렇지 않으면 그것의 “call” 메타메서드가 호출되는데, 첫 번째 인자를prefixexp의 값으로서 소유하며, 원래 호출 인자가 뒤에 오게 된다(2.8 참조하라).
아래의 형식   functioncall ::= prefixexp `:´ Name args
는 “methods”를 호출하기 위해 사용될 수 있다. v:name(...) 호출은 v가 단지 한 번만 평가된다는 점을 제외하면 구문적으로 v.name(v,...)의 대체물이다.
인자는 다음과 같은 구문을 가지고 있다 : args ::= `(´ [explist1] `)´ args ::= tableconstructor args ::= Literal
모든 인자 표현식은 호출 이전에 평가된다. f{...}형식의 호출은 구문론적으로 f({...})의 대체물이다. 즉, 인자 리스트는 단일한 새로운 테이블이라는 것이다. f'...' 형식(또는 f"..." 이나 f[[...]])의 호출은f('...')에 대한 대체물이다. 즉 이 인자 리스트는 단일 문자열 상수라는 것이다.
함수는 여러 개의 결과를 반환할 수 있기 때문에(2.4.4 참조), 결과의 개수는 반드시 그것들이 사용되기 전에 조정되어 야만 한다. 만약 함수가 문장으로서 호출된다면(2.4.6 참조), 그것의 반환 리스트는 0개 요소로 조정된다. 결국 모든 반환값들이 버려진다. 만약 함수가 다른 표현식 내부나 표현식의 리스트 중간에서 호출된다면, 그것의 반환값 리스트는 1개의 요소로 조정된다. 결국 모든 반환값들이 첫 번째의 요소만 제외하고는 버려진다는 것이다. 만약 함수가 표현식 리스트의 마지막 요소로서 호출되었다면, (호출이 괄호로 묶여 있는 경우를 제외하고는) 어떠한 조정도 일어나지 않는다.
약간의 예제를 살펴 보자 : f() -- 0 개 결과로 조정된다 g(f(), x) -- f() 는 1개의 결과로 조정된다 g(x, f()) -- g는 x에다가 f()에 의해 반환되는 모든 값을 획득한다 a,b,c = f(), x -- f()는 1개의 결과로 조정된다(c는 nil을 획득한다) a,b,c = x, f() -- f()는 2개의 결과로 조정된다 a,b,c = f() -- f()는 3개의 결과로 조정된다 return f() -- f()에 의해 반환되는 모든 값을 반환한다 return x,y,f() -- f()에 의해 반환되는 모든 값과 함께 x, y를 반환한다. {f()} -- f()에 의해 반환되는 모든 값을 가진 리스트를 생성한다. {f(), nil} -- f() 는 1개의 결과로 조정된다
만약 괄호로 함수를 닫아버렸다면, 그것은 정확하게 한 개의 값을 반환하도록 조정될 것이다. return x,y,(f()) -- x, y, f()의 첫 번째 값을 반환한다 {(f())} -- 하나의 요소를 가지고 있는 테이블을 생성한다
루아의 무형식(free-format) 구문에 대한 예외 때문에, 함수 호출에서 `(´ 이전에 line break를 넣을 수 없다. 그러한 제약은 언어에서의 약간의 모호성을 배제하기 위함이다. 만약 아래와 같은 코드를 작성했다고 하자. a = f (g).x(a)
루아는 그것을 a = f(g).x(a)라고 읽을 것이다. 그래서 만약 당신이 두 개의 문장을 원한다면, 반드시 세미 콜론을 그것들 사이에 넣어야 한다. 실제로 f 호출을 하고자 한다면, (g) 이전에 line break를 제거해야만 한다.
return functioncall 형식의 호출은 tail call이라 불린다. 루아는 proper tail calls(혹은 proper tail recursion)을 구현하였다 : tail call에서 호출된 함수는 호출자 함수의 스택 엔트리를 재사용한다. 결국 프로그램이 실행할 수 있는 내포된 tail call의 개수는 제한되어 있지 않다. 그러나 tail call은 호출자 함수의 디버그 정보를 제거해 버린다. tail 호출은 단지 return이 인자로 하나의 단일 함수를 가지고 있는 등의 특정 구문에서만 일어나는 것임에 주의하라 : 이 구문은 호출자 함수가 호출된 함수의 반환값을 정확하게 반환하도록 해 준다. 그래서 다음의 모든 예제들은 tail call이다 : return (f(x)) -- 1개의 결과로 조정된다 return 2 * f(x) return x, f(x) -- 부가적인 결과 f(x); return -- 결과는 버려진다 return x or f(x) -- 1개의 결과로 조정된다

2.5.8 - Function Definitions

함수 정의를 위한 구문은 다음과 같다 function ::= function funcbody funcbody ::= `(´ [parlist1] `)´ blockend
다음 구문적 대체물은 함수 정의와 유사하다 : stat ::= function funcname funcbody stat ::= localfunction Name funcbody funcname ::= Name {`.´ Name} [`:´ Name]
이 문장은 function f () ... end
다음과 같이 해석된다 f = function () ... end
이 문장은 function t.a.b.c.f () ... end
다음과 같이 해석된다 t.a.b.c.f = function () ... end
이 문장은 local function f () ... end
다음과 같이 해석된다 local f; f = function () ... end
함수 정의는 실행가능한 표현식인데, 그것의 값은 function유형을 가지고 있다. 루아가 청크를 미리 컴파일할 때, 그것의 함수 바디 전체가 같이 미리 컴파일된다. 그리고 나서 루아가 함수 정의를 실행할 때마다, 함수는 인스턴스화(혹은 closed)된다. 이 함수 인스턴스(혹은 closure)은 표현식의 마지막 값이다. 같은 함수의 서로 다른 인스턴스들은 다른 외연(external) 지역 변수들을 참조할 것이며, 서로 다른 environment 테이블을 참조할 것이다.
지역 변수처럼 행동하는 parameter는 argument 값과 함게 초기화된다 : parlist1 ::= namelist [`,´ `...´] parlist1 ::= `...´
함수가 호출될 때 함수가 variadic이거나 vararg function가 아닌 이상 argument의 리스트는 parameters의 리스트의 길이에 조정된다. vararg function는 그것의 parameter 리스트의 끝에 세 개의 구두점 (`...´)으로 표현된다. vararg 함수는 그것의 argument 리스트를 조정하지 않는다. 대신에 그것은 모든 부가 arguments들을 arg라 불리는 내포된 parameter로 수집한다. arg의 값은 테이블이며, 부가 arguments의 개수를 저장하고 있는 필드와 위치 1, 2, ..., n에서의 부가 arguments를 가지고 있다.
예를 들어서 다음과 같은 정의를 살펴보자 : function f(a, b) end function g(a, b, ...) end function r() return 1,2,3 end
그리고 나서 우리는arguments로부터 parametes에 다음과 같은 매핑을 수행할 수 있을 것이다 : CALL PARAMETERS f(3) a=3, b=nil f(3, 4) a=3, b=4 f(3, 4, 5) a=3, b=4 f(r(), 10) a=1, b=10 f(r()) a=1, b=2 g(3) a=3, b=nil, arg={n=0} g(3, 4) a=3, b=4, arg={n=0} g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2} g(5, r()) a=5, b=1, arg={2, 3; n=2}
결과는 return문을 사용해 반환된다(2.4.4를 참조하라). 만약 제어가 return문을 만나지 않고 함수의 끝에 도달한다면, 함수는 어떠한 결과도 반환하지 않는다.
colon 구문은 methods의 정의를 위해 사용된다. 즉 묵시적인 부가 parameter인 slef를 가지고 있는 함수이다. 결국 다음과 같은 문장 function t.a.b.c:f (...) ... end
는 다음 문장의 구문적 대체물이다. t.a.b.c.f = function (self, ...) ... end

2.6 - Visibility Rules

루아는 어휘적으로 범위를 가지는 언어이다. 변수의 범위(scope)는 그것의 선헌 이후의 첫 번째 분장에서 시작해 선언을 포함하고 있는 블록의 가장 깊은 곳(역주 : 현재 자신이 속한 가장 안 쪽 블록)의 마지막에서 끝난다. 예를 들면 다음과 같다 : x = 10 -- global variable do -- new block local x = x -- new `x', with value 10 print(x) --> 10 x = x+1 do -- another block local x = x+1 -- another `x' print(x) --> 12 end print(x) --> 11 end print(x) --> 10 (the global one)
local x = x과 같은 선언에서 선언되고 있는 새로운 x 는 아직 범위 안에 있지 않으며, 두 번째 x는 바깥쪽의 변수를 참조한다는 것에 주목하라.
어휘적인 범위 규칙 때문에 지역 변수들은 그것들의 범위 안에 정의된 함수에 의해서 자유롭게 접근될 수 있다. 예를 들어 보자 : local counter = 0 function inc (x) counter = counter + x return counter end
내부 함수에 의해 사용되는 지역 변수는 내부 함수 안 쪽에서 upvalue 나 external local variable라고 불린다.
각각의 local문 실행은 새로운 지역 변수를 정의한다는 것에 주목하라. 다음의 예제 코드를 살펴보자 : a = {} local x = 20 for i=1,10 do local y = 0 a[i] = function () y=y+1; return x+y end end
루프는 10개의 closure를 생성한다(즉 10개의 무명 함수 인스턴스이다). 이러한 closures들 각각은 같은x를 공유하는 반면에 서로 다른 y 변수를 사용한다.

2.7 - Error Handling

루아는 확장 언어이기 때문에 모든 루아 실행은 호스트 프로그램의 C 코드에서 루아 라이브러리의 함수를 호출함는 것으로부터 시작한다(3.15을 참조하라). 루아 컴파일이나 실행 동안 에러가 발생할 때마다, 제어는 C로 반환되며, 그것은 (에러 메시지를 출력하는 것과 같은) 적절한 대응을 취할 수 있다.
루아 코드는 error를 호출함으로써 명시적으로 에러를 생성할 수 있다(5.1를 참조하라). 만약 루아에서 에러를 잡을 필요가 있다면, pcall 함수를 사용할 수 있다(5.1를 참조하라).

2.8 - Metatables

루아의 모든 테이블 및 userdata 오브젝트는 metatable을 소유하고 있다. 이 메타테이블은 특정한 특수 연산 하에 있는 원래의 테이블이나 userdata의 행동을 정의하는 정규 루아 테이블이다. 그것의 메타테이블의 특정 필드를 설정함으로써 오브젝트의 행동에 대한 몇 몇 관점을 변경하는 것이 가능하다. 예를 들어서 오브젝트가 덧셈의 피연산자일 때, 루아는 그것의 메타테이블에 있는 "__add"필드에 있는 함수가 있는지 검사한다. 만약 그것을 찾아내면 루아는 그 함수를 호출해 덧셈을 수행할 것이다.
우리는 events 메타테이블과 metamethods값에서 key들을 호출한다. 위의 예제에서 event는 "add" 이며, 메타메서드는 덧셈을 수행하는 함수이다.
set/getmetatable 함수를 통해서 오브젝트의 메타테이블을 변경하거나 요청할 수 있다(5.1을 참조하라).
메타테이블은 오브젝트가 수학 연산(arithmetic operations), 순서 비교(order comparisons), 결합(concatenation), 색인화(indexing)에서 취할 행동을 제어한다. 또한 메타테이블은 userdata의 가비지 컬렉션이 수행될 때 호출되는 함수를 정의하기도 한다. 그러한 연산을 각각에 대해서 루아는 이벤트라는 특별한 키를 연관지어 놓았다. 루아가 그러한 연산 중 하나를 테이블이나 userdata에 수행할 때, 그것은 관련 이벤트를 가지고 있는 메타테이블을 오브젝트가 가지고 있는지 여부를 검사한다. 만약 가지고 있다면 그 키(메타메서드)와 연관된 값은 루아가 연산을 수행하게 될 방법을 제어하게 된다.
메타테이블은 다음에 열거된 연산들을 제어한다. 각 연산은 관련 이름으로 식별된다. 각 연산의 키(key)는 앞에 두 개의 그것의 이름 앞에 두 개의 언더바가 붙은 문자열이다; 예를 들어서 “add” 연산을 위한 키는"__add"문자열이다. 이러한 연산들의 의미론은 인터프리터가 그 연산을 실행하는 방법을 기술하는 루아 함수에 의해 더 자세히 설명되어 있다.
다음 코드는 단지 설명을 위한 것이다; 실제 행동은 인터프리터에 하드코딩되어 있으며 그것은 이 시뮬레이션보다 훨씬 효율적이다. 이러한 설명에서 사용되는 모든 함수는(rawgettonumber 등) 5.1에 설명되어 있다. 특히 주어진 오브젝트의 메타메서드를 검색하기 위해서는 다음 표현식을 사용한다. metatable(obj)[event] 이것은 다음과 같이 읽는다 rawget(metatable(obj) or {}, event)
즉 메타메서드에 대한 접근은 다른 메타메서드에 대한 호출을 발생하지 않으며, 메타테이블 없이 오브젝트에 대해 접근하는 것은 실패하지 않을 것이다(그것은 간단히 nil을 산출한다).
  • "add": the + operation.
    아래의 getbinhandler 함수는 루아가 이진 연산에 대한 핸들러를 선택하는 방식을 정의한다. 먼저 루아는 첫 번째 피연산자를 검사한다. 만약 그것의 유형이 연산에 대한 핸들러를 정의하지 않는다면, 루아는 두 번째 피연산자를 검사한다. function getbinhandler (op1, op2, event) return metatable(op1)[event] or metatable(op2)[event] end
    그 함수를 사용할 때 op1 + op2의 동작은 다음과 같다. function add_event (op1, op2) local o1, o2 = tonumber(op1), tonumber(op2) if o1 and o2 then -- 두 피연산자 모두 숫자형인가? return o1 + o2 -- `+' 이것은 기본적인 `add' else -- 하나 이상의 피연산자가 숫자가 아니다 local h = getbinhandler(op1, op2, "__add") if h then -- 두 피연산자를 가지고 핸들러를 호출하라 return h(op1, op2) else -- 사용가능한 핸들러가 없다 : 기본 행동을 취한다 error("...") end end end
  • "sub": the - operation. Behavior similar to the "add" operation.
  • "mul": the * operation. Behavior similar to the "add" operation.
  • "div": the / operation. Behavior similar to the "add" operation.
  • "pow": the ^ (exponentiation) operation. function pow_event (op1, op2) local o1, o2 = tonumber(op1), tonumber(op2) if o1 and o2 then -- both operands are numeric? return __pow(o1, o2) -- call global `__pow' else -- at least one of the operands is not numeric local h = getbinhandler(op1, op2, "__pow") if h then -- call the handler with both operands return h(op1, op2) else -- no handler available: default behavior error("...") end end end
  • "unm": the unary - operation. function unm_event (op) local o = tonumber(op) if o then -- operand is numeric? return -o -- `-' here is the primitive `unm' else -- the operand is not numeric. -- Try to get a handler from the operand local h = metatable(op).__unm if h then -- call the handler with the operand and nil return h(op, nil) else -- no handler available: default behavior error("...") end end end
  • "concat": the .. (concatenation) operation. function concat_event (op1, op2) if (type(op1) == "string" or type(op1) == "number") and (type(op2) == "string" or type(op2) == "number") then return op1 .. op2 -- primitive string concatenation else local h = getbinhandler(op1, op2, "__concat") if h then return h(op1, op2) else error("...") end end end
  • "eq": the == operation. The function getcomphandler defines how Lua chooses a metamethod for comparison operators. A metamethod only is selected when both objects being compared have the same type and the same metamethod for the selected operation. function getcomphandler (op1, op2, event) if type(op1) ~= type(op2) then return nil end local mm1 = metatable(op1)[event] local mm2 = metatable(op2)[event] if mm1 == mm2 then return mm1 else return nil end end The "eq" event is defined as follows: function eq_event (op1, op2) if type(op1) ~= type(op2) then -- different types? return false -- different objects end if op1 == op2 then -- primitive equal? return true -- objects are equal end -- try metamethod local h = getcomphandler(op1, op2, "__eq") if h then return h(op1, op2) else return false end end a ~= bis equivalent to not (a == b).
  • "lt": the < operation. function lt_event (op1, op2) if type(op1) == "number" and type(op2) == "number" then return op1 < op2 -- numeric comparison elseif type(op1) == "string" and type(op2) == "string" then return op1 < op2 -- lexicographic comparison else local h = getcomphandler(op1, op2, "__lt") if h then return h(op1, op2) else error("..."); end end end a > b is equivalent to b < a.
  • "le": the <= operation. function le_event (op1, op2) if type(op1) == "number" and type(op2) == "number" then return op1 <= op2 -- numeric comparison elseif type(op1) == "string" and type(op2) == "string" then return op1 <= op2 -- lexicographic comparison else local h = getcomphandler(op1, op2, "__le") if h then return h(op1, op2) else h = getcomphandler(op1, op2, "__lt") if h then return not h(op2, op1) else error("..."); end end end end a >= b is equivalent to b <= a. Note that, in the absence of a "le" metamethod, Lua tries the "lt", assuming that a <= b is equivalent to not (b < a).
  • "index": The indexing access table[key]. function gettable_event (table, key) local h if type(table) == "table" then local v = rawget(table, key) if v ~= nil then return v end h = metatable(table).__index if h == nil then return nil end else h = metatable(table).__index if h == nil then error("..."); end end if type(h) == "function" then return h(table, key) -- call the handler else return h[key] -- or repeat operation on it end
  • "newindex": The indexing assignment table[key] = value. function settable_event (table, key, value) local h if type(table) == "table" then local v = rawget(table, key) if v ~= nil then rawset(table, key, value); return end h = metatable(table).__newindex if h == nil then rawset(table, key, value); return end else h = metatable(table).__newindex if h == nil then error("..."); end end if type(h) == "function" then return h(table, key,value) -- call the handler else h[key] = value -- or repeat operation on it end
  • "call": called when Lua calls a value. function function_event (func, ...) if type(func) == "function" then return func(unpack(arg)) -- primitive call else local h = metatable(func).__call if h then return h(func, unpack(arg)) else error("...") end end end

2.9 - Garbage Collection

루아는 자동으로 메모리 관리를 수행한다. 그것은 새로운 오브젝트를 위한 메모리 할당이나 더 이상 필요없는 오브젝트에 대한 메모리 해제에 대해 신경쓸 필요가 없다는 것을 의미한다. 루아는 모든 dead objects를 수집하기 위해 때때로 garbage collector를 실행함으로써 자동적으로 메모리를 관리한다(즉, 그러한 오브젝트들은 루아에서 더 이상 접근할 필요가 없어진 것들이다). 루아에서의 모든 오브젝트들은 자동 관리의 통제를 받는다 : 테이블, userdata, 함수, 스레드, 문자열.
루아는 garbage-collection 주기를 제어하기 위해 두 개의 숫자를 사용한다. 하나는 루아가 사용하고 있는 동적 메모리가 얼마나 되느냐에 대한 카운트를 세고, 다른 하나는 문턱값(threshold, 역주 : 어떤 일이 수행되기 위한 계기가 되는 값)이다. 바이트의 숫자가 문턱값에 다다를 때, 루아는 가비지 콜렉터를 수행하는데, 그것은 모든 죽은 오브젝트의 메모리를 회수한다. 그 바이트 카운트는 조정되며, 문턱값은 바이트 카운터의 새로운 값의 두배로 재설정된다.
C API를 통해서 그러한 숫자들을 요청하거나 문턱값을 변경할 수 있다(3.7를 참조하라). 문턱값을 0으로 설정하는 것은 실제적으로 가비지 콜렉션을 지금 바로 할 것을 강제하는 반면에, 매우 큰 수를 설정하는 것은 가비지 콜렉터를 효율적으로 중지시키는 것이다. 루아 코드를 사용할 때 gcinfo 와 collectgarbage 함수를 통해서 가비지 콜렉션 주기에 대한 더욱 제한된 제어권을 가지게 된다(5.1를 참조하라).

2.9.1 - Garbage-Collection Metamethods

C API를 사용할 때 userdata를 위한 가비지 콜렉터 메타메서드를 설정할 수 있다(2.8). 파이널라이저(finalizer)는 당신이 루아의 가비지 콜렉션을 (파일/네트워크/데이터 베이스 연결 등을 닫는 것과 같은) 외부 리소스 관리와 협력해서 할 수 있게 허용해 준다.
메타테이블에 있는 __gc 필드로 userdata를 해제한다고 해서 곧바로 가비지 콜렉터에 의해 수집되지 않는다. 대신에 루아는 그것들을 리스트에 넣는다. 콜렉션 이후에 루아는 그 리스트에 있는 각 userdata에 대해 다음 함수와 유사한 행동을 수행한다 : function gc_event (udata) local h = metatable(udata).__gc if h then h(udata) end end
각 가비지 콜렉션 주기의 끝에서 그 주기에 수집된 것들 사이에서 userdata의 생성순서와는 반대로 그것들의 파이널라이저가 호출된다. 즉 호출되는 첫 번째 파이널라이저는 프로그램에서 마지막에 생성된 userdata와 관련이 있다..

2.9.2 - Weak Tables

weak table 은 weak references을 요소로 가지고 있는 테이블이다. 약한 참조는 가비지 콜렉터에 의해 무시된다. 다시 말하자면 만약 오브젝트에 대한 참조가 weak reference만인 경우라면 가비지 콜렉터는 그 오브젝트를 수집하지 않는다는 것이다.
weak 테이블은 weak key, weak value 둘 다 소유할 수 있다. weak 키를 가진 테이블은 그것의 키에 대한 수집을 허용하지만, 그것의 값에 대한 수집은 허용하지 않는다. weak 키와 weak 값을 모두 가지고 있는 테이블은 키와 값에 대한 수집을 모두 허용한다. 어떤 경우든지 키나 값이 수집되면, 전체 (역주 : 키/값) 쌍은 테이블에서 제거된다. 테이블의 weakness는 그것의 메타테이블에 있는 __mode필드의 값에 의해 제어된다. 만약 __mode 필드가 `k´문자를 포함하는 문자열이라면 테이블의 키는 weak이다. 만약 __mode필드가 `v´문자를 포함하는 문자열이라면 테이블의 값은 weak이다.
메타테이블로서 테이블을 사용한 다음에, 그것의 __mode 필드의 값을 변경할 수 없다. 반면에 테이블의 weak 행동은 정의되지 않은 이 메타테이블에 의해서 제어된다.

2.10 - Coroutines

루아는 coroutines를 지원하며, 이는 semi-coroutines 이나 collaborative multithreading이라고 불린다. 루아에서 coroutine은 실행에 독립적인 스레드를 표현한다. 그러나 멀티스레드 시스템에서의 스레드와는 달리 coroutine은 단지 명시적으로 yield 함수를 호출함으로써 그것의 실행을 중지할 수 있다.
coroutine.create에 대한 호출을 해서 coroutine을 생성한다. 그것의 유일한 인자는 coroutine의 메인 함수가 되는 함수이다. create 함수는 오직 새로운 coroutine만을 생성하며 그것에 대한 핸들을 반환한다(thread유형의 오브젝트).; 그렇지만 그것이 corouinte을 시작하지는 않는다.
coroutine.resume을 처음으로 호출할 때, coroutine.create에 의해 반환된 스레드가 첫 번째 인자로 넘겨지며, coroutine은 그것의 메인 함수의 시작 라인으로부터 실행을 시작한다. coroutine.resume에 넘겨지는 부가적인 인자는 coroutine의 메인 함수의 인자로서 주어진다. coroutine이 작동하기 시작하면 그것이 끝나거나 yields될 때까지 작동한다.
coroutine은 그것의 실행을 두 가지 방식으로 멈춘다 : 보통 그것의 메인 함수가 반환될 때이다(명시적 혹은 묵시적으로, 마지막 명령 이후에); 그리고 일반적이지는 않지만 보호되지 않은 에러가 존재할 때이다. 첫 번째 경우에 coroutine.resume은 true를 반환하며, 부가적으로 coroutine 메인 함수에 의해 반환되는 값을 반환한다. 에러의 경우에 coroutine.resume은 false와 함께 에러 메시지를 반환한다.
coroutine은 coroutine.yield를 호출함으로써 중지된다. coroutine이 yield(중지)하면 내포된 함수 호출 안쪽에서 yield가 발생하더라도 관련 coroutine.resume은 즉시 반환한다(즉 메인 함수 안 뿐만 아니라 메인 함수에 의해 직간접적으로 호출되는 함수에서도 가능하다). yield의 경우에 coroutine.resume 은 true를 반환하며, 부가적으로 coroutine.yield에 넘겨진 값을 반환한다. 다음으로 같은 coroutine을 resume한다. 그것은 coroutine.resume에 넘겨 주었던 부가 인자를 반환하는coroutine.yield호출과 함께 yield되었던 지점에서부터 실행을 계속한다.
coroutine.wrap 함수는 coroutine.create처럼 coroutine을 생성하는데, 대신에 coroutine 자체를 반환한다. 그것은 호출될 때 coroutine을 resume하는 함수를 반환한다. 그 함수에 넘겨지는 인자들은 resume을 위한 부가적인 인자들로서 활동한다. 그 함수는 첫 번째 것(boolean 에러 코드)만 제외하고 resume에 의해 반환되는 모든 값들을 반환한다. coroutine.resume과는 달리 이 함수는 에러를 잡지 않는다; 에러는 호출자에게 전파된다.
예재로 다음 코드를 살펴 보자 : function foo1 (a) print("foo", a) return coroutine.yield(2*a) end co = coroutine.create(function (a,b) print("co-body", a, b) local r = foo1(a+1) print("co-body", r) local r, s = coroutine.yield(a+b, a-b) print("co-body", r, s) return b, "end" end) a, b = coroutine.resume(co, 1, 10) print("main", a, b) a, b, c = coroutine.resume(co, "r") print("main", a, b, c) a, b, c = coroutine.resume(co, "x", "y") print("main", a, b, c) a, b = coroutine.resume(co, "x", "y") print("main", a, b)
그것을 실행할 때, 그것은 다음과 같은 출력을 산출할 것이다 : co-body 1 10 foo 2 main true 4 co-body r main true 11 -9 co-body x y main true 10 end main false cannot resume dead coroutine

3 - The Application Program Interface

이 섹션은 루아를 위한 C API에 대해 기술한다. 즉 루아와 상호작용하기 위한 호스트 프로그램에서 이용 가능한 c 함수들의 집합이다. 모든 API 함수들과 관련 타입, 그리고 상수들은 lua.h 헤더에 선언되어 있다.
우리가 “함수”라는 개념을 사용하고 있지만, API에서의 기능은 매크로를 대신하는 것으로써 제공되고 있다. 그러한 (언제나 Lua state인 첫 번째 인자를 제외하고는) 모든 매크로들은 그것의 인자를 정확하게 한 번씩 사용하며, 그렇기 때문에 숨겨진 부수효과(side-effect)를 생성하지 않는다.

3.1 - States

루아 라이브러리는 완전히 reentrant(재진입적?, 역주 : 실행할 때마다 새로 초기화한다는 의미인듯)이다 : 그것은 전역 변수를 소유하고 있지 않다. 루아 인터프리터의 전체 state(전역 변수, 스택 등)는 lua_State 유형의 구조체에 동적으로 할당되어 저장된다. 이 state에 대한 포인터는 scratch로부터(無에서) 루아 state를 생성하는 lua_open을 제외하고는 반드시 라이브러리의 모든 함수에 대한 첫 번째 인자로서 넘겨져야만 한다.
API 함수를 호출하기 전에 반드시 lua_open을 호출해 state를 생성해야만 한다 : lua_State *lua_open (void);
lua_open을 사용해 생성된 state를 해제하기 위해서는 call lua_close를 호출하라 : void lua_close (lua_State *L);
이 함수는 주어진 루아 state에 있는 모든 오브젝트를 (만약 존재한다면 관련 가비지 콜렉션 메타메서드를 호출함으로써) 제거하며, 그 state에 사용된 모든 동적 메모리를 해제한다. 몇 몇 플래폼에서는 이 함수를 호출할 필요가 없다. 왜냐하면 모든 리소스들이 호스트 프로그램이 종료될 때 자연적으로 해제되기 때문이다. 다른 측면에서 보자면 웹 서버나 (역주 : 리눅스의) 데몬과 같이 오래 실행되는 프로그램은 메모리를 너무 소비하는 것을 막기 위해서 state가 필요가 없어지자 마자 그것들을 해제할 필요가 있다.

3.2 - The Stack and Indices

루아는 virtual stack을 사용해 C로부터 값을 넘긴다. 이 스택의 각 요소는 루아 값(nil, 숫자, 문자열 등)을 표현한다.
루아가 C를 호출할 때마다 호출된 함수는 새로운 스택을 획득하는데, 이거것은 이전의 스택이나 아직 활성화되어 있는 C 함수의 스택에 대해 독립적이다. 그 스택은 초기에 C 함수에 대한 인자를 포함하며, C 함수가 그것의 결과를 push하는 곳이 호출자가 반환될 곳이다(3.16를 참조하라).
편의를 위해서 API의 대부분의 요청 연산은 엄밀한 스택 규칙을 따르지 않는다. 대신에 그것들은 index를 사용함으로써 스택에 있는 요소들을 참조할 수 있다 : 양의 인덱스는 (1에서 시작하는) 스택의 절대 위치를 표현하며, 음의 인덱스는 스택의 상단(top)으로부터의 오프셋을 표현한다. 더욱 특별하게 만약 스택이 n개의 요소를 가지고 있다면 인덱스 1은 첫 번째 요소를 표현하며(즉 스택에 처음 push된 요소), 인덱스 n은 마지막 요소를 표현한다; 인덱스 -1은 마지막 요소를 표현한다(즉 스택의 top에 있는 요소); 인덱스 -n은 첫 번째 요소를 표현한다. 만약 1부터 스택 top 사이에 인덱스가 있다면 그것을 유효하다고 말한다(즉 1 <= abs(index) <= top).
언제든지 lua_gettop을 호출해서 스택의 top에 대한 인덱스를 획득할 수 있다 :   int lua_gettop (lua_State *L);

인덱스가 1부터 시작하기 때문에 lua_gettop의 결과는 스택에 있는 요소의 개수와 동일하다(결국 0은 비어있는 스택을 의미한다).
루아 API로 작업을 할 때, 스택 오버플로우를 제어할 책임은 프로그래머에게 있다 다음 함수 int lua_checkstack (lua_State *L, int extra);
는 스택 크기를 top + extra 요소로 늘린다; 그것은 스택이 그 크기로 늘려질 수 없을 때 false를 반환한다. 이 함수는 결코 스택을 줄여주지 않는다; 만약 스택이 이미 새로운 크기보다 크다면, 그것은 변경되지 않은채로 남았게 된다.
루아가 C를 호출할 때, 그것은 적어도 LUA_MINSTACK 스택 위치가 이용가능하도록 보장하며, LUA_MINSTACKlua.h에 20으로 정의되어 있다. 그러므로 일반적으로 프로그래머는 자신의 코드가 스택에 반복적으로 요소를 push하고 있지 않는 한 스택 공간에 대해 걱정할 필요는 없다.
대부분의 요청(query) 함수는 인덱스를 이용 가능한 스택 공간 안에 있는 값으로서 받아들인다. 즉 인덱스는 프로그래머가 lua_checkstack을 통해 설정한 최대 스택의 크기까지이다. 그런 인덱스들은 acceptable indices라고 불린다. 더욱 공식적으로 이야기하면 우리는 acceptable index를 다음과 같이 정의한다 : (index < 0 && abs(index) <= top) || (index > 0 && index <= stackspace)
0은 절대로 받아들일 수 없는 인덱스임에 주의하라.
다른 사항이 언급되지 않는 한 유효한 인덱스를 받아들이는 모든 함수는 pseudo-indices라고 불릴 수 있는데, 이는 C 코드에서는 받아들여지는데 스택에서는 그렇지 않는 일부 루아 변수들을 표현한다. 의사-인덱스는 전역 environment, registry, C함수의 upvalue에 접근하는데 사용된다(3.17를 참조하라).

3.3 - Stack Manipulation

API는 다음과 같은 함수를 기본 스택 조작을 위해 제공한다 : void lua_settop (lua_State *L, int index); void lua_pushvalue (lua_State *L, int index); void lua_remove (lua_State *L, int index); void lua_insert (lua_State *L, int index); void lua_replace (lua_State *L, int index);
lua_settop은 acceptable 인덱스나 0을 받아들인다. 그리고 스택의 top을 그 인덱스로 설정한다. 만약 새로운 top이 기존의 것보다 높다면, 새로운 요소가 nil로 채워진다. 만약 index가 0이면, 모든 스택 요소가 제거된다. lua.h에 정의되어 있는 유용한 매크로가 다음에 있는데, #define lua_pop(L,n) lua_settop(L, -(n)-1)
이는 n 요소를 스택으로부터 제거한다.
lua_pushvalue은 주어진 인덱스에 있는 요소의 복사본을 스택에 push한다.
lua_remove 는 주어진 위치의 요소를 제거하는데, 그 위치의 위에 있던 요소를 아래로 내리고 그곳을 공백으로 채운다.
lua_insert는 주어진 위치에 top 요소를 이동시키는데, 그 위치 위에 있던 요소를 위로 올리고 열려진 공간으로 만든다(역주 : 끼워넣기, 지우는게 아님).
lua_replace는 top 요소를 주어진 위치로 이동시키는데, 요소의 이동을 행하지 않는다(결국 주어진 위치의 값이 변경된다. 역주 : 있던 값이 없어짐).
이러한 모든 함수들은 단지 유효한 인덱스만을 받아들인다.( lua_remove나 lua_insert에 의사-인덱스를 넣어 호출할 수 없다. 왜냐하면 그것들은 스택 위치를 표현하는 것이 아니기 때문이다.)
예제로서 만약 스택이 10 20 30 40 50*으로 시작한다고 한다면 (아래에서 위로; `*´ 는 top을 표시함) lua_pushvalue(L, 3) --> 10 20 30 40 50 30* lua_pushvalue(L, -1) --> 10 20 30 40 50 30 30* lua_remove(L, -3) --> 10 20 30 40 30 30* lua_remove(L, 6) --> 10 20 30 40 30* lua_insert(L, 1) --> 30 10 20 30 40* lua_insert(L, -1) --> 30 10 20 30 40* (no effect) lua_replace(L, 2) --> 30 40 20 30* lua_settop(L, -3) --> 30 40* lua_settop(L, 6) --> 30 40 nil nil nil nil*

3.4 - Querying the Stack

스택 요소의 유형을 검사하기 위해서 다음 함수들이 이용 가능하다 : int lua_type (lua_State *L, int index); int lua_isnil (lua_State *L, int index); int lua_isboolean (lua_State *L, int index); int lua_isnumber (lua_State *L, int index); int lua_isstring (lua_State *L, int index); int lua_istable (lua_State *L, int index); int lua_isfunction (lua_State *L, int index); int lua_iscfunction (lua_State *L, int index); int lua_isuserdata (lua_State *L, int index); int lua_islightuserdata (lua_State *L, int index);
이러한 함수들은 acceptable 인덱스와 함께 호출될 수 있다.
lua_type은 스택에 있는 값의 유형을 반환한다. 유효하지 않은 인덱스에 대해서는 LUA_TNONE을 반환한다(즉 그 스택 위치가 “비어있다”는 것이다). lua_type에 의해 반환되는 유형은 lua.h에 정의된 다음과 같은 상수들에 의해 코딩되어 있다 :
LUA_TNIL,
LUA_TNUMBER,
LUA_TBOOLEAN,
LUA_TSTRING,
LUA_TTABLE,
LUA_TFUNCTION,
LUA_TUSERDATA,
LUA_TTHREAD,
LUA_TLIGHTUSERDATA.
다음 함수들은 이러한 상수들을 문자열로 해석한다 : const char *lua_typename (lua_State *L, int type);
lua_is* 함수들은 오브젝트가 주어진 유형과 호환되면 1을 그렇지 않으면 0을 반환한다.
lua_isboolean은 이러한 규칙에 대해 예외이다 : 그것은 boolean값에 대해서만 성공한다(그렇지 않으면 필요가 없다. 왜냐하면 어떤 값이든 boolean 값을 가지고 있기 때문이다). 그것들은 항상 0이나 유효하지 않은 인덱스를 반환한다.
lua_isnumber는 숫자나 수치형 문자열을 받는다;
lua_isnumber accepts numbers and numerical strings;
lua_isstring 는 문자열이나 숫자를 받는다(2.2.1를 참조);
lua_isstring accepts strings and numbers (see 2.2.1);
lua_isfunction는 루아 함수나 C 함수 모두를 받는다; 그리고 lua_isfunction는 full 및 light userdata를 받는다.
루아 함수인지 C 함수인지 확인하려면, lua_iscfunction를 사용할 수 있다.
userdata가 full인지 light인지 확인하려면, lua_islightuserdata를 사용할 수 있다.
숫자인지 수치형 문자열인지 확인하려면, lua_type을 사용할 수 있다.
API는 스택에 있는 두 개의 값을 비교하는 함수도 가지고 있다 : int lua_equal (lua_State *L, int index1, int index2); int lua_rawequal (lua_State *L, int index1, int index2); int lua_lessthan (lua_State *L, int index1, int index2);
lua_equal과 lua_lessthan은 루아에서의 그것들의 닮은꼴과 동일하다.
lua_rawequal는 메타메서드를 사용하지 않고 primitive equality에 대한 값을 비교한다.
이러한 함수들은 인덱스가 유효하지 않을 때 0(false)를 반환한다.

3.5 - Getting Values from the Stack

스택에 있는 값을 지정된 C 유형으로 변환하기 위해서는, 다음과 같은 변환 함수를 사용할 수 있다 : int lua_toboolean (lua_State *L, int index); lua_Number lua_tonumber (lua_State *L, int index); const char *lua_tostring (lua_State *L, int index); size_t lua_strlen (lua_State *L, int index); lua_CFunction lua_tocfunction (lua_State *L, int index); void *lua_touserdata (lua_State *L, int index); lua_State *lua_tothread (lua_State *L, int index); void *lua_topointer (lua_State *L, int index);
이들 함수는 acceptable 인덱스와 함게 호출될 수 있다. 유효하지 않은 인덱스와 함께 호출될 때, 그것들은 주어진 값이 정확하지 않은 유형을 가지고 있는 것처럼 행동할 것이다.
lua_toboolean은 주어진 인덱스에 있는 루아값을 C “boolean”값(0이나 1)으로 바꾼다. 루아에서의 모든 테스트와 마찬가지로 lua_toboolean은 false 나 nil이 아닌 값에 대해서는 1을 반환한다; 그렇지 않으면 0을 반환한다. 또한 유효하지 않은 인덱스를 사용해 호출할 때도 0을 반환한다. (만약 실제 boolean 값만을 받아들이고자 한다면, code>lua_isboolean를 사용해서 값의 유형을 검사하라.)
lua_tonumber 는 주어진 인덱스의 루아 값을 숫자로 변환한다(기본적으로 lua_Number는 double이다). 루아 값은 반드시 숫자로 형변환 가능한 문자열이거나 숫자여야 한다(2.2.1 참조); 그렇지 않으면 lua_tonumber는 0을 반환한다.
lua_tostring는 주어진 인덱스의 루아 값을 문자열 (const char*)로 바꾼다. 루아 값은 반드시 문자열이나 숫자여야 한다; 그렇지 않으면 함수는 NULL을 반환할 것이다. 만약 값이 숫자라면, lua_tostring이 스택안의 실제 값을 문자열로 바꾸게 될 것이다. (이 변환은 lua_tostring이 키에 적용되었을 때 lua_next와 혼란을 준다) lua_tostring는 루아 state 안에 있는 문자열에 대한 완전히 정렬된 포인터를 반환한다. 이 문자열은 항상 마지막 문자 뒤에 0을 포함한다(code>'\0'). 그러나 그것의 몸체에 다른 0을 포함할 수도 있다. 만약 을 포함하고 있는 문자열인지 여부를 알 수 없을 때는 lua_strlen를 사용해서 실제 길이를 획득할 수 있다. 루아는 가비지 콜렉션을 가지고 있기 때문에 lua_tostring에 의해 반환되는 포인터가 스택으로부터 관련 값이 제거된 이후에도 유효하다는 보장을 할 수가 없다. 만약 현재 함수가 반환된 이후에도 문자열을 필요로 한다면, 그것의 복사본을 만들거나 레지스트리에 저장하도록 하라(3.18 참조).
lua_tocfunction은 스택에 있는 값을 C 함수로 변환한다. 이 값은 반드시 C 함수여야 한다; 그렇지 않으면lua_tocfunction는 NULL을 반환한다. lua_CFunction 유형은 3.16에 설명되어 있다.
lua_tothread는 스택에 있는 값을 루아 스레드(lua_State *로서 표현된다)로 변환한다. 이 값은 반드시 스레드여야 한다; 그렇지 않으면 lua_tothread는 NULL을 반환한다.
lua_topointer는 스택에 있는 값을 generic C 포인터로 변환한다(void *). 이 값은 userdata, 테이블, 스레드, 함수일 수 있다; 그렇지 않으면 lua_topointer는 NULL을 반환한다. 루아는 같은 유형의 다른 오브젝트들이 다른 포인터를 가지도록 보장한다. 포인터를 그것의 원래 값으로 돌릴 수 있는 직접적인 방법은 존재하지 않는다. 일반적으로 이 함수는 디버그 정보를 위해서 사용된다.
lua_touserdata는 3.8에 설명되어 있다.

3.6 - Pushing Values onto the Stack

API는 C 값을 스택에 넣기 위해서 다음과 같은 함수를 가지고 있다 : void lua_pushboolean (lua_State *L, int b); void lua_pushnumber (lua_State *L, lua_Number n); void lua_pushlstring (lua_State *L, const char *s, size_t len); void lua_pushstring (lua_State *L, const char *s); void lua_pushnil (lua_State *L); void lua_pushcfunction (lua_State *L, lua_CFunction f); void lua_pushlightuserdata (lua_State *L, void *p);
이 함수들은 C 값을 받아서, 그것들을 관련 루아 값으로 변환하고, 그 결과를 스택에 push한다. 특히lua_pushlstring 와 lua_pushstring은 주어진 문자열의 내부 복사본을 생성한다. lua_pushstring은 단지 적절한 C 문자열을 push하는데만 사용될 수 있다(즉 0으로 끝나며 내포된 0을 포함하지 않는 문자열); 그렇지 않으면 더 일반적인 lua_pushlstring을 사용해야 하는데, 이는 명시된 크기를 받아들인다.
형식있는(formatted) 문자열도 push할 수 있다 :   const char *lua_pushfstring (lua_State *L, const char *fmt, ...); const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);
이러한 함수들은 스택에 형식있는 문자열을 push하고 그 문자열의 포인터를 반환한다. 그것들은 sprintf 나vsprintf와 유사하지만, 조금 중요한 차이점이 있다 :
  • 결과값을 위한 공간을 할당할 필요가 없다 : 결과는 루아 문자열이며 루아는 메모리 할당(해제, 가비지 콜렉션을 통해...)을 관리한다
  • 형변환 지정자는 매우 제한되어 있다. flag, widths, precision을 사용할 수 없다. 형변환 지정자는 매우 간단하게 다음과 같다. `%%´ (`%´ 를 문자열에 삽입다), `%s´ (크기 제한없는 null 종료 문자열을 삽입한다), %f´ (lua_Number를 삽입한다), `%d´ (int를 삽입한다), `%c´ (int를 문자로 삽입한다).
다음의 함수 void lua_concat (lua_State *L, int n);
는 스택의 top에 있는 n값을 결합하고, 그것을 pop하고, top에 결과를 남겨 둔다. 만약 n이 1이면 결과는 단일 문자열이다(즉 함수는 아무 것도 하지 않는다); 만약 n이 0이면 결과는 비어 있는 문자열이다. 결합은 다음에 오는 루아의 일반 의미구조론에 의해 수행된다(2.5.4).

3.7 - Controlling Garbage Collection

루아는 그것의 가비지 콜렉션을 제어하기 위한 두 가지 숫자를 사용한다 : count 와 threshold이다(2.9를 참조하라). 카운트가 문턱값에 도달했을 때, 루아는 가비지 콜렉터를 실행한다. 수집이 끝난 다음에는 카운트를 갱신하고 문턱값은 카운트 값의 2배로 설정된다.
이 두 숫자의 현재값에 다음 함수를 사용해 접근할 수 있다 : int lua_getgccount (lua_State *L); int lua_getgcthreshold (lua_State *L);
두 함수 모드 자신의 상대값을 Kbytes로 반환한다. 문턱값을 다음 함수로 변경할 수도 있다. void lua_setgcthreshold (lua_State *L, int newthreshold);
다시 언급하지만 newthreshold 값은 KBytes단위이다. 이 함수를 호출할 때 루아는 새로운 문턱값을 설정하고, 그것을 byte 카운터와 비교한다. 만약 새로운 문턱값이 byte 카운터보다 작다면, 루아는 즉시 가비지 콜렉터를 실행한다. 특히 lua_setgcthreshold(L,0)는 가비지 콜렉션을 강제로 수행하게 한다. 콜렉션을 수행한 이후에는 새로운 문턱값이 이전의 규칙에 의거해 설정된다.

3.8 - Userdata

userdata는 루아에서의 C 값을 표현한다. 루아는 두 가지 유형의 userdata를 지원한다 : full userdatalight userdata이다.
full userdata는 메모리 블록을 의미한다. 그것은 (테이블과 같은) 개체이다 : 프로그래머는 반드시 그것을 생성해야 하며, 그것은 자신만의 메타테이블을 가질 수 있다. 프로그래머는 그것이 수집될 때를 검사할 수 있다. full userdata는 (raw equality 하에서) 오직 자신과만 일치(equal)한다.
light userdata는 포인터를 의미한다. 그것은 (숫자와 같은) 값이다 : 그것을 생성하지 않으며, 그것은 메타테이블도 가지고 있지 않다. 그것은 수집되지 않는다(왜냐하면 생성도 안 됐다). light userdata는 같은 C 어드레스를 가지고 있다면 어떤 light userdata와도 동일하다.
루아 코드에서 userdata가 full인지 light인지를 체크할 수 있는 방법은 없다; 두 가지 모두 userdata형이다. C 코드에서 lua_type 은 full userdata에 대해 LUA_TUSERDATA를 반환하고, light userdata에 대해LUA_TLIGHTUSERDATA를 반환한다.
다음 함수로 새로운 full userdata를 생성할 수 있다 : void *lua_newuserdata (lua_State *L, size_t size);
이 함수는 주어진 크기를 가진 새로운 메모리 블록을 할당하고, 블록 주소를 가진 새로운 userdata를 스택에 push하며, 이 주소를 반환한다.
light userdata를 스택에 push하기 위해서는 lua_pushlightuserdata를 사용할 수 있다(3.6를 참조하라).
lua_touserdata(3.5 참조)는 userdata의 값을 획득한다. full userdata 상에서 적용될 때 그것은 그 블록의 주소를 반환한다; light userdata 상에서 적용될 때 그것은 포인터를 반환한다; userdata가 아닌 값 상에서 적용될 때 그것은 NULL을 반환한다.
루아가 full userdata를 수집할 때, 만약 존재한다면 그것은 userdata의 gc 메타메서드를 호출한 후에 userdata의 관련 메모리를 해제한다.

3.9 - Metatables

다음은 오브젝트의 메타테이블을 조작하도록 허용하는 함수들이다 : int lua_getmetatable (lua_State *L, int index); int lua_setmetatable (lua_State *L, int index);
lua_getmetatable는 주어진 오브젝트의 메타테이블을 스택에 push한다. 만약 인덱스가 유효하지 않거나 오브젝트가 메타테이블을 가지고 있지 않다면, lua_getmetatable은 0을 반환하고 스택에 아무것도 push하지 않는다.
lua_setmetatable는 스택으로부터 테이블을 pop하며, 주어진 오브젝트에 대한 새로운 메타테이블로서 그것을 설정한다.lua_setmetatable는 주어진 오브젝트의 메타테이블을 설정할 수 없을 때 0을 반환한다(즉 오브젝트가 userdata나 테이블이 아닐 때); 그리고 나서 심지어는 스택으로부터 테이블을 pop한다.

3.10 - Loading Lua Chunks

lua_load를 사용해 루아 청크를 로드할 수 있다 : typedef const char * (*lua_Chunkreader) (lua_State *L, void *data, size_t *size); int lua_load (lua_State *L, lua_Chunkreader reader, void *data, const char *chunkname);
lua_load의 반환값은 다음과 같다:
  • 0 --- 에러 없음;
  • LUA_ERRSYNTAX --- 미리 컴파일 동안 구문 에러 발생.
  • LUA_ERRMEM --- 메모리 할당 에러.
만약 에러가 없다면 lua_load는 스택의 top에 컴파일된 청크를 루아 함수로서 push한다. 그렇지 않으면 에러 메시지를 push한다.
lua_load는 자동적으로 청크가 텍스트인지 바이너리인지 검사하며, 적절하게 그것을 로드한다(luac 프로그램을 참조하라).
lua_load는 사용자 제공 reader 함수를 사용해 청크를 읽어들인다. 청크의 또 다른 조각을 필요로 할 때마다lua_load는 reader를 호출하는데, 자신의 dataparameter를 함께 넘긴다. reader는 반드시 새로운 조각의 청크와 함께 메모리 블록에 대한 포인터를 넘겨야 하며, size를 블록의 크기로 설정해야 한다. 청크의 끝을 판단하기 위해서 reader는 NULL을 반환한다. reader 함수는 0보다는 큰 크기의 조각을 반환할 것이다.
현재의 구현에서 reader 함수는 루아 함수를 호출할 수 없다; 그것을 보장하기 위해서 그것은 항상 루아 state로서 NULL 을 받는다.
chunkname은 에러메시지와 디버그 정보를 위해 사용된다(4를 참조하라).
lua_load의 사용방법에 대한 예제와 파일과 문자열로부터 청크를 읽어들일 수 있도록 준비되어 있는 함수들에 대해 알고자 한다면 auxiliary(보조) 라이브러리 (lauxlib.c)를 참조하라.

3.11 - Manipulating Tables

테이블은 다음의 함수 호출에 의해 생성된다 void lua_newtable (lua_State *L);
이 함수는 새로운 비어있는 테이블을 생성하고 그것을 스택에 push한다.
스택의 어딘가에 존재하는 테이블로부터 값을 읽어들이고자 할 때는 다음 함수를 호출하라. void lua_gettable (lua_State *L, int index);
여기에서 index는 테이블을 가리킨다. lua_gettable은 스택으로부터 키를 pop하고 (스택에) 키에 해당하는 테이블의 콘텐츠를 반환한다. 테이블은 스택에 존재하던 위치 그대로 남아있게 된다. 왜냐하면 루아에서 이 함수는 “index” 이벤트를 위한 메타메서드를 트리거하기 때문에 (2.8 참조). 메타메서드에 대한 호출 없이 어떤 테이블 키의 실제 값을 획득하고자 한다면 raw 버전을 사용하라 : void lua_rawget (lua_State *L, int index);
값을 스택의 어딘가에 있는 테이블에 저장하고자 한다면, 키를 push하고 값을 스택에 push한 다음, 다음 함수를 호출하면 된다. void lua_settable (lua_State *L, int index);
index는 테이블을 가리킨다. lua_settable는 스택으로부터 키와 값을 모두 pop한다. 테이블은 스택에 있던 위치에 그대로 남아있게 된다. 왜냐하면 루아에서 이 연산은 “settable”이나 “newindex” 이벤트를 위해 메타메서드를 트리거하기 때문이다. 메타메서드의 호출 없이 테이블 인덱스의 실제 값을 설정하고자 한다면raw버전을 사용하라 : void lua_rawset (lua_State *L, int index);
다음 함수로 테이블을 검색할 수 있다 : int lua_next (lua_State *L, int index);
여기에서 index는 검색하고자 하는 테이블을 가리킨다. 이 함수는 스택에서 키를 pop하고, 테이블로부터의 키-값 쌍을 push한다. (주어진 키 이후의 “next” 쌍) 만약 더 이상 요소가 없다면 lua_next는 0을 반환한다(그리고 아무것도 push하지 않는다). 검색의 시작을 표시하기 위해 nil 키를 사용하라.
일반적인 검색은 다음과 같이 보인다 : /* table is in the stack at index `t' */ lua_pushnil(L); /* first key */ while (lua_next(L, t) != 0) { /* `key' is at index -2 and `value' at index -1 */ printf("%s - %s\n", lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1))); lua_pop(L, 1); /* removes `value'; keeps `key' for next iteration */ }
테이블을 검색하고 있는 동안 키가 실제로 문자열이라는 확신 없이는 직접적으로 키에 lua_tostring 를 호출하지 말라. 그 lua_tostring에 대한 재호출은 주어진 인덱스에 있는 값을 변경한다; 이것은 lua_next에 대한 다음 호출을 혼란스럽게 만든다.

3.12 - Manipulating Environments

모든 전역 변수들은 정규 루아 테이블에 유지되며 이는 environments라고 불린다. 초기의 environment는 전역 environment라고 불린다. 이 테이블은 항상 의사-인덱스 LUA_GLOBALSINDEX에 존재한다.
전역 변수의 값에 접근하거나 그것을 변경하고자 한다면, 정규 테이블 연산자를 environment 테이블에 사용할 수 있다. 예를 들어서 전역 변수의 값에 접근하고자 한다면 다음과 같이 하면 된다. lua_pushstring(L, varname); lua_gettable(L, LUA_GLOBALSINDEX);
lua_replace를 사용하여 루아 스레드의 전역 environment를 변경할 수 있다.
다음 함수들은 루아 함수의 environment를 획득하거나 설정한다 : void lua_getfenv (lua_State *L, int index); int lua_setfenv (lua_State *L, int index);
lua_getfenv는 스택에 스택안의 index 인덱스에 있는 함수의 environment 테이블을 push한다. 만약 함수가 C 함수라면 lua_getfenv는 전역 environment를 push한다. lua_setfenv는 스택으로부터 테이블을 pop하며 그것을 스택의 index 인덱스에 있는 함수를 위한 새로운 environment로 설정한다. 주어진 인덱스의 만약 오브젝트가 루아 함수가 아니라면 lua_setfenv는 0을 반환한다.

3.13 - Using Tables as Arrays

API는 루아테이블을 배열로서 사용할 수 있도록 도와주는 함수들을 소유하고 있다. 즉 테이블은 숫자만으로 색인화된다 : void lua_rawgeti (lua_State *L, int index, int n); void lua_rawseti (lua_State *L, int index, int n);
lua_rawgeti는 스택의 index 위치에 있는 테이블의 n번째 요소의 값을 push 한다. lua_rawseti는 스택의index 위치에 있는 테이블의 n번째 요소의 값을 스택의 top에 있는 값으로 설정하고, 이 값은 스택에서 제거한다.

3.14 - Calling Functions

루아에서 정의된 함수와 루아에 등록된 C 함수는 호스트 프로그램에서 호출될 수 있다. 이것은 다음의 규약을 사용함으로써 수행된다 : 먼저 호출될 함수가 스택에 push된다; 그리고 나서 그 함수의 인자들이 direct order로 push된다. 즉 첫 번째 인자가 첫 번째로 push된다는 것이다. 마지막으로 함수는 다음의 함수를 사용해 호출된다. void lua_call (lua_State *L, int nargs, int nresults);
nargs는 스택에 push한 인자의 개수이다. 모든 인자들과 함수 값이 스택으로부터 pop되며, 그 함수의 결과가 push된다. nresults가 LUA_MULTRET가 아닌 한, 결과의 개수는 nresults로 조정된다. 그러한 경우 함수로부터의 모든 결과가 push된다. 루아는 반환 값이 스택 공간에 들어 맞아야 한다는 것에 주의한다. 이 함수의 결과는 direct order로 스택에 push 된다 (첫 번째 결과가 첫 번째로 push된다). 그래서 호출 이후에 마지막 결과가 top에 존재하게 된다.
다음 예제는 호스트 프로그램이 이 루아 코드와 동일한 것을 수행하는 방식에 대해 보여 준다 : a = f("how", t.x, 14)
이것을 C에서는 이렇게 한다 : lua_pushstring(L, "t"); lua_gettable(L, LUA_GLOBALSINDEX); /* global `t' (for later use) */ lua_pushstring(L, "a"); /* var name */ lua_pushstring(L, "f"); /* function name */ lua_gettable(L, LUA_GLOBALSINDEX); /* function to be called */ lua_pushstring(L, "how"); /* 1st argument */ lua_pushstring(L, "x"); /* push the string "x" */ lua_gettable(L, -5); /* push result of t.x (2nd arg) */ lua_pushnumber(L, 14); /* 3rd argument */ lua_call(L, 3, 1); /* call function with 3 arguments and 1 result */ lua_settable(L, LUA_GLOBALSINDEX); /* set global variable `a' */ lua_pop(L, 1); /* remove `t' from the stack */
위의 코드는 “균형잡혀 있음”에 주목하라 : 그것의 마지막에서 스택은 원래의 구성으로 되돌아 간다. 이것은 좋은 프로그래밍 경험이라고 여겨진다.
(세부적인 부분을 보여주기 위해서 우리는 이 예제를 루아 API에 의해 제공되는 raw 함수들만을 사용해서 작성했다. 일반적으로 프로그래머들은 몇 몇 매크로를 정의/사용하거나 루아에 대한 고수준 접근을 제공하는 보조 라이브러리를 사용하면 된다. 예제로 표준 라이브러리의 소스 코드를 참조하라.)

3.15 - Protected Calls

lua_call을 사용해 함수를 호출 할 때, 호출된 함수 내부에 있는 에러는 위로 전파된다(longjmp해서..). 만약 에러를 다룰 필요가 있다면, lua_pcall을 사용하면 된다 : int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
nargs 와 nresults는 lua_call에서와 같은 의미이다. 만약 호출 동안 에러가 발생하지 않는다면lua_pcall은 lua_call와 정확하게 같은 행동을 취할 것이다. 그러나 에러가 발생한다면 lua_pcall은 그것을 잡아서, 스택에 단일 값(에러 메시지)을 push한다. lua_call과 마찬가지로 lua_pcall은 항상 함수와 그것의 인자를 스택에서 제거한다.
만약 errfunc이 0이라면 반환되는 에러 메시지는 정확한 원본 에러 메시지이다. 그렇지 않으면 errfuncerror handler function에 대한 스택 인덱스를 부여한다. (현재의 구현에서 그 인덱스는 의사-코드일 수 없다.) 실행시간 에러인 경우에 그 함수는 에러 메시지와 함께 호출될 것이며, 그것의 반환값은 lua_pcall에 의해 반환되는 메시지일 것이다.
일반적으로 에러 핸들러 함수는 스택 traceback과 같은 에러 메시지에 대하 디버그 정보를 더 많이 추가하는데 사용된다. 그러한 정보는 lua_pcall이 반환된 이후에는 수집할 수 없다. 왜냐하면 스택이 풀려버렸기 때문이다.
lua_pcall 함수는 성공시에는 0을 반환하고, 그렇지 않으면 다음 에러 코드들 중 하나를 반환한다(lua.h에 정의됨):
  • LUA_ERRRUN --- 실행시간 에러
  • LUA_ERRMEM --- 메모리 할당 에러. 그러한 에러들에 대해 루아는 에러 핸들러 함수를 호출하지 않는다.
  • LUA_ERRERR --- 에러 핸들러 함수가 실행되는 동안의 에러.

3.16 - Defining C Functions

루아는 C로 작성된 함수들을 사용해 확장될 수 있다. 이러한 함수들은 반드시 lua_CFunction 유형이어야 하며 이는 다음과 같이 정의되어 있다. typedef int (*lua_CFunction) (lua_State *L);
C 함수는 루아 state를 받고 정수를 반환하는데, 그것은 루아에 반환되기 원하는 숫자 값이다.
루아와 적절히 상호작용하기 위해서 C함수는 반드시 다음 규약을 따라야 하며, 이는 인자와 반환값이 넘겨지는 방식을 정의한다 : C 함수는 그것의 인자를 루아로부터 받아들이는데, 스택에 있는 것들을 direct order(첫 번째 인자가 처음 push됨)로 받아들인다. 그래서 함수가 시작되면, 그것의 첫 번째 인자가 (만약 존재한다면) 인덱스 1에 존재하게 된다. 루아에 값을 반환하기 위해서 C 함수는 단지 그 값을 스택에 direct order로 push하기만 하면 된다. 그리고 결과를 반환하면 된다. 스택에서 결과값 밑에 있는 값들은 루아에 의해 적절히 해제된다. 루아 함수와 마찬가지로 루아에 의해 호출되는 C함수도 많은 결과를 반환할 수 있다.
예를 들어 아래의 함수는 다양한 개수의 수치 인자를 받아들이고 그것들의 평균과 합계를 반환한다 : static int foo (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ lua_Number sum = 0; int i; for (i = 1; i <= n; i++) { if (!lua_isnumber(L, i)) { lua_pushstring(L, "incorrect argument to function `average'"); lua_error(L); } sum += lua_tonumber(L, i); } lua_pushnumber(L, sum/n); /* first result */ lua_pushnumber(L, sum); /* second result */ return 2; /* number of results */ }
C 함수를 루아에 등록하기 위해서 다음과 같은 편리한 매크로가 있다 : #define lua_register(L,n,f) \ (lua_pushstring(L, n), \ lua_pushcfunction(L, f), \ lua_settable(L, LUA_GLOBALSINDEX)) /* lua_State *L; */ /* const char *n; */ /* lua_CFunction f; */
이 함수는 루아에서 포함하게 될 함수의 이름과 함수에 대한 포인터를 받아들인다. 결국 위의 C 함수 foo는 아래의 매크로를 호출함으로써 루아에 average라고 등록될 것이다. lua_register(L, "average", foo);

3.17 - Defining C Closures

C 함수가 생성될 때 어떤 값들을 그것과 연관시키는 것이 가능하다. 즉 C closure를 생성하는 것이다; 이러한 값들은 함수가 호출될 때마다 함수에 대해 접근가능하다. 값을 C 함수와 연관시키기 위해서는, 먼저 이러한 값들은 스택에 push되어야만 한다(다중 값이 존재한다면 첫 번째 값이 첫 번째로 push된다). 그리고 나서 다음 함수 void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
를 사용해 C 함수를 스택에 push한다. 이 때 인자 n는 몇 개의 값들이 이 함수와 연관될 것인지를 말해 준다. (또한 lua_puahcclosure는 이 값들을 스택으로부터 pop한다) 사실 lua_pushcfunctionlua_pushcclosure의 인자 n이 0으로 설정된 것과 같다.
그리고 나서 C 함수가 호출될 때마다, 이러한 값들은 지정된 의사-인덱스에 놓이게 된다. 이러한 의사 인덱스들은 lua_upvalueindex에 의해 생성된다. 함수와 관련된 첫 번째 값은 lua_upvalueindex(1)에 위치하는 식이다. n이 현재 함수의 upvalues의 개수보다 클 때 lua_upvalueindex(n)에 대한 접근은 접근가능한(그렇지만 유효하지 않은) 인덱스를 생성한다.
C 함수와 closure에 대한 예를 보려면 공식 루아 배포판에 있는 표준 라이브러리를 참조하라(src/lib/*.c).

3.18 - Registry

루아 값을 저장될 필요가 있는 곳에 저장하기 위해서, 특히 C 코드가 루아 값을 C 함수의 생명 주기 바깥에서도 유지하고자 할 때 C 코드에 의해 사용될 수 있도록, 루아는 레지스트리와 기정의된 테이블을 제공한다. 이 테이블은 항상 의사-인덱스 LUA_REGISTRYINDEX에 위치한다. 모든 루아 라이브러리는 데이터를 이 테이블에 저장할 수 있으며, 동시에 그것은 다른 라이브러리와는 다른 키를 선택한다. 일반적으로 프로그래머는 키를 라이브러리 이름을 포함하는 문자열이나 이나 코드 내 C 오브젝트의 주소를 가지고 있는 light userdata로서 키를 사용하게 된다.
레지스트리의 integer 키는 참조 메카니즘에 의해 사용되며, 보조(auxiliary) 라이브러리에 의해 구현된다. 그리고 결국 그것은 다른 목적으로 사용될 수 없다.

3.19 - Error Handling in C

내부적으로 루아는 C longjmp 기능을 사용해 에러를 다룬다. 루아가 (메모리 할당 에러나, 형 에러, 구문 에러와 같은) 에러를 만날 때, 그것은 에러를 전파(raise, 역주 : 영영사전에는 ‘raise한다고 하면 그것을 움직여서 높은 위치로 바꾼다는 의미’라고 함)한다. 즉 long jump를 수행한다. 보호된 environment는 setjmp를 사용해 복구 지점을 설정한다; 에러는 가장 가까운 활성 복구 지점으로 이동한다.
만약 에러가 보호된 environment 바깥에서 발생하면, 루아는 panic 함수 를 호출한 다음exit(EXIT_FAILURE)를 호출한다. 프로그래머는 panic 함수를 다음과 같이 변경할 수 있다. lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);
새로운 panic 함수는 응용프로그램이 반환하지 않고 프로그램을 종료하는 것(예를 들어 long jump를 수행하는 것)을 방지해야 한다. 그럼에도 불구하고 관련 루아 state는 일관되게 유지되지 못할 것이다; 유일한 안전한 연산은 그것을 닫는것이다. 한 개의 요소만을 가지고 시작한다. 에러가 발생하는 경우 lua_cpcalllua_pcall와 같은 에러 코드를 반환하며(3.15을 참조하라), 스택의 top에 에러 개체를 추가한다; 그렇지 않으면 그것은 0을 반환하고, 스택을 변경하지 않는다. func에 의해 반환되는 값은 버려진다.
C 코드는 다음 함수를 호출함으로써 루아 에러를 생성한다. void lua_error (lua_State *L);
(실제적으로 어떤 유형의 개체든지 가능한) 에러 메시지는 반드시 스택 top에 위치해야만 한다. 이 함수는 long jump를 수행하며 결국 결코 반환되지 않는다.

3.20 - Threads

루아는 싱행중 다중 스레드에 대한 부분적인 지원을 제공한다. 만약 다중 스레드를 제공하는 C 라이브러리를 가지고 잇다면, 루아는 루아의 동등한 기능을 구현함으로써 그것과 상호작용할 수 있다. 또한 루아는 자체의 coroutine 시스템을 스레드의 상위에 구현한다. 다음 함수는 루아에서 새로운 스레드를 생성한다 : lua_State *lua_newthread (lua_State *L);
이 함수는 스레드를 스택에 push하고 이 새로운 스레드를 의미하는 lua_State에 대한 포인터를 반환한다. 이 함수에 의해 반환되는 새로운 state는 (테이블과 같은) 원래의 state와 함께 모든 전역 개체를 공유한다. 그러나 독립적인 실시간 스택을 소유한다.
스레드를 닫거나 파괴하는 명시적인 함수는 없다. 스레드는 다른 오브젝트들처럼 가비지 콜렉션의 지배를 받는다.
스레드를 coroutine처럼 조작하기 위해서, 루아는 다음과 같은 함수들을 제공한다 : int lua_resume (lua_State *L, int narg); int lua_yield (lua_State *L, int nresults);
corutine을 시작하기 위해서, 먼저 새로운 스레드를 생성한다 : 그리고 나서 함수와 함께 최종 인자를 스택에 push한다; 그리고 나서 lua_resume를 인자의 개수를 의미하는 narg와 함게 호출한다. 이 호출은 coroutine이 그것의 실행에 의해 정지하거나 끝날 때 반환된다. 그것이 반환될 때 스택은 lua_yield에 정달된 모든 값이나 몸체 함수에 의해 반환되는 모든 값을 포함한다. lua_resume은 coroutine의 실행 중 에러가 발생하지 않았을 때 0을 반환하며, 그렇지 않을 경우 에러 코드를 반환한다(3.15를 참조하라). 에러의 경우에 스택은 단지 에러 메시지만을 포함한다. coroutine을 재시작하기 위해서는 그것의 스택에 yield의 결과로서 전달될 값만을 넣으면 된다. 그리고 나서 lua_resume을 호출하라.
lua_yield함수는 단지 C 함수의 반환 표현식으로서만 호출될 수 있을 뿐이다, 다음을 참조하라: return lua_yield (L, nresults);
C 함수가 그런 식으로 lua_yield를 호출할 때, 실행중인 coroutine이 그것의 실행을 중지하고, 이 coroutine을 시작한 lua_resume에 대한 호출이 반환된다. nresults 인자는 스택으로부터 lua_resume에 결과로서 넘겨지는 값의 개수를 의미한다.
다른 스레드 간에 값을 교환하기 위해서 lua_xmove를 사용할 수 있다 : void lua_xmove (lua_State *from, lua_State *to, int n);
이 함수는 스택 from으로부터 n 값들을 pop하고, 스택 to에 그것들을 push한다.

4 - The Debug Interface

루아는 내장된 디버깅 기능을 가지고 있지 않다. 대신에 그것은 함수와 hooks에 의한 특별한 인터페이스를 제공한다. 이 인터페이스는 서로 다른 종류의 디버거, 프로파일러, 인터프리터로부터 “내부 정보”를 필요로 하는 툴들을 생성하는 것을 허용한다.

4.1 - Stack and Function Information

인터프리터 실시간 스택에 대한 정보를 획득하는 주요 함수는 다음과 같다. int lua_getstack (lua_State *L, int level, lua_Debug *ar);
이 함수는 lua_Debug 구조체의 일부를 주어진 레벨에서 실행중인 함수의 활성 레코드(activation record)에 대한 확인(identification)으로 채운다. 0 레벨은 현재 실행 중인 함수를 의미하지만, n+1레벨은 레벨 n을 호출했던 함수이다. 에러가 없다면 lua_getstack은 1을 반환한다; 스택 깊이보다 큰 레벨과 함께 호출되면 0을 반환한다.
lua_Debug 구조체는 활성 함수에 대한 서로 다른 정보의 조각들을 저장하기 위해 사용된다 : typedef struct lua_Debug { int event; const char *name; /* (n) */ const char *namewhat; /* (n) `global', `local', `field', `method' */ const char *what; /* (S) `Lua' function, `C' function, Lua `main' */ const char *source; /* (S) */ int currentline; /* (l) */ int nups; /* (u) number of upvalues */ int linedefined; /* (S) */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ ... } lua_Debug;
lua_getstack은 나중에 사용하기 위해 이 구조체의 private 부분만을 채운다. lua_Debug의 다른 필드를 유용한 정보로 채우고자 한다면 아래의 함수를 호출하라. int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
이 함수는 에러(예를 들어 what의 옵션이 유효하지 않을 때)가 발생했을 경우 0을 반환한다. what 문자열의 각 문자는 채워져야 할 ar 구조체의 특정 필드를 선택하는데, 위의 lua_Debug 정의에서 괄호로 묶여진 문자들에 의해 식별된다 : `S´는 source 필드, linedefined필드,what필드를 채운다; `l´는 currentline필드 등을 채운다; `f´는 스택에 주어진 레벨에서 실행중인 함수를 push한다.
활성화되지 않은 함수(즉, 스택에 없는)에 대한 정보를 획득하고자 한다면, 그것을 스택에 push하고 `>´와 함께 what 문자열을 시작한다; 예를 들어 f가 정의된 라인이 어디인지 알고자 한다면, 다음과 같이 작성할 수 있다. lua_Debug ar; lua_pushstring(L, "f"); lua_gettable(L, LUA_GLOBALSINDEX); /* get global `f' */ lua_getinfo(L, ">S", &ar); printf("%d\n", ar.linedefined);
lua_Debug의 필드는 다음과 같은 의미를 가지고 있다 :
  • source 만약 함수가 문자열로 정의되었다면, source는 그 문자열이다. 만약 함수가 파일에서 정의되었다면, source는 `@´로 시작해서 파일 명이 붙는다.
  • short_src 에러 메시지에 사용될 “출력 가능한” 버전의 source.
  • linedefined 함수 정의가 시작되는 라인 번호.
  • what 만일 이것이 루아 함수라면 "Lua"라는 문자열, C 함수라면 "C"라는 문자열, 청크의 메인 파트라면 "main"이라는 문자열, tail 호출을 실행하는 함수였다면 "tail"이라는 문자열이다. 후자의 경우 루아는 이 함수에 대한 다른 정보를 보유하지 않는다.
  • currentline 주어진 함수가 실행중인 현재 라인. 어떠한 라인 정보도 이용할 수 없다면,currentline은 -1로 설정된다.
  • name 주어진 함수에 대한 합리적인 이름. 루아에서의 함수는 최상위 클래스 값이기 때문에, 그것들은 고정된 이름을 가지지 않는다 : 어떤 함수들은 다중 전역 변수의 값일 것이며, 반면에 어떤 함수들은 테이블 필드에만 저장될 것이다. lua_getinfo 함수는 함수가 호출되는 방식이나 그것이 전역변수인지 여부를 검사하여 적절한 이름을 찾아 낸다. 이름을 찾지 못한다면 name은 NULL로 설정된다.
  • namewhat name을 설명한다. namewhat의 값은 "global""local""method""field""" (공백 문자열)일 수 있다. 이 값들은 함수가 호출되는 방식과 관련이 있다. (루아는 적용될만한 다른 옵션이 없을 때 공백 문자열을 사용한다.)
  • nups 함수의 upvalue 개수.

4.2 - Manipulating Local Variables and Upvalues

지역 변수와 upvalue를 조작하기 위해서 디버그 인터페이스는 인덱스를 사용한다 : 첫 번째 인자나 지역 변수는 인덱스 1인 식이다. upvalue는 특별한 순서를 가지고 있지 않다. 왜냐하면 그것들은 전체 함수에 걸쳐 활성화되어 있기 때문이다.
다음 함수는 주어진 활성 레코드의 지역 변수에 대한 조작을 허용한다 : const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
인자 ar은 lua_getstack 스택에 대한 이전 호출에 의해 채워지거나 hook(4.3 참조)에 대한 인자로서 주어진 유효한 활성 레코드여야 한다. lua_getlocal은 지역 변수의 인덱스 n을 받아, 변수의 값을 스택에 push하며, 그것의 이름을 반환한다. lua_setlocal은 스택의 top에 있는 값에 변수를 할당하며, 그것의 이름을 반환한다. 두 함수 모두 인덱스가 실제 지역 변수의 개수보다 클 때 NULL을 반환한다.
다음 함수는 주어진 함수의 upvalue에 대한 조작을 허용한다 (지역 변수와는 달리 함수의 upvalue는 함수가 활성화되어 있지 않아도 접근 가능하다): const char *lua_getupvalue (lua_State *L, int funcindex, int n); const char *lua_setupvalue (lua_State *L, int funcindex, int n);
이러한 함수들은 루아 함수나 C 함수 상에서 모두 작동한다. (루아 함수에 대해 upvalue는 함수가 사용하는 외부 지역 변수이며 그것의 closure의 항상 포함되는 것이다.) funcindex는 스택에 있는 함수를 가리킨다.lua_getupvalue는 upvalue의 인덱스 n을 획득하고 그 upvalue의 값을 스택에 push한다.lua_setupvalueNULL을 반환한다. C함수에 대해 이들 함수는 모든 upvalue의 이름으로서 공백 문자열 ""를 사용한다.
예를 들어 다음 함수는 주어진 레벨의 스택에 있는 함수에 대한 지역 변수와 upvalue의 이름을 열거한다 : int listvars (lua_State *L, int level) { lua_Debug ar; int i; const char *name; if (lua_getstack(L, level, &ar) == 0) return 0; /* failure: no such level in the stack */ i = 1; while ((name = lua_getlocal(L, &ar, i++)) != NULL) { printf("local %d %s\n", i-1, name); lua_pop(L, 1); /* remove variable value */ } lua_getinfo(L, "f", &ar); /* retrieves function */ i = 1; while ((name = lua_getupvalue(L, -1, i++)) != NULL) { printf("upvalue %d %s\n", i-1, name); lua_pop(L, 1); /* remove upvalue value */ } return 1; }

4.3 - Hooks

루아는 hook 메커티즘을 제공하는데, 프로그램 실행 동안 호출되는 이는 사용자 정의 C함수이다. hook은 네 가지 이벤트에서 호출된다 : 루아가 함수를 호출할 때 call이벤트; 루아가 함수로부터 반환할 때 return이벤트; 루아가 코드의 새로운 라인을 실행하기 시작할 때 line 이벤트; 모든 “count” 명령이 발생할 때 count이벤트. 루아는 이러한 이벤트들을 다음과 같은 상수로 식별한다 : LUA_HOOKCALLLUA_HOOKRET (아래에서 보듯이 LUA_HOOKTAILRET 일 수도 있음), LUA_HOOKLINE, and LUA_HOOKCOUNT.
hook은 lua_Hook 유형을 가지는데, 아래와 같이 정의되어 있다 : typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
다음 함수를 사용해 hook을 설정할 수 있다 : int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
func는 hook이다. mask는 hook이 호출될 이벤트가 무엇인지 지정한다 : 그것은 다음 상수의 선언에 의해 형성된다. LUA_MASKCALLLUA_MASKRET,LUA_MASKLINELUA_MASKCOUNTcount인자는 단지 mask가LUA_MASKCOUNT를 포함하고 있을 때만 의미가 있다. 각 이벤트에 대해 hook은 아래에서 설명되는 것과 같이 호출된다 :
  • call hook은 인터프리터가 함수를 호출할 때 호출된다. 이 훅은 루아가 새로운 함수로 진입한 이후에 바로 호출된다.
  • return hook은 인터프리터가 함수로부터 반환할 때 호출된다. 이 훅은 함수를 떠나기 바로 전에 호출된다.
  • line hook은 인터프리터가 코드의 새로운 라인을 실행하기 시작할 때 호출되거나, (같은 라인이더라도) 코드로 다시 되돌아 갈 때 호출된다. (이 이벤트는 루아가 루아 함수를 실행하고 있는 동안에만 발생한다.)
  • count hook은 인터프리터가 모든 count 명령을 실행하고 있을 때 호출된다. (이 이벤트는 루아가 루아 함수를 실행하고 있는 동안에만 발생한다.)
mask를 0으로 설정함으로써 hook을 불가능하게 한다.
다음 함수를 사용해 현재 hook, 현재 mask, 현재 count를 획득할 수 있다 : lua_Hook lua_gethook (lua_State *L); int lua_gethookmask (lua_State *L); int lua_gethookcount (lua_State *L);
hook이 호출될 때마다, 그것의 ar 인자는 그것의 event 필드를 소유하는데, 그 필드는 hook을 트리거한 지정된 이벤트로 설정되어 있다. 특히 라인 이벤트에 대해서는 currentline 필드도 같이 설정된다. ar에 있는 다른 필드의 값을 획득하고자 한다면, hook은 lua_getinfo를 호출해야만 한다. 반환값에 대해서 eventLUA_HOOKRET, 일반적인 값, 또는 LUA_HOOKTAILRET일 것이다. 마지막 값의 경우에 루아는 tail 호출을 수행하는 함수로부터 반환을 시뮬레이션할 것이다; 이 경우 lua_getinfo 호출은 무의미하다.
루아가 hook을 실행하고 있는 동안에, hook에 대한 다른 호출은 불가능하다. 결국 만약 hook이 함수나 청크를 실행하기 위해 루아를 콜백한다면(calls back Lua), 그 실행은 hook에 대한 어떠한 호출도 없이 수행될 것이다.

댓글 없음: