Skip to content
N0067HPublic

About

A small stack-based esolang that uses Usagi’s sounds from Chiikawa

Topics

Resources

Stars

31 stars

Watchers

0 watching

Forks

Repository files navigation

Yahalang

치이카와의 우사기

English / 한국어 / 日本語

Yahalang은 치이카와 우사기의 랩 후렴에서 문법을 따온 난해한 프로그래밍 언어입니다.
한국어, 영어, 일본어 표기로 코드를 작성합니다. 값을 스택에 쌓아 계산하며, 부호 있는 64비트 정수와 정수 배열을 다룹니다.

자세한 문법은 DESIGN.md에 있습니다.

실행

cargo build
cargo run -- run examples/hello_world.yaha --lang ko
cargo run -- run examples/countdown.yaha --lang ko

첫 번째 예제는 Hello, world!와 줄바꿈을, 두 번째 예제는 321을 출력합니다.
예제 파일은 모두 한국어 표기로 작성했으므로 --lang ko 옵션을 붙여 실행합니다.

cargo run -- repl --lang ko
yaha> 하야하 뿌루뿌루 야하 이이이야하 뿌루루
2
yaha> :q

REPL은 입력 사이에 스택과 배열을 유지합니다. 블록이나 숫자가 닫히지 않으면 ...>에서 계속 입력받고, 코드가 완성되면 실행합니다.
:stack은 스택을 아래에서 위 순서로 표시하며 배열은 현재 원소를 표시합니다. :reset은 스택, 배열과 입력 중인 코드를 지웁니다.
:q나 :quit은 종료합니다. 입력 중인 코드가 있다면 버립니다. 입력 종료(EOF) 시 미완성 코드는 오류로 표시합니다.
구문 오류가 나면 입력 중인 코드만 버립니다. 실행 오류가 나면 그 시점의 스택과 배열 변경은 남습니다. 얏은 현재 코드 실행만 끝내며 REPL 상태를 지우지 않습니다.
파일로 실행하면 파일 전체가 하나의 프로그램입니다. 줄이 바뀌어도 스택과 배열의 값은 유지됩니다.\

숫자

숫자는 하야하로 시작하고 야하로 끝납니다. 그 사이에 넣은 뿌루의 개수가 숫자 값입니다.

코드 값
하야하 야하 0
하야하 뿌루 야하 1
하야하 뿌루뿌루 야하 2

숫자 안에는 공백과 줄바꿈을 넣어도 됩니다. 숫자 뒤에 우라 얏을 붙이면 부호가 바뀝니다.

다음 코드는 2와 3을 더해 5를 출력합니다.

하야하 뿌루뿌루 야하
하야하 뿌루뿌루뿌루 야하
야하
이이이야하 뿌루루
얏

명령과 구절

한국어 영어 일본어 동작
하야하 … 야하 hayaha … yaha ハヤハ … ヤハ 뿌루 개수로 정수 생성
야하 yaha ヤハ 덧셈
우라 야하 ura yaha ウラ ヤハ 뺄셈
핫 hat ハッ 곱셈
우라 핫 ura hat ウラ ハッ 정수 나눗셈
우라 핫핫 ura hathat ウラ ハッハッ 나머지
하아 핫 haa hat ハア ハッ 두 정수가 같으면 1, 아니면 0
하아 이야 haa iya ハア イヤ 아래 정수가 위 정수보다 작으면 1, 아니면 0
하아 야하 haa yaha ハア ヤハ 아래 정수가 위 정수보다 크면 1, 아니면 0
우라 얏 ura yat ウラ ヤッ 부호 반전
뿌루 / 우라 뿌루 ppuru / ura ppuru プル / ウラ プル 복제 / 버리기
이야 뿌루 iya ppuru イヤ プル 맨 위 두 값 교환
이이이야하 iiiiyaha イイイヤハ 정수 입력
우라라 이이이야하 urara iiiiyaha ウララ イイイヤハ 문자 입력, EOF는 -1
우라라 야다 urara yada ウララ ヤダ 한 줄을 코드 포인트 배열로 입력
이이이야하 뿌루루 iiiiyaha ppururu イイイヤハ プルル 정수 출력
이이이야하 우라라 iiiiyaha urara イイイヤハ ウララ 유니코드 문자 출력
야다 이이이야하 yada iiiiyaha ヤダ イイイヤハ 정수 배열을 문자들로 출력
우라 이야 ura iya ウラ イヤ 가장 가까운 반복문 탈출
얏 yat ヤッ 프로그램 종료

사칙연산은 스택 맨 위의 정수 두 개를 꺼내 계산합니다. 아래에 있던 값이 왼쪽 피연산자입니다. 7을 넣고 2를 넣은 뒤 뺄셈을 실행하면 5가 남습니다.
나눗셈은 소수점 아래를 버립니다. 예를 들어 -7을 2로 나누면 -3입니다.
정수 입력 명령은 한 줄에 정수 하나를 읽습니다. 출력 명령은 스택에서 값을 꺼내 출력하며, 줄바꿈을 붙이지 않습니다.

소리는 이야, 우라처럼 씁니다. 대시를 넣거나 정해지지 않은 소리를 쓰면 오류가 납니다.
야하야하, 뿌루뿌루, 핫핫핫처럼 붙여 써도 됩니다. 소리가 겹치면 긴 쪽을 먼저 읽습니다.
뿌루루는 뿌루와 다른 소리로 읽습니다.
공백과 줄바꿈은 구절을 끊지 않습니다. 이이이야하 다음 줄에 뿌루루를 써도 하나의 정수 출력 명령으로 읽습니다.

명령을 명확히 구분하려면 ;를 넣습니다.

  • 우라 야하야하: 조건 블록 닫기
  • 우라 야하 ; 야하: 뺄셈 후 덧셈
  • 야하야하: 덧셈 두 번

숫자나 블록을 여닫는 구절 중간에는 ;를 넣을 수 없습니다.
문자열이나 문자를 직접 적는 문법과 주석 문법은 아직 없습니다.

반복과 조건

시작 끝 동작
이야 야다야다 우라 야다야다 맨 위 정수가 0이 아닌 동안 반복
이야 야하야하 우라 야하야하 정수를 꺼내 0이 아니면 본문 실행

반복문은 본문 실행 전과 매 반복이 끝날 때 스택 맨 위의 정수가 0인지 확인합니다. 검사한 값은 스택에 그대로 남습니다.
반복문 본문에는 다음 검사에 쓸 정수 하나를 남겨야 합니다. 블록은 최대 128단계까지 중첩됩니다.

하야하 뿌루뿌루뿌루 야하
이야 야다야다
    뿌루
    이이이야하 뿌루루
    하야하 뿌루 야하
    우라 야하
우라 야다야다
얏

실행하면 321이 나오고, 빈 줄과 들여쓰기는 자유롭게 넣어도 됩니다.

반복 중간에서 탈출하기

우라 이야는 가장 가까운 반복문 하나를 끝내고 닫는 구절 다음으로 이동합니다. 조건 블록 안에서도 쓸 수 있고 중첩 반복에서는 안쪽 반복만 끝냅니다. 스택의 값은 그대로 두므로 탈출 전에 다음 반복 검사에 쓸 정수를 남길 필요는 없습니다. 반복문 밖에서 쓰면 실행 전에 구문 오류가 나며, 탈출 뒤의 코드도 문법 검사를 받습니다. 우라와 이야는 줄바꿈으로 나눌 수 있지만 사이에 ;는 넣을 수 없습니다.

하야하 뿌루뿌루뿌루 야하
이야 야다야다
    우라 이야
우라 야다야다
이이이야하 뿌루루

조건값 3이 남아 있지만 우라 이야가 반복을 끝냅니다. 반복 뒤의 출력은 계속 실행되므로 결과는 3입니다. 프로그램 전체를 끝내는 얏과 동작이 다릅니다.

break_on_zero.yaha는 정수를 계속 읽어 출력하다가 0을 만나면 조건 블록 안의 우라 이야로 반복을 끝냅니다. 0은 출력하지 않습니다.

printf '3\n2\n0\n' | cargo run -- run examples/break_on_zero.yaha --lang ko

출력: 32. 입력을 끝낼 때는 0을 넣어야 하며, 그 전에 EOF를 만나면 정수 입력 오류가 납니다.

비교

비교 구절은 하아로 시작합니다. 핫은 같음, 이야는 작음, 야하는 큼을 검사합니다.
스택 아래 정수가 왼쪽, 맨 위 정수가 오른쪽이며, 두 값을 꺼내 참이면 1, 거짓이면 0을 넣습니다. 배열 비교는 지원하지 않습니다. 음수와 64비트 정수의 최솟값·최댓값도 직접 비교합니다.

이이이야하
이이이야하
하아 이야
이야 야하야하
    하야하 뿌루 야하
    이이이야하 뿌루루
우라 야하야하

첫 번째 입력이 두 번째보다 작으면 1을 출력하고, 아니면 아무것도 출력하지 않습니다. 비교는 입력값을 소비하므로 다시 쓸 값은 미리 복제해야 합니다. 같지 않음은 같음 비교 결과를 0과 다시 비교하고, 이하·이상은 반대 방향 비교 결과를 0과 비교해 표현할 수 있습니다. 야다와 뒤 소리는 줄바꿈으로 나눠도 되지만, 사이에 ;를 넣을 수 없습니다. 이이이야하 우라라 문자 출력과 이야 야다야다 반복 구절의 의미는 그대로입니다.

입력

이이이야하
이이이야하
야하
이이이야하 뿌루루
얏
printf '12\n30\n' | cargo run -- run examples/add_two_numbers.yaha --lang ko

숫자마다 줄을 나눠 입력하면 42가 나오지만, 12 30을 한 줄에 넣으면 오류가 납니다.

문자 입력

우라라 이이이야하는 UTF-8 유니코드 문자 하나를 읽어 코드 포인트를 스택에 넣습니다. 문자 출력인 이이이야하 우라라와 순서가 반대입니다. 공백과 줄바꿈도 문자로 읽으며, 입력 끝(EOF)에서는 -1을 넣습니다. 잘못되거나 중간에 끊긴 UTF-8은 실행 오류입니다.

우라라 이이이야하
이이이야하 우라라

위 코드는 입력 문자 하나를 그대로 출력합니다. EOF의 -1은 문자 출력에 쓸 수 없으므로 반복 입력 시 먼저 검사해야 합니다. character_echo.yaha는 읽은 값에 1을 더한 결과로 반복 여부를 검사해, 코드 포인트 0도 출력하고 EOF에서 멈춥니다.

printf '안녕!\n' | cargo run -- run examples/character_echo.yaha --lang ko

문자 입력은 다음 문자나 줄바꿈을 미리 소비하지 않습니다. 터미널에서는 문자를 입력한 뒤 Enter를 눌러야 전달될 수 있고, 그 줄바꿈은 다음 입력에 남습니다. REPL도 같은 입력 스트림을 사용하므로 남은 문자는 이후 프로그램 입력이나 REPL 소스로 읽힙니다.

문자열 입력

우라라 야다는 UTF-8 한 줄을 읽어 코드 포인트 배열을 스택에 넣습니다. 문자 하나를 읽는 우라라 이이이야하와 달리 줄 끝까지 읽으며 LF 또는 CRLF는 배열에 넣지 않습니다. 앞뒤 공백, 탭, NUL은 보존하고 마지막에 홀로 있는 CR도 보존합니다.

우라라 야다
야다 이이이야하

가나다를 입력하고 Enter를 누르면 가나다가 출력됩니다. 출력 명령은 줄바꿈을 붙이지 않습니다. 빈 줄은 빈 배열이 되며, 줄바꿈 없이 EOF로 끝난 마지막 내용도 한 줄로 읽습니다. 읽을 내용 없이 EOF를 만나면 정수 입력처럼 실행 오류입니다. 문자 입력의 EOF 값 -1과 다릅니다.

printf '가나다\n' | cargo run -- run examples/string_echo.yaha --lang ko

출력: 가나다.

우라라 야다
야다 뿌루루
이이이야하 뿌루루

위 코드는 입력한 줄의 유니코드 코드 포인트 수를 출력합니다. 가나🦀는 3입니다. 조합된 이모지나 결합 문자는 화면에 보이는 한 글자가 여러 원소일 수 있습니다. string_length.yaha로 실행할 수 있습니다.

매 입력은 독립된 배열을 만듭니다. 잘못된 UTF-8과 줄 끝을 제외한 1 MiB 초과 입력은 오류이며 새 값을 스택에 넣지 않습니다. 이미 읽은 입력은 되돌리지 않습니다. REPL에서도 소스 입력과 같은 스트림을 쓰며, 문자열 입력은 그 다음 한 줄과 줄 끝만 소비합니다. 두 소리는 줄바꿈으로 나눌 수 있지만 사이에 ;는 넣을 수 없습니다.

배열

야다 하야하
하야하 야하 야다 뿌루
하야하 뿌루 야하 야다 뿌루
하야하 뿌루뿌루 야하 야다 뿌루
하야하 뿌루 야하 야다 이야
이이이야하 뿌루루

위 코드는 [0, 1, 2] 배열을 만들고 두 번째 원소를 출력합니다. 인덱스는 0부터 시작하므로 출력값은 1입니다.
빈 배열은 야다 하야하로 만듭니다. 기존 [] 리터럴도 지원합니다. 배열 안에 배열을 넣는 문법은 지원하지 않습니다.

명령 피연산자: 스택 아래에서 위로 동작
야다 뿌루루 배열 길이 조회
야다 이야 배열, 인덱스 정수 조회
야다 핫 배열, 인덱스, 값 원소 수정

길이 조회·원소 조회·수정은 피연산자를 모두 스택에서 꺼냅니다. 같은 배열을 다시 쓰려면 명령 실행 전에 뿌루로 배열 참조를 복제합니다.
두 참조는 같은 배열을 가리킵니다. 어느 쪽에서 원소를 수정해도 함께 바뀝니다.

기존 #, @, =는 각각 야다 뿌루루, 야다 이야, 야다 핫과 같은 동작이며 계속 지원합니다.

소리로 배열을 만들고 값 넣고 꺼내기

한국어 영어 일본어 스택 변화: 아래에서 위로
야다 하야하 yada hayaha ヤダ ハヤハ 새 빈 배열을 스택에 추가
야다 뿌루 yada ppuru ヤダ プル 배열, 정수 → 배열
야다 우라 yada ura ヤダ ウラ 배열 → 배열, 마지막 정수
야다 우라라 yada urara ヤダ ウララ 배열 → 원본 배열, 독립된 복사본
야다 뿌루루 yada ppururu ヤダ プルル 배열 → 길이
야다 이야 yada iya ヤダ イヤ 배열, 인덱스 → 원소
야다 핫 yada hat ヤダ ハッ 배열, 인덱스, 값 → 피연산자 소비

야다 하야하는 실행할 때마다 독립된 빈 배열을 만듭니다. 하야하 뒤에 숫자 닫기인 야하를 붙이지 않습니다. 추가·꺼내기 두 명령은 배열 참조를 스택에 남깁니다. 추가는 정수만 소비하며, 꺼내기는 마지막 원소를 배열에서 제거하고 스택에 올립니다. 복제해 둔 참조에서도 배열 변경이 보입니다. 빈 배열에서 꺼내기, 잘못된 타입, 스택 부족은 실행 오류이며 스택과 배열을 변경하지 않습니다. 야다 뒤 소리는 줄바꿈으로 나눌 수 있지만 사이에 ;를 넣을 수 없습니다.

야다 하야하
하야하 뿌루 야하
야다 뿌루
하야하 뿌루뿌루 야하
야다 뿌루
야다 우라
이이이야하 뿌루루
야다 우라
이이이야하 뿌루루

빈 배열에 1, 2를 추가하고 뒤에서부터 꺼내므로 21을 출력합니다.

실행한 구절 스택
야다 하야하 [[]]
1 생성 후 야다 뿌루 [[1]]
2 생성 후 야다 뿌루 [[1, 2]]
야다 우라 [[1], 2]
이이이야하 뿌루루 [[1]]
야다 우라 [[], 1]
이이이야하 뿌루루 [[]]

reverse_input.yaha는 EOF까지 문자를 배열에 모은 뒤 하나씩 꺼내 거꾸로 출력합니다. 문자 입력에서 EOF가 -1이므로, 입력값을 복제하고 1을 더한 값을 반복 조건으로 씁니다. 출력 반복은 배열 길이가 0이면 끝납니다. 유니코드 코드 포인트 순서를 뒤집으며, 결합 문자나 여러 코드 포인트로 된 이모지는 묶어서 처리하지 않습니다.

printf '가나다' | cargo run -- run examples/reverse_input.yaha --lang ko

출력: 다나가. 입력에 줄바꿈을 넣으면 줄바꿈도 뒤집힌 위치에 출력됩니다.

배열을 독립적으로 복사하기

뿌루는 같은 배열을 가리키는 참조를 복제하고, 야다 우라라는 배열 내용을 별도의 배열에 복사합니다. 원본 참조는 그대로 두고 복사본을 스택 맨 위에 넣습니다. 빈 배열도 복사할 수 있고, 정수나 빈 스택에 쓰면 실행 오류이며 상태를 바꾸지 않습니다.

야다 하야하
하야하 뿌루 야하
야다 뿌루
야다 우라라
하야하 뿌루뿌루 야하
야다 뿌루

처음 [1]을 만들고 복사한 다음, 복사본에만 2를 추가합니다. REPL에서 이어서 :stack을 입력하면 [[1], [1, 2]]가 나옵니다. 야다 우라라 대신 뿌루를 쓰면 같은 배열을 공유하므로 [[1, 2], [1, 2]]가 됩니다. 원본에 대한 기존 참조들은 계속 원본을 공유하고, 복사본의 수정·추가·제거는 원본에 영향을 주지 않습니다.

array_copy.yaha는 복사본 길이 2와 원본 길이 1을 차례로 출력합니다.

cargo run -- run examples/array_copy.yaha --lang ko

출력: 21. 구절은 줄바꿈으로 나눌 수 있지만 야다 ; 우라라처럼 쓸 수는 없습니다.

배열을 문자로 출력하기

야다 이이이야하는 배열의 정수를 코드 포인트로 해석해 앞에서부터 출력합니다. 자동 줄바꿈은 없고, 성공하면 배열 참조 하나를 스택에서 꺼냅니다. 배열 내용은 바꾸지 않습니다. 배열을 다시 쓰려면 먼저 뿌루로 참조를 복제합니다. 빈 배열은 아무것도 출력하지 않습니다.

야다 하야하
우라라 이이이야하
야다 뿌루
우라라 이이이야하
야다 뿌루
야다 이이이야하

입력으로 가나를 전달하면 두 문자를 배열 [44032, 45208]에 모은 뒤 가나를 출력합니다. 코드에 숫자를 길게 쓰지 않아도 입력한 문자로 배열을 만들 수 있습니다.

print_character_array.yaha는 EOF까지 문자를 모은 뒤 배열 전체를 한 번에 출력합니다. 입력이 끝나기 전에는 출력하지 않습니다.

printf '안녕!\n' | cargo run -- run examples/print_character_array.yaha --lang ko

출력은 안녕!과 입력에 들어 있던 줄바꿈입니다. 공백과 NUL도 그대로 출력합니다. 문자 입력의 EOF 값인 -1은 배열에 넣으면 안 됩니다. 음수·서로게이트·유니코드 범위를 벗어난 값이 하나라도 있으면 출력하기 전에 오류를 내며 스택도 유지합니다. 스택 부족·정수 타입 입력도 실행 오류입니다. 출력 장치 오류 때는 배열 참조를 유지하지만 이미 전달된 출력은 되돌릴 수 없습니다. 구절 중간의 줄바꿈은 허용하고 ;는 허용하지 않습니다.

언어 설정과 오류

run과 repl에서 --lang ko, --lang en, --lang ja로 코드의 언어 표기를 선택합니다. 한 프로그램 안에서는 같은 표기를 사용합니다.
자주 쓰는 표기는 아래 명령으로 기본값을 저장합니다. 실행할 때 지정한 --lang이 저장된 설정보다 우선합니다. 둘 다 없으면 영어 표기를 사용합니다.

cargo run -- set lang ko

설정 파일에는 lang = "ko" 형식으로 저장합니다. 운영체제별 경로는 다음과 같습니다.

  • Linux: $XDG_CONFIG_HOME/yahalang/config.toml 또는 ~/.config/yahalang/config.toml
  • macOS: ~/Library/Application Support/yahalang/config.toml
  • Windows: %APPDATA%/yahalang/config.toml

기본 오류 메시지는 우사기 말소리로 나오고, friendly를 붙이면 원인을 표시합니다.

cargo run -- repl --lang ko --error-style friendly

실행 중에는 스택 값 부족, 맞지 않는 타입, 잘못된 입력이나 배열 인덱스, 유효하지 않은 문자 코드, 정수 범위를 넘는 계산, 0으로 나누기가 오류의 원인이 됩니다.
닫지 않은 숫자나 블록, 잘못 쓴 구절은 실행 전에 검사합니다. friendly 오류 메시지에 표시된 줄 번호로 위치를 확인합니다.

예제

파일 동작
hello_world.yaha Hello, world!와 줄바꿈 출력
add_two_numbers.yaha 정수 두 개의 합 출력
character_echo.yaha EOF까지 입력 문자 그대로 출력
string_echo.yaha 한 줄을 배열로 읽어 출력
string_length.yaha 한 줄의 코드 포인트 수 출력
integer_echo.yaha 입력 정수 출력
print_character_array.yaha EOF까지 문자를 모아 배열 전체 출력
array_copy.yaha 복사본과 원본 길이를 출력해 21 확인
array_push_pop.yaha 1, 2를 추가하고 꺼내 21 출력
reverse_input.yaha EOF까지 읽은 문자의 순서를 뒤집어 출력
array_access.yaha 배열 수정과 조회
compare.yaha 첫 번째 입력이 두 번째보다 작으면 1 출력
break_on_zero.yaha 정수를 출력하다 0에서 반복 탈출
countdown.yaha 321 출력
truth_machine.yaha 0은 한 번 출력, 1은 계속 출력

truth_machine.yaha에는 0이나 1을 입력하고, 1의 반복 출력은 Ctrl-C로 멈춥니다.

About

A small stack-based esolang that uses Usagi’s sounds from Chiikawa

Topics

Resources

Stars

31 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages