Guidedog 설명서 0.2.0
언어
이 페이지의 내용
Guidedog / 문서 0.2.0

Odin 문서화

Odin 도메인은 패키지, 프로시저, 타입, 멤버에 명명된 대상을 제공한다. Guidedog은 Odin 소스를 파싱해 API 페이지도 생성하지만 컴파일러나 패키지를 실행하지 않는다. 생성된 시그니처에도 유용한 계약이 필요하다.

객체 설명

패키지는 Python 모듈처럼 뒤따르는 내용의 문맥을 정한다.

.. odin:package:: core:strings
   :synopsis: Procedures to manipulate UTF-8 encoded strings.
   :imports: rt=base:runtime

.. odin:procedure:: @(require_results) clone :: proc(s: string, allocator := context.allocator) -> (res: string, err: rt.Allocator_Error) #optional_allocator_error

   Clones a string.

   :param s: The string to be cloned.
   :result res: The cloned string.
   :result err: An allocator error, or ``nil``.

.. odin:struct:: Builder :: struct

   A dynamic byte buffer.

   .. odin:field:: buf: [dynamic]byte

시그니처는 Odin 선언처럼 한 줄로 쓰고 그대로 표시한다. 아래 예제는 다음과 같이 보인다.

shapes.Kind :: enum u8
Circle = 1
@(require_results) shapes.area :: proc(side: f32, scale: f32 = 1) -> (a: f32, ok: bool) #optional_ok

정사각형을 측정합니다.

매개변수:

side – 변의 길이.

반환값:
  • a – 넓이.

  • ok – 종류를 아는지 여부.

지시문과 받는 시그니처:

odin:package와 odin:currentpackage

core:fmt 또는 프로젝트 루트 아래 경로인 readers/sphinx를 쓴다. 객체 이름은 패키지 가져오기 경로를 쓴다(core:fmt.println). odin:currentpackage는 대상 없이 문맥만 바꾸고 None은 해제한다. 옵션은 :synopsis:, :platform:, :deprecated:, :no-index:, :imports:다. 마지막은 alias=package 쌍(gd=core rst=readers/rst)으로 시그니처 별칭을 지정해 gd.Node_Id를 core.Node_Id에 연결한다. :private:는 비공개 타입과 프로시저 그룹의 비공개 멤버(State Rules catalog_text)를 나열한다. 연결할 설명이 없어 텍스트로 표시하며 생성 페이지가 자동으로 목록을 만든다.

odin:procedure (또는 odin:proc)

name :: proc(params) -> results에 이름 앞 속성(@(require_results)), proc 앞 #force_inline, 호출 규약(proc "c" (...)), 기본 인수(x := 1, x: int = 1), $T 다형 인수, .. 가변 인수, using, #c_vararg, 이름 있는 결과, 태그(#optional_ok), where 절을 지원한다. name(params) -> results는 축약형이다.

odin:procgroup

name :: proc{a, b}: 각 멤버는 해당 프로시저로 연결된다.

odin:struct, odin:union, odin:enum, odin:bitset, odin:bitfield, odin:type

Name :: struct($T: typeid) #packed, Name :: union #no_nil {A, B}, Name :: enum u8, Name :: bit_set[Flag; u8], Name :: bit_field u32를 쓴다. odin:type에는 구별 타입과 별칭(Handle :: distinct uintptr, Callback :: proc(x: int) -> bool)을 쓴다. 타입 내용에는 멤버가 들어간다.

odin:field와 odin:enumerator

타입 안에서는 name: Type에 태그(name: string `json:"n"`), 비트 크기(low: u8 | 3)를 붙인다. 열거자는 Name 또는 Name = 3이다. 이름은 Type.member다.

odin:const, odin:var, odin:foreign

NAME :: 64 또는 NAME : int : 64; name: Type, name := value 또는 name: Type = value; 외부 가져오기는 libc "system:c"이다.

모든 객체는 Sphinx의 :no-index:, :no-index-entry:, :no-contents-entry:, :no-typesetting:을 받는다. 추가로 :package:(다른 패키지에서 설명), :private:와 :deprecated: message(시그니처에 없으면 @(private)·@(deprecated="message") 표시), :availability: Windows, Linux(첫 줄에 플랫폼 표시), :foreign: libc(외부 블록 프로시저)가 있다. 여러 시그니처 줄은 대상별로 다른 한 객체를 설명하며 첫 줄만 ID와 색인 항목을 얻는다.

타입 위치의 이름은 시그니처의 패키지·타입에서 먼저 찾고 원래 이름으로 찾아 연결한다. 내장 타입(int, string, rawptr, typeid, any 등), 키워드, 다형 이름($T와 이후 T, 외부 타입 포함), 배열 길이, 기본값, 상수, 태그, where 절은 그대로 표시하고 링크하지 않는다.

역할과 ID

:odin:pkg:, :odin:proc:, :odin:type:(구조체·공용체·열거·비트 집합·비트 필드 등), :odin:const:, :odin:var:, :odin:field:, :odin:enumerator:, :odin:obj:(모든 객체)로 링크한다. Python처럼 ~는 마지막 부분만 표시한다(:odin:proc:`~core:fmt.println`는 println). 앞의 .은 이름 끝을 찾는다. 참조의 타입·패키지, 원래 이름, 경로의 :·/ 뒤 순서로 찾는다. fmt.println는 core:fmt.println를, sphinx.Config는 readers/sphinx.Config를 찾아 Odin의 마지막 경로 이름 사용과 같다. :imports: 별칭을 우선한다. default-domain:: odin 또는 primary_domain = "odin"이면 odin:을 생략한다.

객체 ID는 odin-과 전체 이름에 Sphinx의 make_id를 적용한 odin-core-fmt.println, odin-readers-sphinx.Config.docname이다. Python처럼 대소문자와 점을 유지하고 경로의 :·/는 하이픈으로 바꾼다. URL에서 읽기 쉽고 빌드마다 같으며 마지막 이름이 같은 패키지도 구별한다. 패키지 ID는 odin-package-와 경로다. 색인은 "println (procedure in core:fmt)"처럼 표시한다. objects.inv는 odin 도메인과 타입(odin:procedure, odin:struct, odin:field 등)으로 모든 객체를 나열하며 Sphinx 모듈처럼 패키지를 먼저 둔다.

내용의 정보 필드는 다른 도메인처럼 묶는다. :param name:와 :type name:, 이름 있는 결과의 :result name:, :returns:, :rtype:가 있다.

소스에서 생성

소스 폴더 기준 패키지 위치를 지정한다.

odin_autoapi_dirs = ["../src"]
odin_collections = {core = "/usr/local/lib/odin/core"}  # optional

odin_autoapi_dirs 아래 패키지는 상대 경로로 이름을 붙인다(src/shapes/round는 shapes/round). 컬렉션은 Odin 가져오기 이름(core:strings)을 쓴다. 생성 지시문은 autodoc처럼 작동한다.

.. odin:autopackage:: shapes
   :members:
   :undoc-members:
   :member-order: groupwise

.. odin:autoproc:: shapes.area
.. odin:autotype:: Shape

odin:autopackage는 패키지와 문서 주석을 출력하고 :members:로 전체 또는 지정 선언도 출력한다. odin:autoproc, odin:autoprocgroup, odin:autotype, odin:autoconst, odin:autovar는 현재 패키지 이름 또는 package.name의 선언 하나를 쓴다. 옵션은 :members:, :undoc-members:, :private-members:(기본 제외되는 @(private)), :exclude-members:, :member-order:(기본 source는 파일 이름과 위치순, alphabetical, groupwise는 "Types", "Procedures", "Procedure groups", "Constants", "Variables", "Foreign imports"로 분류), :no-index:다.

문서 주석은 선언 바로 위의 // 줄이나 /* */ 블록, 필드·열거자 줄 끝의 주석이다. 일반 텍스트를 다음 규칙으로 reStructuredText로 바꾼다.

  • 문단은 문단으로, - item 행 목록은 목록으로 유지된다.
  • 빈 줄이나 콜론으로 끝나는 줄(Example:) 뒤의 들여쓴 블록은 code-block:: odin이며 Output: 뒤는 text다. 문단 줄 바로 뒤의 텍스트보다 더 들여쓴 줄은 문단을 이어 간다.
  • Inputs: 뒤 - name: text는 :param name: 필드, Returns: 항목은 :result name:(이름이 없으면 :returns:)이 된다. Odin 코어 라이브러리 형식을 따른다.
  • NOTE: 또는 WARNING:로 시작하는 줄은 참고 또는 경고가 된다.
  • `code`는 리터럴 텍스트다. reStructuredText가 마크업으로 읽는 나머지 문자(*, |, 단어 끝 _ 등)는 이스케이프하므로 주석이 그대로 보이며 경고하지 않는다.

API 페이지

odin_autoapi_dirs를 설정하면 각 패키지에 sphinx-autoapi처럼 읽기 중 페이지를 생성한다. api/shapes에는 odin_autoapi_options의 odin:autopackage:: shapes가, api/index에는 패키지와 요약이 있다. toctree, 검색, 전체 색인, singlehtml, PDF에 포함되는 프로젝트 문서지만 소스 폴더에는 쓰지 않는다. 설정은 다음과 같다.

odin_autoapi_dirs

페이지를 만들 패키지 폴더이며 소스 기준 경로다. 이 폴더와 odin_collections 아래의 파일만 읽고 다른 곳으로 향하는 링크는 거부한다.

odin_collections

core:strings 같은 지시문 인수용 컬렉션 이름과 폴더 표다. 이 패키지에는 페이지를 만들지 않는다.

odin_autoapi_root

페이지 폴더이며 기본값은 "api"다. 같은 이름의 소스 문서가 있으면 그 문서를 유지하고 경고한다.

odin_autoapi_options

기본값은 ["members", "undoc-members"]이며 private-members를 추가할 수 있다.

odin_autoapi_member_order

"source"(기본값), "alphabetical" 또는 "groupwise".

odin_autoapi_add_toctree_entry

true(기본값): api/index를 루트 문서의 첫 toctree에 추가한다.

odin_autoapi_generate_api_docs

true(기본값). false면 지시문을 위해서만 폴더를 읽는다.

문서화하지 않는 패키지의 타입(core:io.Writer)은 링크할 수 없다. -n에서는 nitpick_ignore_regex = [["odin:type", "(core|base):.*"]]로 억제한다. Guidedog의 docs/api도 이 방식이다.

플랫폼

Odin과 같은 파일 판단으로 패키지의 빌드 대상을 문서화한다. *_windows.odin, *_linux_arm64.odin, *_amd64.odin은 해당 대상만 포함한다. #+build linux, darwin, #+build !windows(구형 //+build도)가 범위를 좁힌다. 파일의 when ODIN_OS == .Windows가 ODIN_OS·ODIN_ARCH만 비교하면 각 분기와 else를 대상별로 구분한다. *_test.odin과 #+ignore는 제외한다. 전체 공통 선언은 한 번, 일부 대상은 "Availability: Windows"로 표시한다. 다른 시그니처는 각각 대상과 함께 보여 주고 첫 항목만 ID를 갖는다. 일부 대상만 지원하는 패키지는 :platform:로 밝힌다.

다시 빌드하기

읽은 Odin 파일은 페이지의 의존성이다. 소스 수정 뒤에는 해당 패키지를 설명하는 페이지만 다시 읽는다. 패키지 추가·삭제·요약 변경 시 색인을 다시 읽는다. guidedog serve는 HTML 제공 전에 odin_autoapi_dirs와 odin_collections의 수정 시간을 확인한다. 편집 뒤 브라우저를 새로 고친다.

한계

  • 소스는 파싱만 하며 컴파일·타입 검사는 하지 않는다. 타입과 상수 값은 그대로 표시하고 100자를 넘는 값은 생략하며 추론하지 않는다. A :: B의 타입·상수 여부는 이름으로 판단한다. 전체 대문자가 아닌 대문자 시작 이름이나 내장 타입이면 타입이다.
  • when 조건은 ODIN_OS와 ODIN_ARCH 비교 외에는 평가하지 않고 양쪽 분기를 모두 문서화한다.
  • 비공개 타입을 참조한 선언은 텍스트로 표시한다. :private-members:가 없으면 해당 항목이 없기 때문이다. 생성 패키지는 :private:로 나열하므로 -n에서도 보고하지 않는다.
  • core:odin/parser는 #optional_ok와 where 절을 함께 쓰는 프로시저를 거부한다. 해당 파일을 보고하고 읽을 수 있었던 부분은 문서화한다.
  • Odin 파서는 깊은 중첩에 많은 스택을 쓰며 수백 괄호로 스레드 스택이 넘칠 수 있다. 실제 코드보다 훨씬 깊은 파일(괄호·타입 약 50단계, else 사슬 약 천 개)은 odin.autodoc.nesting 경고로 제외하고 나머지 패키지는 문서화한다. when의 &&, ||, !는 64단계까지 평가하며 그 이상은 미평가다.