'평범한 한글' 프로그램은 한글과 그 외 문자로 이루어진 문자열입니다. 여기서 한글은 다음의 범위에 들어가는 유니코드 문자를 말합니다.
- U+1100-U+11FF (한글 자모)
- U+302E-U+302F (방점)
- U+3131-U+318E (호환용 한글 자모)
- U+A960-U+A97C (한글 자모 확장 A)
- U+AC00-U+D7AF (한글 음절)
- U+D7B0-U+D7C6, U+D7CB-U+D7FB (한글 자모 확장 B)
- U+FFA1-U+FFBE, U+FFC2-U+FFC7, U+FFCA-U+FFCF, U+FFD2-U+FFD7, U+FFDA-U+FFDC (반각 한글 자모)
한글은 다시 초성(호환용 한글 자모와 반각 한글 자모는 자음) 및 그 외 한글로 구분해, 초성이 아닌 한글은 삭제하고 초성은 해당하는 호환용 한글 자모로 변환합니다. 한글 음절은 초성만 추출하여 변환합니다. 편의를 위해 본 명세에서 한글은 호환용 한글 자모 중 자음이 대표합니다. 변환 예시는 다음과 같습니다.
동해물과 백두산이
=ㄷㅎㅁㄱ ㅂㄷㅅㅇ
나랏〮말〯ᄊᆞ미〮 듕귁〮에〮달아〮
=ㄴㄹㅁㅆㅁ ㄷㄱㅇㄷㅇ
한편 한글이 아닌 문자는 달리 구분하지 않고 공백과 같이 생각합니다. 따라서 본 명세에도 한글이 아닌 문자는 공백이 대표합니다.
거센소리와 된소리는 예사소리와 같게 취급하며, 겹자음은 자음 여러 개로 분리하여 생각합니다. 이는 작문 시 단어 선택의 폭을 넓히기 위한 선택입니다. 예시로, 다음 단어쌍들은 동일합니다.
ㄱㄲㅅㄹ
=ㄱㄱㅅㄹ
ㅇㅊㅂㅌ
=ㅇㅈㅂㄷ
ㄳㅎㄴㄷ
=ㄱㅅㅎㄴㄷ
간결한 설명을 위해 예사소리 단자음 10개 ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅎ
만 사용하여 설명하겠습니다. 또한, 모든 ㅇ
과 ㅎ
은 그 앞에 공백이 있는 것과 없는 것을 같게 봅니다. 따라서 편의를 위해 모든 ㅇ
과 ㅎ
앞에 미리 공백을 추가했다고 가정하겠습니다.
ㅇ
과 ㅎ
을 제외한 8개 자음 ㄱㄴㄷㄹㅁㅂㅅㅈ
은 8진수 가변 길이 정수를 부호화하기 위한 목적으로만 사용됩니다.
- 자음
ㄱ, ㄴ, ㄷ, ㄹ, ㅁ, ㅂ, ㅅ, ㅈ
은 각각 8진수 숫자 0, 1, 2, 3, 4, 5, 6, 7을 뜻합니다. - 가장 낮은 자리수가 가장 먼저 옵니다. 즉, 역순으로 부호화합니다. 예를 들어, 8진수 210은
ㄱㄴㄷ
로 적습니다. - 자음 개수가 홀수면 양수를, 자음 개수가 짝수면 음수를 뜻합니다. 뒤에
ㄱ
(0)을 덧붙이는 방법으로 원하는 부호를 지정할 수 있습니다.
예시는 다음과 같습니다.
ㄱ
(oct +0) = 0ㄴ
(oct +1) = 1ㄴㄱ
(oct -01) = -1ㄴㄱㄱ
(oct +001) = 1ㄱㄴ
(oct -10) = -8ㄱㄴㄱ
(oct +010) = +8ㄱㄱㄴ
(oct +100) = +64ㄱㄱㄴㄱ
(oct -0100) = -64
단어란 공백을 포함하지 않는 가장 긴 연속한 한글열을 말합니다. 예를 들어 프로그램 ㄱ ㄴㄷ ㄱ ㅎㄷ
은 단어 네 개 ㄱ
, ㄴㄷ
, ㄱ
, ㅎㄷ
으로 이루어져 있습니다.
객체는 프로그램 실행의 기본 단위입니다. 객체는 ㄱㄴㄷㄹㅁㅂㅅㅈ
으로만 단어를 구성한 정수 리터럴이거나, 함수 만들기 혹은 함수 호출로 만들어진 것이며, 함수의 인수로 사용할 수 있습니다. 객체의 자료형은 다음 열두 가지입니다.
- 정수
- 실수
- 복소수
- 논릿값
- 문자열
- 바이트열
- 목록
- 사전
- 함수
- 드나듦
- 예외
- 빈값
모든 함수는 0개 이상의 객체를 인수로 받아 객체 하나를 돌려줍니다. 함수를 만들 때 인수를 몇 개 받을지는 따로 표기하지 않습니다. 함수를 만드는 표현식은 다음과 같이 함수 몸통이 되는 객체 하나와 단어 ㅎ
으로 이루어지며, 표현식 전체가 함수 객체가 됩니다.
<함수 몸통> ㅎ
예를 들어 인수에 상관 없이 정수 3을 돌려주는 함수는 ㄹ ㅎ
으로 만듭니다.
인수 n개를 써서 함수를 호출하는 표현식은 다음과 같이, (n+1)개의 객체에 이어 ㅎ
뒤에 인수 개수 n을 부호화한 정수 리터럴을 붙인 단어 1개로 구성됩니다. 마지막 객체가 호출할 함수이고, 첫 n개의 객체가 이 함수에 인수로 전달됩니다.
<인수0> <인수1> <인수2> ... <인수n-1> <함수> ㅎ<정수 리터럴 n>
인수에 상관 없이 정수 3을 돌려주는 함수에 두 개의 인수 1과 -1을 전달하여 부르는 표현식은 다음과 같습니다.
ㄴ ㄴㄱ ㄹ ㅎ ㅎㄷ
본래 호출할 함수는 함수 객체여야 하지만, 정수 리터럴에 한해 정수 객체를 허용하며, 복소수, 논릿값, 사전, 목록, 문자열 및 바이트열 객체도 허용합니다. 자세한 내용은 기본 제공 함수 문서를 참조해주세요.
함수를 만들 때 만들려는 함수와 둘러싼 함수를 함수 몸통에서 쓸 수 있습니다. 만들려는 함수를 0번째라고 하고, k번째 함수를 바로 둘러싼 함수를 (k+1)번째라고 합시다. m번째 함수를 쓰려면 다음과 같이 합니다.
<정수 리터럴 m> ㅇ
예를 들어 1번째 함수를 쓰려면 다음과 같이 합니다.
ㄴ ㅇ
m이 음수인 경우 바깥쪽에서 (-m-1)번째 함수를 씁니다. 참고로 이 번호는 함수 객체 안에 저장되어 함수 객체를 어느 문맥에서 부르더라도 일관된 행동을 보장합니다.
함수를 만들 때 만들려는 함수와 둘러싼 함수에 전달되는 인수를 사용할 수 있습니다. 앞서와 같이 함수에 번호를 매겨서, m번째 함수의 n번째 인수를 가져오는 표현식은 다음과 같습니다. m이 음수인 경우 바깥쪽에서 (-m-1)번째 함수의 n번째 인수를 가져옵니다. 다만 n이 음수인 경우는 금지됩니다.
<정수 리터럴 n> ㅇ<정수 리터럴 m>
예를 들어 자기가 받은 0번째 인수를 내놓는 함수는 다음과 같습니다.
ㄱ ㅇㄱ ㅎ
또, 자기가 받은 0번째 인수와 1번째 인수를 더해 내놓는 함수는 다음과 같습니다.
ㄱ ㅇㄱ ㄴ ㅇㄱ ㄷ ㅎㄷ ㅎ
한편 함수 λx.λy.(x+y)는 다음과 같이 작성할 수 있습니다.
ㄱ ㅇㄴ ㄱ ㅇㄱ ㄷ ㅎㄷ ㅎ ㅎ
이 함수에 차례로 3과 4를 인수로 넣어 호출하는 표현식은 다음과 같습니다.
ㄹ ㅁ ㄱ ㅇㄴ ㄱ ㅇㄱ ㄷ ㅎㄷ ㅎ ㅎ ㅎㄴ ㅎㄴ
몇 번째 인수를 가져올지 미리 정해지지 않으면 동적으로 값을 평가해 그 값으로 가져올 수 있습니다. 다음과 같이 정수 객체 하나와 ㅇ
으로 시작하는 단어 하나로 이뤄진 표현식을 생각합시다. 정수 객체의 값이 n이면, 다음 표현식은 m번째 함수의 n번째 인수를 가져옵니다.
<정수 객체 n> ㅇ<정수 리터럴 m>
예를 들어 ㄱ ㅇㄱ ㅇㄱ ㅎ
은 다음 의사 코드로 표현할 수 있습니다.
def func(*argv):
return argv[argv[0]]
또 ㄱ ㅇㄱ ㄴ ㄷ ㅎㄷ ㅇㄱ ㅎ
은 다음 의사 코드로 표현할 수 있습니다.
def func(*argv):
return argv[argv[0] + 1]
'평범한 한글'의 드나듦 객체는 하스켈의 IO 모나드(Monad)에서 따왔습니다. 이것은 부작용이 있는 연산을 격리하고 실행 순서를 명확하게 하기 위한 목적입니다. 이를 위해 입력받은 값을 바로 활용하는 것은 금지되고, 대신 그 값을 어떻게 활용할지를 미리 계획합니다. 표현식을 전부 평가했을 때 드나듦 객체가 나오면, 인터프리터는 그때부터 드나듦 객체에 적힌 계획에 따라 입출력 연산을 실행하게 됩니다.
먼저, 기본 제공 함수 ㄹ
을 부르면 표준 입력에서 문자열을 받아오는 계획이 담긴 드나듦 객체를 얻습니다. 이것과 함께 나중에 그 값으로 무엇을 할지 계획한 함수로 기본 제공 함수 ㄱㄹ
을 불러 새 드나듦 객체를 만듭니다. 이때 마지막 인수인 함수는 드나듦 객체를 내놓아야 합니다.
예를 들어, 표준 입력에서 문자열을 받아 그대로 표준 출력으로 보내는 계획을 담은 드나듦 객체는 다음과 같습니다.
ㄹ ㅎㄱ ㄱ ㅇㄱ ㅈㄹ ㅎㄴ ㅎ ㄱㄹ ㅎㄷ
여기서 ㄹ ㅎㄱ
은 앞서 언급했듯 문자열을 입력받는 계획이 담긴 드나듦 객체입니다. 한편 ㅈㄹ ㅎㄴ
은 인수를 출력하는 계획이 담긴 드나듦 객체입니다. 따라서 ㄱ ㅇㄱ ㅈㄹ ㅎㄴ ㅎ
은 인수를 받아서 그것을 출력할 드나듦 객체를 만드는 함수입니다. 기본 제공 함수 ㄱㄹ
은 드나듦 객체와 드나듦 객체를 만들 함수를 하나씩 인수로 받아 새 드나듦 객체를 만듭니다. 인터프리터는 이 드나듦 객체를 만나면 첫번째 인수를 실행해 값을 얻고, 이것으로 두번째 인수를 부릅니다. 그 결과로 인터프리터는 표준 출력을 하는 드나듦 객체를 만나, 출력을 수행하고 끝냅니다.
다른 예로, 입력값을 받아 실수로 변환하는 프로그램은 다음과 같습니다. 여기서 기본 제공 함수 ㄱㅅ
은 나중에 실행되면 인수로 받은 값을 그대로 돌려주는 드나듦을 만듭니다.
ㄹ ㅎㄱ ㅅㅅ ㄱㅅ ㄴㄱ ㅎㄷ ㄱㄹ ㅎㄷ
여기서 함수 ㅅㅅ ㄱㅅ ㄴㄱ ㅎㄷ
은 문자열을 실수로 변환해 드나듦 객체로 감싸 내놓는 함수입니다. 기본 제공 함수 ㄱㄹ
은 ㄹ
을 불러서 받은 드나듦 객체와 이 함수를 묶어 새 드나듦 객체를 만듭니다. 이후에 이 드나듦 객체가 실행되면 먼저 드나듦 ㄹ ㅎㄱ
을 실행해 값을 받은 후 이것을 인수로 위 함수를 불러 최종 드나듦을 받게 됩니다. 이것까지 실행하면 원하는 실수값이 나옵니다.