(* 문법 문서의 어휘 절을 코드에서 생성한다. 키워드 표와 "줄을 끝낼 수 있는 토큰" 목록은 지금까지 token.ml과 grammar.ebnf 양쪽에 손으로 적혀 있었다. 그런 목록은 어긋난다 — 실제로 이 프로젝트의 문법 문서 전체가 그렇게 어긋났다. 여기서 생성하고 테스트가 대조하므로, 이제 토큰을 추가하면 문서가 낡거나 빌드가 깨진다. 사람이 두 곳을 맞출 의무가 없다. *) let display (k : Token.kind) = match k with | Token.Ident _ -> Some "ident" | Token.Int _ -> Some "int_lit" | Token.Str _ -> Some "string_lit" | Token.Newline | Token.Eof -> None | _ -> Some ("\"" ^ Token.show_kind k ^ "\"") let is_keyword (k : Token.kind) = Token.keyword (Token.show_kind k) = Some k let keywords () = List.filter_map (fun k -> if is_keyword k then Some ("\"" ^ Token.show_kind k ^ "\"") else None) Token.all_kinds let statement_enders () = List.filter_map (fun k -> if Token.can_end_statement k then display k else None) Token.all_kinds let begin_mark = "(* 여기부터 lib/token.ml에서 생성됩니다 — 손으로 고치지 마십시오 *)" let end_mark = "(* 생성 끝 *)" (* 한 줄이 길어지지 않게 접는다. 구분자는 줄 끝에 남겨 이어짐이 보이게 한다 *) let wrap ~indent items = let rec go acc line = function | [] -> List.rev (line :: acc) | x :: rest -> if line = indent then go acc (line ^ x) rest else if String.length line + String.length x + 3 > 72 then go ((line ^ " |") :: acc) (indent ^ x) rest else go acc (line ^ " | " ^ x) rest in String.concat "\n" (go [] indent items) let render () = String.concat "\n" [ begin_mark; " * 줄을 끝낼 수 있는 토큰. 줄의 마지막 토큰이 이 중 하나이면 그 줄 끝에"; " * NEWLINE이 삽입된다. Token.can_end_statement가 원본이다."; " *"; wrap ~indent:" * " (statement_enders ()); " *"; " * 키워드. 이름으로 쓸 수 없다. Token.keyword가 원본이다."; " *"; wrap ~indent:" * " (keywords ()); " " ^ end_mark; ]