- Cognition은 Lisp와 연결형 언어의 읽기 선행(read-ahead) 문제를 피하려고 완전 후위(postfix) antisyntax를 택한 언어 연구 프로젝트임
- 핵심 장치인 delimiter, ignore, singlet, falias, crank, metacrank는 프로그램이 자기 토큰화 규칙과 실행 주기를 바꾸게 해줌
- 부트스트랩은 모든 문자를 단일 토큰으로 읽는 상태에서 시작해, 공백과 개행을 구분자로 쓰는 환경으로 스스로 이동함
crank와metacrank는 토큰을 언제 평가하고 언제 쌓아둘지 제어해#주석,\escape,[quote,(macro 같은 prefix 문법을 후위 시스템 안에서 정의하게 함- Brainfuck 방언도 별도 파서가 아니라 Cognition 단어와 토큰화 규칙으로 구현해, 문법 자체를 코드로 만들고 자동화하는 방향을 보여줌
기존 문법에서 Cognition이 문제 삼는 지점
- Lisp는 s-expression과 매크로 시스템으로 강력한 메타프로그래밍을 제공하지만, 여전히 고정된 문법의 영향을 받음
- 왼쪽 괄호는 오른쪽 괄호가 나올 때까지 읽어야 한다는 신호이므로, 언어 안에서 괄호의 역할을 바꾸기 어렵거나 일부 구현에서는 불가능함
- 이미 읽은 토큰의 구분 방식을 나중에 바꾸려면 많은 문자열 처리가 필요함
- 현재 입력을 보고 뒤를 더 읽어야 하는 과정이 syntax이며, 기본 읽기 선행을 가정하는 순간 특정 문법 형태에 묶임
- Cognition은 이를 피하려고 완전 후위 방식의 antisyntax를 사용함
- 연결형 언어와 비슷하지만, 일반적인 연결형 언어도 대괄호나 문자열 quote 문자 때문에 읽기 선행 문제를 갖는다고 봄
- Racket의 매크로 시스템은 실행 중 동적 문법 변경이 아니라 전처리를 사용하는 방식으로 구분됨
프로젝트와 기본 아이디어
- Cognition은 Matthew Hinton과 함께 몇 달 동안 진행한 활성 연구 프로젝트임
- 구현 저장소는 cognition-rust에 있으며, 저장소에는 언어에 대한 논문도 있음
- 이해에는 파싱, 토큰화, 문법에 대한 배경이 도움이 됨
- 설명은 “baremetal cognition” 코드에서 출발해 Stem과 비슷한 문법으로 발전하는 흐름을 따라감
Baremetal Cognition과 토큰화
- baremetal Cognition은 겉보기에는 Brainfuck과 비슷하지만, 더 강한 메타프로그래밍을 할 수 있음
- 매우 작은 부트스트랩 코드는 공백과 개행까지 의미 있게 사용하며, 초기 상태에서는 모든 단일 문자가 토큰으로 읽힘
- Cognition은 기본적으로 스택 기반 설계이지만, 스택보다 일반적인 의미로 container라는 용어를 사용함
- 기본 환경에서는 특별한 falias를 제외하면 어떤 단어도 자동 실행되지 않음
-
delimiter, ignore, singlet
- delimiter는 토크나이저가 한 토큰의 끝과 다음 토큰의 시작을 알 수 있게 함
- 단일 문자 delimiter 목록은 Cognition 코드 안에서 수정하고 읽을 수 있음
- ignore 문자는 각 read-eval-print loop의 토큰 수집 시작 단계에서 건너뜀
- 기본값은 모든 문자가 delimiter이고 ignore 문자는 없는 상태임
- delimiter, singlet, ignore 목록은 whitelist/blacklist 플래그로 동작 방식을 바꿀 수 있음
- 기본 설정은 blacklist delimiter 없음, whitelisted singlet 없음, whitelisted ignore 문자 없음임
- singlet은 자기 자신을 토큰에 포함한 뒤 토큰 수집을 끝내는 세 번째 토큰화 범주임
-
falias
- falias는 container에 올라가는 즉시 실행되는 단어 목록임
- 기본 falias인
f는 container에 쌓이지 않고 container 맨 위 단어를 실행함 - 예시에서
f가d를 실행하면,d는 delimiter 목록을 단어의 문자열 값으로 바꿈 - 이후
l,g,t,d같은 문자를 단계적으로 non-delimiter로 만들고, 공백과 개행을 delimiter 및 ignore로 바꾸는 부트스트랩이 이어짐
부트스트랩이 만드는 실행 환경
- 초기 부트스트랩은 delimiter와 ignore 규칙을 바꿔 최종적으로 공백과 개행이 토큰 delimiter가 되고, 토큰 시작 단계에서 공백과 개행이 무시되는 환경을 만듦
- 그 뒤
1과crank를 읽고,f가crank를 실행해 crank 1 환경으로 들어감 - 이 과정의 핵심은 실행 중 토큰화 방식을 바꿀 수 있다는 점임
- delimiter, singlet, ignore 변경을 프로그램으로 자동화할 수 있음
- 후위 방식이고 읽기 선행이 없기 때문에 표현식 평가 전에 하나 이상의 토큰을 미리 파싱할 필요가 없음
- falias는 prefix 단어나 기본 단어 자동 실행 없이도 특정 단어를 실행하게 해줌
crank와 metacrank
- crank는 stack 위 토큰을 어떤 주기로 실행할지 정하는 장치임
crank단어는 숫자를 인자로 받아, 이후 container에 올라오는 매n번째 단어마다 stack top을 실행함- 부트스트랩 마지막의
1 crank는 모든 토큰을 평가하는 환경을 만듦 5 crank상태에서는 다섯 번째 토큰이 들어올 때까지 단어들이 쌓일 수 있음
- 부트스트랩 마지막의
- 예시 코드는
2crank라는 단어를 만들기 위해unglue,swap,quote,prepose,def를 사용함unglue는 단어 값을 가져오며,crank같은 builtin의 함수 포인터도 가져올 수 있음prepose는 Stem의compose와 비슷하지만 앞에 붙이고 VMACRO에 넣음def는2crank가2를 stack에 놓고crankbuiltin을 호출하도록 정의함
-
Cognition의 container와 macro
- Stem에서는 단어를 stack에 직접 둘 수 있지만, Cognition에서는 평가되지 않은 단어가 container 안에 들어감
- 이 설계 덕분에
compose같은 단어가 단일 단어 container와 다른 container를 일관된 API로 다룰 수 있음 - Cognition의 macro는 Stem quote와 다름
- macro가 평가되면 crank를 무시하고 내부 모든 요소가 평가됨
- 단어에 바인딩된 macro를 평가하면 macro 전체가 crank와 무관하게 실행되고 cranker는 한 번만 증가함
- macro는 crank에 독립적인 코드와 최적화 목적의 expansion에 유용함
-
metacrank
n m metacrank는 stack에서n개 아래의 요소에 대해 주기m의 평가를 설정함crank는0 m metacrank와 같음- 토큰 하나당 평가될 수 있는 metacrank는 하나뿐이며, 낮은 metacrank가 우선권을 가짐
- metacrank와 crank는 파일의 토큰뿐 아니라 단어 정의를 재귀적으로 평가하는 과정에도 적용됨
- metacrank는 “n개 토큰을 읽은 뒤 이 토큰을 실행하고 싶다” 같은 직접 문법 조작을 가능하게 함
- prefix 단어를 프로그래밍한 뒤 필요 없어지면
undef할 수 있음 - 특정 종료 문자가 아니라 특정 토큰 수 이후 멈추는 prefix 문자도 만들 수 있음
- 사용자 입력을 수학 프로그램에 넣고, 그 출력을 metacrank 같은 문법 시스템에 전달할 수 있음
Stem 방언으로 발전하는 과정
- Cognition은 부트스트랩 이후 Stem v2에 가까운 문법을 언어 내부에서 점진적으로 구축함
- 먼저
f를 falias 목록에서 제거하고ing만 남김f를 직접 stack에 놓으면 실행되기 때문에ff를 만든 뒤 문자열을 반으로 잘라 두 개의f를 얻음- 이후
f는 false에 해당하는 빈 단어로 정의됨
-
#주석#문자는 처음으로 진짜 prefix처럼 동작하는 코드 예시임- 이 주석 문자는 newline 전까지의 텍스트를 버리는 prefix로 동작해, 파서가 앞을 읽는 형태의 문법을 만듦
- 구현은
geti,getd,gets,crankbase,halt,VMACRO cast,singlet,delim등을 조합함 geti,getd,gets는 각각 ignore, delimiter, singlet을 문자열로 가져옴halt는 모든 metacrank를 0으로 만듦VMACRO cast는 stack top의 container를 macro로 바꿈#정의는 토큰화 규칙을 바꾼 뒤 미래에 토큰화될 단어에 대해#를 호출하고, 그 주석을 drop한 뒤 원래 crank와 metacrank로 돌아감
-
escape, quote, macro
\는 평가될 단어라도 stack에 올릴 수 있게 하는 escape 문자로 정의됨- 이후
[quote 정의가 추가되고, 다시 그 quote를 사용해 재귀 quote가 가능한 더 나은[정의로 자기 자신을 재정의함 - 후위
def때문에 이전 정의를 사용해 새 정의를 만들 수 있음 - 이 패턴은 low-level Cognition에서 자주 쓰이는 개발 방식임
(는 macro bracket으로 정의됨- macro는 자동 확장되도록 만들어져, 이미 확장된 macro를 단어에 바인딩하는 것이 더 효율적이라고 봄
- 기능적으로는 같은 방식으로 평가됨
expand는 quote나 macro 안의 단어 정의를unglue로 재귀 확장함- 먼저 기본
expand를 정의한 뒤, 더 일반적인 경우를 처리하도록expand자신을 사용해 재정의함
Brainfuck 방언
- Cognition은 발전된 Stem 방언 위에서 Brainfuck 방언을 정의함
- 실행 예시는
../crank -s 2 bootstrap.cog helloworld.bf brainfuck.cog임 brainfuck.cog는 일반적인 의미의 Brainfuck 파서가 아님- Brainfuck 단어를 정의함
- Brainfuck을 토큰화함
- Cognition 네이티브 환경에서 실행함
- 이 예시는 Cognition 문법으로 대체 문법을 쉽게 정의할 수 있음을 보여줌
- Cognition은 기호를 읽고 기호에 따라 동작을 결정하는 대신, metacrank를 사용하는 prefix 문자 자체를 단어로 정의해 문법을 단어 정의 안에 넣음
dialect dialect 구상
mkprefix같은 단어를 상상할 수 있음- 예를 들어
[와]두 입력 단어와 어떤 연산을 받아,[가]를 만날 때까지 그 연산을 적용하도록 자동 정의하는 단어임
- 예를 들어
- 이런 구상이 가능한 이유는
metacrank와def가 모두 일반 단어이기 때문임 d,i,s까지 포함해 모든 것이 단어이므로, 문법 구현 과정을 자동화하는 더 추상적인 방언을 만들 수 있음- 표준 라이브러리에는 아직 구현되지 않았지만, Matthew Hinton과 표준 라이브러리 가능성으로 논의한 항목이 있음
- 추상 단어를 자동 생성하고 호출하는 metaword
- 현재 wordlist를 자동 추상화할 word-generator 검색
- 문제 해결을 위해 추상화 프레임워크를 지시하는 방식
문법을 코드로 다루는 가능성
- Cognition에서는 문자열 처리가 토크나이저 후처리와 같아, 문자열 연산이 강한 의미를 가짐
- 잠재 적용 분야로 Symbolic AI, 문법과 grammar 연구, 언어와 metalanguage 프로토타입 실험이 제시됨
- 설정 파일을 읽는 프로그램, Cognition 기반 shell, Cognition 기반 운영체제 같은 아이디어도 있음
- 핵심은 Cognition이 “syntax as code”를 가능하게 한다는 점임
- 문법을 동적으로 프로그래밍할 수 있음
- 문법 생성 자체를 자동화할 수 있음
- Metastack과
cd같은 개념은 다루지 않았고, 후속 글에서 다룰 수 있는 주제로 남아 있음