마고(Margo)는 C ABI에 자연스럽게 연결되면서도 더 빠른 프로토타이핑 경험을 주기 위한 실험적 언어다. concept.margo 초기 사양에는 다음과 같은 축이 제시되어 있다.
- C-Native / Zero-Grief: C와 완벽히 호환되는 타입 레이아웃과 스코프 기반 자동 해제(RAII) 철학.
- 타입 시스템:
auto타입 추론,string편의 타입,weird(T, n)포인터 축약 구문. - 제어 흐름: 암시적 루프 변수, 조건부 증감식 같은 하이브리드 문법.
- 지시자/도구:
@import,@style,@set autocorrect등 메타 프로그래밍 지시자.
| 경로 | 설명 |
|---|---|
concept.margo |
사양 초안 문서. |
README.md, README.en.md |
국/영문 개요 문서. |
docs/spec-outline.md |
사양의 구현 관점 요약. |
TODO.md |
구현 단계별 작업 목록. |
deps/ |
외부 실험용 라이브러리(libttak 등). |
examples/number_baseball/ |
fn/@import만으로 작성한 첫 실행 예제. |
tutorials/ |
주제별 실습 세트. 각 폴더가 문법/함수를 설명하는 Markdown·예제 코드를 포함. |
1차 목표는 사양을 문서화하고 구현 단계를 정의하는 것이다. TODO.md에 다음 단계가 정리되어 있으며, 완료된 항목에는 [v]를 표시한다.
Margo가 자기 자신을 컴파일하기 위해 필요한 최소 기능들이 구현되었다.
alloc / alloc_and_init으로 할당된 변수는 블록({}) 종료 시 컴파일러가 자동으로 free()를 삽입한다.
fn process() {
string buf = alloc_and_init(256, "hello")
// ... 처리 로직 ...
} // ← 여기서 free(buf)가 자동 삽입됨
return 문 전에도 모든 소유 변수에 대해 free()가 주입된다. 반환되는 변수(소유권 이전)는 제외.
C의 NULL로 직접 변환된다:
if ptr == null { ... }
모든 표준 C 헤더를 한 번에 가져온다. 컴파일러 구현 같은 시스템 프로그램 작성 시 유용하다:
@import godmode
C++ 바인딩 지원은 향후 마일스톤이다. 지시자는 주석으로 보존된다.
| 지시자 | 매핑 |
|---|---|
@import std/io |
<stdio.h> |
@import std/mem |
<string.h> |
@import std/string |
<string.h> |
@import std/math |
<math.h> |
@import std/stdlib |
<stdlib.h> |
@import std/time |
<time.h> |
@import std/assert |
<assert.h> |
@import std/errno |
<errno.h> |
@import std/file |
<stdio.h> |
@import file/core |
<stdio.h> + 파일 패턴 검색 헬퍼 |
@import network/core |
POSIX socket 헤더 + 네트워크 I/O 헬퍼 |
@import threads/core—threads <name> { locked_var { ... } threadN ... }문법을 지원하는 헤더 온리 런타임이다.threadN.init(...),threadN.join(),threads.<cluster>.join_all(chan),threads.<cluster>.sync()가 일관된 API로 노출되며, 추가 데코레이터 없이@chantype,@nochan,@independent,@lazyjoin만 허용한다.@import process/@import process/core—process <name>블록과chan T기반 IPC를 묶은 런타임. 프로세스는spawn/wait_all/supervise조합만 제공하며, 채널 송수신은 Go 스타일chan <- value,value <- chan구문으로 단순화된다.- 두 런타임을 묶어 보여주는 최초의 벤치는
examples/thread_process_bench에 포함된다. examples/matrix_astar는matrix/core의 기초 연산과mat_for/mat_neighbor구문을 활용해 A* 경로 탐색을 행렬 기반 상태 기계로 표현한다.
컴파일 전에 토큰 스트림을 분석한다:
- 함수 시그니처 레지스트리: 모든
fn선언 기록, 호출 위치에서 인자 수 검증. weird차원 검증: 포인터 차원이 [0, 8] 범위를 벗어나면 오류.- RAII 소유권 테이블:
ident = alloc(...)패턴 감지, 트랜스파일러에 전달. - 다중 오류 누적: 첫 번째 오류에서 멈추지 않고 모든 오류를 수집해 한꺼번에 출력.
build/margo 바이너리는 .margo 소스에서 지시자(@import, @set 등)만 얇게 정규화한 뒤, 나머지 코드를 그대로 LLVM clang 프런트엔드로 넘겨 IR 혹은 네이티브 바이너리를 만든다. 추가 구문(fn, auto, weird)은 런타임 전처리 매크로로 확장되므로 별도의 C 소스 파일이 생성되지 않는다. 현재 지원되는 축약 문법은 다음과 같다.
@import c/<header>→#include <header>fn [return_type] name(args) { ... }→ C 함수 시그니처 (return_type생략 시int)string편의 타입 (typedef char* string;로 치환)auto→__auto_type,weird(T, n)→T+n개의*
make # build/margo-c(bootstrap) + self-compiled build/margo 생성
./build/margo build examples/number_baseball/number_baseball.margo -o number_baseball
./number_baseball--clang <경로> 또는 CLANG=<경로> 환경변수로 원하는 LLVM 툴체인을 지정할 수 있으며, IR을 확인하고 싶다면 --emit-llvm number_baseball.ll 옵션을 추가하면 된다.
experiment/margo_compiler.margo는 이제 @style c로 전체 컴파일러 코어를 포함하므로,
이 파일에서 생성된 바이너리는 bootstrap 실행 파일에 위임하지 않고도 스스로를 다시 빌드할 수 있다.
make 기본 타깃은 셀프 컴파일을 포함하며, 아래 명령은 같은 과정을 명시적으로 실행한다.
make margo-in-margo생성 결과:
margo_build/margo_stage1—build/margo-c로 빌드한 1단계margo_build/margo—margo_stage1이 다시 빌드하고 한 번 더 재검증한 최종 단계build/margo— 기본make가 배치하는 self-compiled 최종 바이너리build/margo-c— C로 빌드한 bootstrap 컴파일러
print(value1, value2, ..., sep = \" \", endl = \"\\n\")
_Generic기반으로 bool/정·부호 정수/부동소수/문자/문자열/포인터를 자동 감지해 출력한다.sep/endl인자는 선택적으로 덮어쓸 수 있으며, positional 인자 뒤에 반드시 위치해야 한다.Scan(&int_var)/ScanLine(buffer)
@import std/io가 포함된 번역 단위에서만 사용할 수 있다.Scan은 공백 단위 토큰을 읽고 성공적으로 파싱한 인자 수를 반환하며,ScanLine은 개행까지 읽는다. 여러 인자를 넘기면 각각의 타입에 맞는%d,%f,%s등이 자동으로 연결된다.alloc(size)/alloc_and_init(size, literal)
우선 libttak 메모리 할당을 시도하고, 실패/불가 조건에서는 abstract memory fallback(내장 힙 폴백)으로 자동 전환한다.
컴파일러는alloc계열 대입 변수를 소유 객체로 추적해 스코프 종료/return직전에 자동 해제를 주입한다.
해제 시에는 통합 free 훅이 할당 원천(libttak/fallback)을 판별해 안전하게 정리한다.seek_from_file(stream, pattern),pos_from_file(stream, pattern),jmp_from_file(stream, pattern),return_all_bitmask_offsets(stream, pattern)
@import file/core를 통해 노출되며,FILE *스트림 안에서 바이트 패턴을 검색하거나 (존재 여부/오프셋) 확인하고, 필요 시fseek으로 점프한다.return_all_bitmask_offsets는 전체 파일을 한 번 훑어 패턴이 시작되는 모든 위치를 bool 마스크로 돌려주며,[0x7F, 'E', 'L', 'F']처럼 정적 바이트 배열 리터럴을 즉시 인자로 넘길 수 있다. 컴파일러가 자동으로 상수 버퍼를 생성하고hits.indices()같은 도우미도 제공한다.margo_file_read,margo_file_write
파일 접근은 공통 래퍼를 통해 수행되며, 파일 읽기/쓰기에 대한 일관된 API를 제공한다.margo_net_tcp_connect,margo_net_send,margo_net_recv,margo_net_close
@import network/core와 함께 네트워크 소켓 I/O를 위한 기본 연결/송수신/종료 경로를 제공한다.
examples/number_baseball은 위 빌드 플로우를 자동화한 Makefile을 포함한다. fn, @import, auto/weird 등의 문법만 사용하며 @style c 블록이 없다.
cd examples/number_baseball
make
./number_baseball이 경로는 “순수” .margo 파일을 직접 빌드 가능한 첫 사례다.
margo 전면부는 이제 토큰화/파서 단계를 거쳐 추가 문법 설탕을 이해한다. 현재 프로토타입에서는 다음과 같은 변환을 지원한다.
@import c/stdio처럼.h확장자를 생략한 C 헤더 포함 (자동으로.h를 붙인다).for idx=0; <N; ++ { ... }형태의 암시적 루프 문법을 정규 Cfor헤더로 재작성한다. 초기화식에서 추출한 변수 이름을 조건/증감식의 생략된 식별자 앞에 자동으로 붙여준다.if v == 1 return 100처럼 괄호를 생략한 조건문은 조건식을 자동으로()로 감싸고, 이후에 이어지는 문장이나 블록 앞의 공백도 그대로 보존한다.- 함수 앞에 붙는
@decorator토큰은 주석으로 치환되어 C 컴파일러가 이해할 수 있도록 정리된다. mat_find(arr, target, precision) { ... }와mat_for(arr, fn_name)는 행렬 차수를 직접 명시하지 않아도 전체 배열을 순회하거나 근사값을 찾을 수 있는 고수준 행렬 전용 구문이다. 일치 지점을 찾았을 때만 블록이 실행되며, 복수 차수 인덱싱은 언어가 자동으로 관리하고, 내부적으로는 선형대수학식 평탄화를 이용해 캐시 지역성을 확보한다.mat_neighbor()는mat_for안에서 암시적으로 현재 좌표의 반경 1 이웃을, 스코프 밖에서는mat_neighbor(arr, i, j, ..., n | seek_fn = fn)형태로 임의 반경/필터를 적용해 안전하게 잘린 이웃 뷰를 돌려준다.[0x01, 0x02, ...]형태의 바이트 패턴 리터럴은 별도 선언 없이 정적uint8_t버퍼로 승격되어 파일 패턴 헬퍼나 기타 저수준 API에 즉시 전달할 수 있다.for x in [1, 2, 3, 4] { ... }처럼 Python 스타일의 리스트 열거 구문을 지원한다. 내부적으로는 숨김 상수 배열을 만든 뒤 기존for문으로 내린다.
숫자 야구 예제는 위 문법을 사용해 콘셉트 문서와 유사한 스타일을 미리 체험할 수 있다.
matrix_fill(rows, cols, value)는 지정한 차수의 새로운auto_matrix를 만들어 모든 셀을value로 채운다. 결과는 일반 2차원 배열처럼grid[i][j]로 수정할 수 있으며,mat_for나mat_neighbor와도 호환된다.matrix_identity(size, diag_value = 1)는 주어진 크기의 단위 행렬을 곧바로 생성한다. 변환/회전 행렬을 빠르게 조합하거나 그래프 가중치를 정규화할 때 사용한다.matrix_mul(lhs, rhs)는 행렬 곱셈을 수행해 새 행렬을 반환한다.lhs열수와rhs행수가 맞지 않으면 진단을 발생시킨다.matrix_transpose(matrix)는 데이터를 복사하지 않고 축을 뒤집은 뷰를 돌려주며, 이후 곱셈/맵핑 함수에 그대로 전달할 수 있다.matrix_map(matrix, fn mapper)는 동일 차수의 새 행렬을 만들고 각 셀을 사용자 정의 변환 결과로 치환한다.mapper는(mat_neighbor_cell cell) -> auto서명을 따르며,cell.value와cell.index를 모두 참고할 수 있다.matrix_rows,matrix_cols,matrix_get/matrix_set,matrix_neighbor(...)/neighbor_view_for_each(...)같은 런타임 도우미도 제공되어 필요할 때 직접 행렬 요소를 순회하거나 인접 셀을 탐색할 수 있다.
@import file/core
@import std/io
fn dump_zip_offsets(string path) {
weird(FILE, 1) fp = fopen(path, "rb")
if !fp return
bool hits[] = return_all_bitmask_offsets(fp, [0x50, 0x4B, 0x03, 0x04])
for slot in [0, 32, 64, 96] {
print("slot ", slot, ": ", hits[slot])
}
for pos in hits.indices() {
if hits[pos] print("central dir? @", pos)
}
fclose(fp)
}
fn sum_hotspots(auto_matrix field) {
mat_find(field, 10, 0.001) {
print("exact 10 at", mat_index[0], mat_index[1])
}
mat_for(field, fn(i, j, cell) bool {
auto neigh = mat_neighbor()
auto spikes = mat_neighbor(field, i, j, 2, seek_fn = fn(nc) bool { return nc.value > cell })
neigh.for_each(fn(nc) {
if nc.value > cell * 2 print("spike", nc.index[0], nc.index[1])
})
if spikes.count == 0 && cell > 0 {
print("isolated hot cell", i, j)
}
return true
})
}
tutorials/README.md에는 다음과 같은 실습 폴더가 준비되어 있다. 각 폴더는 README.md, Makefile, example.margo를 제공하므로 루트에서 make로 build/margo만 만들어두면 바로 따라 할 수 있다.
| 순번 | 디렉터리 | 주요 주제 |
|---|---|---|
| 01 | tutorials/01_getting_started |
@import, fn start, print로 기본 실행 흐름 구성 |
| 02 | tutorials/02_types_and_strings |
string, auto, weird(T, n)와 같은 타입 편의 기능 |
| 03 | tutorials/03_control_flow |
축약 for/while/if 문법과 증감 패턴 |
| 04 | tutorials/04_memory_management |
alloc, alloc_and_init를 통한 메모리 확보와 점검 |
| 05 | tutorials/05_std_io |
@import std/io 기반의 Scan, ScanLine, print 고급 옵션 |
| 06 | tutorials/06_styles_and_decorators |
@style, @set, 함수 데코레이터 구문 |
- 모든 새로운 개념은 먼저 문서화(
docs/) 후 코드에 반영한다. - 영어 주석을 강제한다.
- LLVM 기반 백엔드를 사용할 계획이므로, IR 설계와 타입/메모리 분석은 이를 염두에 둔다.
자세한 일정과 기능별 요구사항은 TODO.md에서 확인할 수 있다.
