표준 라이브러리 수준 언어 준비도
C 의 libc 정도 되는 표준 라이브러리를 KingSejong 코드로 직접 쓸 수 있으려면 언어에 무엇이 더 필요한지 정리한다.
작성: 2026-11-03 (v0.6.0 + main ed0e758 기준) 근거: kingsejong-examples 를 쓰면서, 그리고 아래 각 항목의 짧은 프로그램을 kingsejong 으로 직접 실행해서 확인했다.
1. 목표와 판단 기준
libc 는 문자열(string.h), 문자 분류(ctype.h), 변환·정렬·난수(stdlib.h), 입출력(stdio.h), 수학, 시간으로 이루어진다. 이 함수들 대부분은 다음 다섯 가지 언어 기능만 있으면 언어 자신으로 쓸 수 있다.
- 문자열의 글자 하나를 읽고, 글자와 코드 값을 오간다 (
s[i],ord/chr) - 반복을 중간에 끝내거나 건너뛴다 (
break/continue) - 정수의 비트를 다룬다 (
& | ^ ~ << >>, 16진수 리터럴) - 정수 연산의 경계가 정의되어 있다 (오버플로가 어떻게 되는지)
- 비교 함수를 받는 정렬, 컨테이너 크기 같은 기본 연산이 모든 컨테이너에 있다
아래 2장은 이미 고친 것, 3장은 빠진 기능, 4장은 사용자가 정해야 하는 언어 설계 문제다.
2. 예제를 쓰며 찾아 고친 문제
| PR | 문제 | 고친 방향 |
|---|---|---|
| #95 | 배열·딕셔너리 == 가 늘 거짓(a == a 도), 3 <= 3.0 이 거짓 | 내용 비교, 정수·실수는 크기 비교, 순환 참조 안전 |
| #96 | i가 1부터 n까지 가 구문 오류 (짧은 영문·숫자로 끝나는 이름) | 형태소 분석의 바이트 길이 가정 수정 |
| #97 | 깊은 재귀가 segfault 로 죽음 (macOS 기본 스택에서 깊이 약 5500) | OS 스택 한도 근처에서 실행 오류로 멈춤 |
| #98 | 린터 unused-import, no-unused-parameter 오탐 | 모듈 파일을 읽어 판단, 클로저 안까지 확인 |
| #99 | 문서가 실제 동작과 다름 (조건의 진리값, 확장의 린터 설정) | 실제 동작대로 기술 |
| #101 | 반복을 중간에 끝낼 방법이 없음 | 중단 / 계속 추가 (평가기·VM) |
3. 빠진 기능
각 항목의 "확인" 은 실제로 돌린 코드와 결과다. "호환성" 은 기존 프로그램이 달라지는지다.
3.1 반복 제어 (break / continue) — 완료 (#101)
- 결과:
중단/계속을 추가했다. 평가기와 VM 이 같은 결과를 낸다(tests/LoopControlTest.cpp). - 남은 제약: VM 은
마지막블록 안의중단·계속을 컴파일 오류로 거부한다(평가기는 지원). - 호환성:
중단·계속은 이제 변수 이름으로 쓸 수 없다. 저장소의stdlib/,examples/에는 식별자로 쓰는 곳이 없었다.
3.2 문자열 글자 접근과 코드 값
- 확인:
"안녕"[0]→배열 또는 딕셔너리가 아닌 값에 인덱스 접근. 글자↔코드 값 내장 함수가 없다(내장 함수 전체 목록에서 확인). - 필요한 이유:
isdigit,toupper,atoi, 해시, UTF-8 처리. 지금은부분문자열(s, i, i + 1)로 꺼내야 하고, UTF-8 이라 매번 앞에서부터 세므로 문자열 한 번 훑기가 O(n²) 이다. - 제안: 읽기 전용
s[i](글자 단위, 음수 인덱스는 배열과 같게),문자코드(글자),코드문자(정수). 문자열은 지금처럼 바꿀 수 없게 둔다(s[0] = "x"는 지금도 오류). - 호환성: 지금 오류인 식이 값을 갖게 되는 것뿐이라 기존 프로그램은 바뀌지 않는다. 내장 함수 이름 두 개를 새로 쓴다.
3.3 비트 연산과 숫자 리터럴
- 확인:
5 & 3→ 렉서가&를ILLEGAL로 읽는다.0xFF,0b101,1e10→ 구문 오류. - 필요한 이유: 해시, 플래그, Base64·UTF-8 인코딩, 체크섬. 지금
stdlib/crypto의 해시는 C++ 내장 함수에 기대고 있다. - 제안:
&,|,^,~,<<,>>와0x,0b, 지수 리터럴. 정수에만 허용한다. - 호환성: 한 글자
&,|는 지금 쓸 수 없는 문자라 충돌이 없다.&&,||는 그대로다.
3.4 정수 오버플로 — 완료
- 확인:
9223372036854775807 + 1→-9223372036854775808. 평가기가int64_t덧셈을 그대로 하므로 C++ 에서는 정의되지 않은 동작이었다. - 결과: 4.2 의 결정대로 넘치면 실행 오류, 감싸 돌기는
감싸더하기·감싸빼기·감싸곱하기로 한다. 함께 정의되지 않은 동작이던제곱·정수·반올림·올림·내림·절대값의 범위 밖 변환과, 끝 값이 정수 끝인 범위 반복문도 고쳤다(tests/IntegerOverflowTest.cpp).
3.5 컨테이너 기본 연산
| 확인 | 결과 | 제안 |
|---|---|---|
길이({"a": 1}) | 길이() 함수는 문자열 또는 배열 타입이어야 합니다 | 딕셔너리는 키 개수 |
[3, 1, 2]를 정렬한다(함수(a, b) { 반환 b - a }) | 알 수 없는 배열 메서드 '정렬한다' | 비교 함수를 받는 정렬 (qsort 대응) |
각각 (토큰은 Token.cpp 에 있음) | 'GAKGAK'으(로) 시작하는 표현식을 파싱할 수 없습니다 | 파서에 연결하거나 토큰을 지운다. docs/PLAYGROUND_TUTORIAL.md:626 이 동작하지 않는 숫자들을 각각(...) 을 예로 든다 |
3.6 프로그램 바깥과의 경계
- 명령줄 인자: 내장 함수가 없다(
명령줄_인자()→ 정의되지 않음).main(argc, argv)대응이 필요하다. - 표준 오류 출력:
출력()은 표준 출력뿐이다. 오류 메시지를 따로 내보낼 수 없다. - 바이트 단위 입출력: 파일 함수는 문자열 단위다. 바이너리 파일을 읽고 쓸 수 없다.
- 종료 코드:
프로세스_종료(코드)가 있다. 결함 없음.
3.7 성능 (뒤로 미룸)
- 재귀 깊이는 #97 이후 약 5000 에서 실행 오류로 멈춘다. 트리를 걸으며 평가하는 구조라 호출 하나가 C++ 프레임 여러 개를 쓴다.
부분문자열로 글자를 꺼내는 루프는 O(n²) 이다 (3.2 가 해결한다).- 성능 개선은 위 기능을 갖춘 뒤에 다룬다.
4. 정해야 하는 언어 설계 문제
아래는 정답이 하나가 아니라서 구현 전에 정해야 한다. 각 항목은 지금 동작, 후보, 영향을 적었다.
4.1 반복 제어 키워드 이름 — 결정: 중단 / 계속
4.2 정수 오버플로 정책 — 결정: 실행 오류 + 감싸 도는 내장 함수
- 결정 전: 감싸 돌기처럼 보이지만 실제로는 C++ 의 정의되지 않은 동작이었다.
- 고른 것: 기본 산술은 넘치면
시도로 잡을 수 있는 오류, 감싸 돌기가 의도인 계산은감싸더하기·감싸빼기·감싸곱하기. 실수로 넘친 값을 조용히 넘기지 않으면서 해시도 비트 마스크 없이 쓸 수 있다. - 고르지 않은 것: 오류만 두는 안(해시마다 마스크가 필요), 감싸 돌기를 정의하는 안(실수로 넘친 값을 놓친다).
4.3 조건의 진리값 — 결정: 엄격
- 결정 전: 의미 분석기가 타입을 아는 식은 논리 값만 받고(
만약 ("")은 오류), 변수는 실행 중에 진리값 규칙("",0은 거짓)을 썼다. 같은 값이라도 변수에 담느냐에 따라 결과가 달랐다. - 고른 것: 실행 중에도
만약·반복조건과when가드가 논리 값이 아니면 오류. 결정 전 측정에서 저장소의.ksj73개와 kingsejong-examples 20개 중 논리 값이 아닌 조건을 쓴 곳은 없었다. - 고르지 않은 것: 의미 분석기 검사를 경고로 낮추는 안(기존 코드는 모두 돌지만 실수를 덜 잡고, 나중에 엄격하게 바꾸면 그때 호환이 깨진다).
- 범위 밖:
!,그리고,또는는 진리값 규칙을 그대로 쓴다.
4.4 반복문 안 클로저가 잡는 변수 — 결정: 회차마다 새 환경
- 결정 전:
i가 1부터 3까지 { f = 함수() { 반환 i } ... }로 모은 함수가 모두3을 돌려줬다. 반복 변수와 본문 변수를 모든 회차가 공유했다(평가기·VM 같음). - 고른 것: 회차마다 새 환경. 반복 변수와 본문에서 처음 만든 변수가 회차마다 따로 생기고 반복문 뒤에는 없다. 의미 분석기가 이미 본문 변수를 반복문 범위로 다루고 있어 실행 동작이 그 규칙과 맞게 됐다.
- 고르지 않은 것: 반복 변수만 새로 만드는 안(Go 1.22, 규칙이 두 갈래), 지금처럼 공유하는 안(버그 원인이 남는다).
- 비용: 30만 회 반복 두 개를 도는 측정에서 평가기 0.10초 → 0.14초, VM 0.10초 → 0.16초.
5. 진행 순서 제안
- 기존 프로그램을 바꾸지 않는 추가 (결정 불필요): 3.2 글자 접근과 코드 값, 3.3 비트 연산과 리터럴, 3.5
길이(딕셔너리)와 비교 함수 정렬, 3.6 명령줄 인자·표준 오류. - 새 키워드 (4.1 결정 후): 반복 제어,
각각. - 의미가 바뀌는 것 (4.2–4.4 결정 후): 오버플로, 진리값, 클로저 캡처. 바뀌는 동작은
CHANGELOG.md와 마이그레이션 문서에 적는다. - 각 단계마다 kingsejong-examples 에 그 기능을 쓰는 레슨을 더하고, 마지막에
stdlib/의 일부(예: 문자 분류, 문자열 함수)를 KingSejong 코드로 다시 써서 기능이 충분한지 확인한다.