4P by GN⁺ | ★ favorite | 댓글 1개
  • Piet는 코드가 추상화처럼 보이도록 만든 난해한 프로그래밍 언어로, 기하학적 추상미술을 개척한 Piet Mondrian의 이름을 따옴
  • 프로그램은 20가지 인식 색상으로 만든 그래픽이며, 실행기는 색상 블록 사이를 이동하며 색상 변화량을 명령으로 해석함
  • 모든 데이터는 정수로만 존재하고 스택에 저장되며, 색상 블록의 크기는 값이지만 push 명령 없이는 자동으로 스택에 올라가지 않음
  • 흐름 제어는 Direction Pointer와 Codel Chooser, 검은 블록·가장자리·흰 블록 규칙으로 정해지며 일부 동작은 구현체마다 달라질 수 있음
  • 예제와 외부 도구 생태계가 있지만 권위 있는 공식 인터프리터는 없고, 오류 처리와 비표준 색상 해석도 구현 의존적으로 남아 있음

Piet의 기본 아이디어

  • Piet는 프로그램 코드가 추상미술처럼 보이는 프로그래밍 언어임
  • 이름은 기하학적 추상미술을 개척한 Piet Mondrian에서 따옴
  • Mondrian이라는 이름을 쓰고 싶었지만 이미 같은 이름의 스크립트 언어가 있어 Piet라는 이름을 사용함
  • 명세 작성 이후 작은 커뮤니티가 생겨 프로그램, 인터프리터, IDE, 컴파일러를 만들어 왔음
  • 권위 있는 공식 인터프리터는 없으며, 사용 가능한 구현체들은 명세를 조금씩 다르게 해석할 수 있음
  • 일부 명세 해설이 추가됐지만 기존 구현체 중 일부는 이를 따르지 않을 가능성이 있음

색상과 코드 단위

  • Piet는 총 20가지 색상을 사용함
    • 18개 색상은 색상 순환과 명도 순환에 포함됨
    • 흰색과 검은색은 두 순환에 포함되지 않음
  • 색상 순환red -> yellow -> green -> cyan -> blue -> magenta -> red 순서임
  • 명도 순환light -> normal -> dark -> light 순서임
    • lightdark보다 한 단계 더 어두운 것으로도 간주되고, 그 반대도 성립함
  • 주황색이나 갈색 같은 비표준 색상은 사용할 수 있지만 효과는 구현 의존적임
    • 가장 단순한 경우 비표준 색상은 흰색처럼 처리됨
    • 다른 가능성으로는 검은색처럼 처리될 수 있음

Codel과 색상 블록

  • Piet 코드는 인식 가능한 색상으로 구성된 그래픽임
  • 개별 코드 픽셀이 언어적으로 의미를 가지므로, 보기 쉽게 확대된 프로그램에서는 코드상의 단일 픽셀을 codel이라고 부름
  • 기본 실행 단위는 색상 블록
    • 색상 블록은 같은 색상의 codel이 상하좌우로 이어진 영역임
    • 대각선으로만 닿은 블록은 이어진 것으로 보지 않음
    • 색상 블록은 어떤 모양도 가능하고, 내부에 다른 색상의 구멍을 가질 수 있음
    • 내부 구멍은 해당 블록의 일부가 아님

스택과 값 표현

  • Piet는 모든 데이터 값을 스택에 저장함
  • 데이터 값은 정수만 존재함
    • 명령에 따라 Unicode 문자 값으로 입력하거나 출력할 수 있음
  • 스택은 개념적으로 무한히 깊지만, 구현체는 유한한 최대 스택 크기를 둘 수 있음
  • 유한 스택에서 오버플로가 발생하면 런타임 오류이며, 처리는 구현 의존적임
  • 검은색과 흰색이 아닌 색상 블록은 해당 블록의 codel 개수와 같은 정수 값을 나타냄
    • 양수가 아닌 정수는 직접 표현할 수 없음
    • 연산자를 통해 생성할 수는 있음
    • 색상 블록 값은 자동으로 스택에 push되지 않고, 명시적인 push 명령이 필요함
  • 정수 크기도 개념적으로 무한하지만, 구현체는 유한한 최대 정수 크기를 둘 수 있음
    • 정수 오버플로는 런타임 오류이며 처리는 구현 의존적임

실행 흐름

  • 인터프리터는 프로그램의 왼쪽 위 codel을 포함하는 색상 블록에서 실행을 시작함
  • 실행 중에는 두 가지 상태가 유지됨
    • Direction Pointer(DP): 처음에는 오른쪽을 가리키며, 오른쪽·왼쪽·아래·위 중 하나를 가리킴
    • Codel Chooser(CC): 처음에는 왼쪽을 가리키며, 왼쪽 또는 오른쪽 중 하나를 가리킴
  • 다음 이동 대상은 현재 색상 블록의 경계와 DP·CC 조합으로 결정됨
    • DP 방향으로 가장 멀리 있는 현재 색상 블록의 가장자리를 찾음
    • 그 가장자리에서 DP 진행 방향을 기준으로 CC 방향으로 가장 먼 codel을 고름
    • 해당 codel에서 DP 방향으로 바로 다음 codel이 속한 색상 블록으로 이동함
  • 이 과정을 반복하다가 종료 조건에 도달하면 프로그램이 종료됨

검은 블록, 가장자리, 흰 블록

  • 검은 색상 블록과 프로그램 가장자리는 실행 흐름을 막는 장벽으로 동작함
  • 인터프리터가 검은 블록으로 이동하거나 가장자리 밖으로 나가려 하면 멈추고 CC를 전환함
  • 두 번째 시도도 실패하면 DP를 시계 방향으로 한 단계 돌림
  • CC와 DP를 번갈아 바꾸며 총 8번 시도한 뒤에도 현재 색상 블록을 벗어날 수 없으면 프로그램이 종료됨
  • 흰 블록 이동

    • 흰 색상 블록은 인터프리터가 방해 없이 통과하는 자유 영역임
    • 색상 블록에서 흰 영역으로 이동하면, 인터프리터는 DP 방향으로 직선 이동하다가 흰색이 아닌 색상 블록에 도달함
    • 흰 블록을 지나 새 색상으로 이동할 때는 명령이 실행되지 않음
    • 이 특성 때문에 흰 블록은 명령 실행 없이 현재 색상을 바꿀 수 있어 루프 작성에 유용함
    • 흰 블록 이동은 비흰색 색상 블록에서 출구를 고르는 절차를 사용하지 않고 직선 이동만 수행함
  • 흰 블록에서 막혔을 때

    • 흰 블록을 직선으로 지나가다 검은 블록이나 가장자리를 만나면 제한에 걸린 것으로 처리됨
    • 이 경우 CC를 전환하지만 이동하려는 위치가 달라지지 않으므로 DP를 즉시 시계 방향으로 한 단계 돌림
    • 이후 현재 흰 codel에서 새 DP 방향으로 다시 직선 이동함
    • 흰 블록 안에서 제한을 만날 때마다 CC 전환과 DP 회전을 반복함
    • 색상 블록에 진입하면 실행이 계속되고, 흰 블록 안에서 경로를 되짚기 시작하면 빠져나갈 길이 없어 실행이 종료됨

명령 체계

  • Piet의 명령은 한 색상 블록에서 다음 색상 블록으로 이동할 때의 색상 변화로 결정됨
  • 색상 순환에서 몇 단계 이동했는지와 명도 순환에서 몇 단계 이동했는지가 명령을 정함
  • 흰 블록을 통해 이동한 색상 전환에서는 명령이 실행되지 않음
  • 스택 및 산술 명령

    • push: 방금 빠져나온 색상 블록의 값을 스택에 올림
    • pop: 스택 맨 위 값을 꺼내 버림
    • add: 상위 두 값을 더해 결과를 다시 스택에 올림
    • subtract: 두 번째 값을 맨 위 값으로 뺀 결과를 스택에 올림
    • multiply: 상위 두 값을 곱함
    • divide: 두 번째 값을 맨 위 값으로 정수 나눗셈함
    • 0으로 나누기는 구현 의존 오류이며, 명령을 무시하는 방식이 권장됨
    • mod: 두 번째 값을 맨 위 값으로 나눈 나머지를 스택에 올림
    • 결과는 제수, 즉 스택 맨 위 값과 같은 부호를 가짐
    • 맨 위 값이 0이면 0으로 나누기 오류이며, 명령을 무시하는 방식이 권장됨
    • 음수 피제수에 대한 mod는 Wikipedia의 modulus operation에서 말하는 floored division과 동일함
  • 비교, 포인터, 입출력 명령

    • not: 스택 맨 위 값이 0이 아니면 0으로, 0이면 1로 바꿈
    • greater: 두 번째 값이 맨 위 값보다 크면 1, 아니면 0을 스택에 올림
    • pointer: 스택 맨 위 값을 꺼내 DP를 그 횟수만큼 시계 방향으로 회전함
    • 음수면 반시계 방향으로 회전함
    • switch: 스택 맨 위 값을 꺼내 CC를 그 횟수만큼 전환함
    • 음수면 절댓값만큼 전환함
    • duplicate: 스택 맨 위 값의 복사본을 스택에 올림
    • roll: 상위 두 값을 꺼내 남은 스택 일부를 지정 깊이와 횟수만큼 회전함
    • 깊이가 음수면 오류이며 명령은 무시됨
    • 구현 의존 최대 스택 깊이를 넘는 roll은 구현 의존 오류이며, 명령 무시가 권장됨
    • in: STDIN에서 숫자 또는 문자를 읽어 스택에 올림
    • 입력이 없거나 정수 입력에서 정수를 받지 못하면 오류이며 명령은 무시됨
    • out: 스택 맨 위 값을 숫자 또는 문자로 STDOUT에 출력함
    • 스택 값이 부족해 수행할 수 없는 연산은 명령이 무시되고 다음 명령으로 넘어감

예제와 도구

댓글과 토론

Hacker News 의견들
  • 예시 페이지의 마지막 프로그램은 정말 놀라움: Piet라는 이름의 사람이 Piet 언어를 떠올리게 하는 미술 작품을 보고 실행해 봤다고 함
    실행됐고, 아마도 그래픽 아티스트가 우연히 동작하는 컴퓨터 프로그램을 그린 역사상 첫 사례일 수 있음
    https://www.dangermouse.net/esoteric/piet/samples.html
    https://gitlab.fabcity.hamburg/hofalab/piet-get-together

    • “동작하는” 조건을 충분히 느슨하게 잡으면, 대부분의 물감 튀김은 이미 유효한 Perl 프로그램이라는 것이 보인 적 있음
      https://www.mcmillen.dev/sigbovik/
    • Piet J.가 작은 갤러리에서 미술 작품을 보다가 Piet 프로그램 같다고 느꼈고, 작가는 그 언어를 전혀 모른다고 했음
      Piet가 작품 사진을 찍고 Piet 팔레트에 가까운 색으로 정리한 이미지 파일로 변환한 뒤 실행했더니 실제로 실행됐고, 코드는 ASCII 문자를 읽어 해당 ASCII 숫자값을 출력하는 무한 루프였음
      이건 정말 믿기 힘들 정도임
    • π를 계산하는 예시도 좋았음
      “당연히 더 큰 프로그램을 쓰면 더 정확한 값을 얻을 수 있다”는 설명은 내겐 처음 보는 식의 농담처럼 느껴짐
    • 안타깝게도 그건 npiet와 현재 Piet 명세 사이의 차이에 의존함
      명세상 인터프리터는 현재 흰색 코델에서 새 방향의 DP로 미끄러지기 시작해, 색 블록에 들어가거나 다른 제한을 만날 때까지 진행해야 함
      하지만 npiet 인터프리터는 공백을 들여다본 뒤 마지막 색 코델 위치로 되감음. 언젠가 내 Piet 컴파일러의 렉서에 그 동작을 옵션으로 넣고 싶지만 아직 손대지 않았음
      명세를 따르면 그 프로그램은 거의 모든 블록의 극단 모서리가 흰색에 인접해 있어서 단순한 비종료 루프가 됨. 여러 인터프리터와 컴파일러를 대상으로 복잡한 Piet 프로그램을 쓰는 건 꽤 어렵고, 다들 문서화되지 않은 미묘한 해석 차이가 있음
      내 Piet 백엔드 출력은 대체로 인터프리터에 덜 의존한다고 생각하지만, 자세히 파본 건 다른 인터프리터 서너 개 정도뿐임
      https://github.com/boothby/repiet/
    • 이런 단순한 그림, 즉 큰 직사각형 블록 몇 개짜리가 유효한 프로그램일 확률이 얼마나 되는지 궁금함
      문서를 훑어보면 “스택에 값이 부족해 꺼낼 수 없는 연산처럼 수행할 수 없는 연산은 그냥 무시하고 다음 명령으로 계속 진행한다”는 조건 때문에, 이런 이미지들은 전부 오류 없이 실행될 수도 있을 것 같음
      다만 그런 무작위 이미지 중 실제로 “의미 있는” 일을 하는 것이 얼마나 될지는 별도 문제임
  • Piet는 난해한 프로그래밍 언어 중에서도 이정표 같은 실험이지만, 개발자가 정말 의도하지 않는 한 프로그램을 Mondrian 그림처럼 보이게 한다는 목표에는 못 미친다고 봄
    언어 구조 자체가, 무엇을 “쓰든” Mondrian 그림처럼 보이게 설계됐으면 좋겠음

    • 그렇긴 한데 Mondrian은 사실상 원색만 주로 썼으니 꽤 제한적이었을 것임
  • 늘 이런 질문이 떠오름: 알고리즘은 어떻게 생겼을까?
    Herman Hesse의 소설 _The Glass Bead Game_에 나오는 것과 비슷한 무언가를 현실에서 만들 수 있을까? 원제는 _Magister Ludi_임
    시각 지향적인 사람으로서 가능하다고 믿고 싶고, 실제로 그런 도구를 써보기도 했음
    https://community.carbide3d.com/uploads/default/original/3X/5/b/5b0872a5666fec9b7bb6fd623c431de03263372d.jpeg
    하지만 앞 질문에 대한 명확한 답이 없으면 이런 도구들은 늘 다음처럼 될 위험이 있음
    https://blueprintsfromhell.tumblr.com/
    https://scriptsofanotherdimension.tumblr.com/
    시각적 표현력과 모듈성 사이의 균형을 맞추기도 어렵고, 모듈성을 밀어붙이면 피하려던 텍스트 장벽으로 너무 쉽게 돌아가게 됨

    • Piet를 손으로 작성해 보면 “알고리즘은 어떻게 생겼는가”를 탐색하는 재미가 있음
      Sergei Lewis와 나는 각각 Piet 코드를 생성하는 도구를 만들었음. Sergei의 어셈블러가 내 Piet 백엔드보다 훨씬 보기 좋은 코드를 만들어냄
      내 컴파일러 출력에서 실제로 보이는 건 내가 정말 게으르게 트램펄린을 썼다는 정도임
      http://www.toothycat.net/wiki/wiki.pl?MoonShadow/Piet
      https://github.com/boothby/repiet/
      https://en.wikipedia.org/wiki/Trampoline_(computing)
    • 어떤 알고리즘이든, 심지어 어떤 정신적 개념이든 시각적 표현과 1:1 관계가 있다고 믿음
      Steven Pinker의 책을 읽다가 얻은 생각인데, 추상어는 더 단순한 말로 쪼갤 수 있고 결국 어떤 공간적 관계를 설명하게 된다고 함. 예를 들어 “rekindle”은 “두 가지를 다시 함께 가져오다”로 볼 수 있음
      비슷하게 for 루프도 “하나가 여러 다른 것들 위를 지나가는” 정신적 개념이고, 이는 “100” -> “010” -> “001” 같은 시각 표현을 가짐
      그렇다면 이런 구성 요소를 순수한 시각 변환으로 정의하는 언어를 만들 수 있을지 궁금함
    • 단순한 프로그램이라면 기호를 색상으로 둔 튜링 기계를 구현한다고 상상할 수도 있음
  • 이런 건 범죄 스릴러에 나와서 주인공이나 수사관들을 막히게 하다가 누군가 코드라는 걸 알아채는 장면에 어울림
    QR 코드만 유용하다고 생각했는데 말임

  • 누군가 Piet로 quine을 만들었음: http://mamememo.blogspot.com/2009/10/piet-quine.html?m=1
    그 글의 이미지는 깨졌지만, 복사본은 여기 있음: https://codegolf.stackexchange.com/a/23255/103045

  • Piet를 발견하는 순간은 경외감, 혼란, 놀라움이 섞인 특별한 순간임
    내 경우는 친구 Oz와 함께한 컴퓨터 과학 팟캐스트 “The CS Primer Show”의 이 대화에 담겨 있음: https://show.csprimer.com/episodes/e2-dont-let-a-gpt-have-all-the-fun

  • 대학에서 난해한 프로그래밍 언어를 다루는 작은 수업이 있었음
    각자 Brainfuck, Piet 같은 언어 중 하나를 골라 가지고 놀아야 했고, 나는 Piet를 골라 꽤 즐겼음
    솔직히 내가 만든 작은 예제 앱은 미적으로 그리 훌륭하진 않았고, Piet로 예술을 만들려면 Piet 전문가가 되어야 할 것 같음

  • 예시 페이지가 훌륭함
    캔버스가 점점 더 정교하고 보기 좋게 진화하는 과정을 볼 수 있음
    https://www.dangermouse.net/esoteric/piet/samples.html

  • “light는 dark보다 한 단계 더 어둡다고 간주한다”라니, 꽤 심오함

  • Python이나 다른 덜 난해한 언어의 코드를 받아 Piet로 출력하는 법을 학습하는 자동인코더를 만들 수 있으면 멋질 것 같음
    그러면 Stable Diffusion 비슷하게 무작위 알고리즘을 생성할 수도 있을 듯함