Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
199 commits
Select commit Hold shift + click to select a range
226dd86
Update lib
owjs3901 May 9, 2026
46695f7
Fix override
owjs3901 May 10, 2026
f3494e0
Rm override
owjs3901 May 10, 2026
c9bc713
Fix testcase
owjs3901 May 10, 2026
e1d2aa6
Fix testcase
owjs3901 May 10, 2026
1e3d2a4
Fix context issue
owjs3901 May 11, 2026
d6fddae
Add Korean+math mixed expression support (PDF 제6항 [붙임])
owjs3901 May 11, 2026
9a9836a
Generalize fraction + repeating-decimal algorithms, fix testcase syntax
owjs3901 May 11, 2026
f935491
Fix rulw 28
owjs3901 May 12, 2026
d68befb
Apply UEB 'part' contraction + relax `-` terminator rule (rule_35)
owjs3901 May 12, 2026
65cd0de
Remove 분철 (line-break) markers from rule_35.json testcase
owjs3901 May 12, 2026
25b238f
Add UEB pdf
owjs3901 May 12, 2026
3e182f2
Fix math_16.json input to match PDF math 제16항 본문
owjs3901 May 12, 2026
032a289
Update testcase
owjs3901 May 12, 2026
28c6460
Fix math issue
owjs3901 May 12, 2026
584b598
Update AGENTS.md
owjs3901 May 12, 2026
07cc634
Fix testcase issue
owjs3901 May 12, 2026
ca21e1a
Update testcase
owjs3901 May 12, 2026
ed7d796
Update testcase
owjs3901 May 12, 2026
611d3cd
Update math 15
owjs3901 May 12, 2026
2d32d7f
Fix testcase
owjs3901 May 12, 2026
69771c1
Update math_11.json
owjs3901 May 13, 2026
af20571
Fix math issue
owjs3901 May 13, 2026
71edba2
Update rule
owjs3901 May 13, 2026
68ed6dd
Fix testcase
owjs3901 May 14, 2026
af08ab3
Update testcase
owjs3901 May 14, 2026
a344e88
Add testcase
owjs3901 May 14, 2026
27c7a2c
Update testcase
owjs3901 May 14, 2026
61c7dec
Improve
owjs3901 May 14, 2026
b31e18a
Add testcase
owjs3901 May 14, 2026
4c20621
Update issue
owjs3901 May 14, 2026
3d3164e
Update
owjs3901 May 14, 2026
b7897e8
Fix testcase
owjs3901 May 14, 2026
8371dff
Update
owjs3901 May 14, 2026
02f7bb6
Fix bug
owjs3901 May 14, 2026
c20d4a0
Update testcase
owjs3901 May 15, 2026
578fb40
Fix english
owjs3901 May 15, 2026
9a2fcf6
Fix en issue
owjs3901 May 15, 2026
880f2c5
Update testcase
owjs3901 May 15, 2026
c1d9b39
Fix testcase
owjs3901 May 15, 2026
e9bf1da
Fix testcase
owjs3901 May 15, 2026
fdeedb1
Fix math issue
owjs3901 May 15, 2026
3f1818f
Support latex
owjs3901 May 15, 2026
dd62ec4
Update testcase
owjs3901 May 15, 2026
ff3d6d1
Fix testcase
owjs3901 May 15, 2026
7be43b3
Fix math issue
owjs3901 May 15, 2026
4e379aa
Update testcase
owjs3901 May 15, 2026
5a988d7
Update testcase
owjs3901 May 18, 2026
7343cd4
Update rule_68.json
owjs3901 May 18, 2026
9b5db58
Update math_18.json
owjs3901 May 18, 2026
fb2e1ef
Update math_21.json
owjs3901 May 18, 2026
9fa7216
Update math_22.json
owjs3901 May 18, 2026
fe48adb
Update math_24.json
owjs3901 May 18, 2026
405e10d
Update math_25.json
owjs3901 May 18, 2026
9de6c57
Update math_48.json
owjs3901 May 18, 2026
5c2cf1e
Update rule_45.json
owjs3901 May 18, 2026
7da6c3b
Update math_10.json
owjs3901 May 18, 2026
3b9625b
Update math_2.json
owjs3901 May 18, 2026
6d948ea
Update math_4.json
owjs3901 May 18, 2026
14c0019
Update math_12.json
owjs3901 May 18, 2026
32858c3
Create math_12_b1.json
owjs3901 May 18, 2026
03e06cd
Update math_6.json
owjs3901 May 18, 2026
36dc108
Update map and math6
owjs3901 May 18, 2026
5b925af
Update math_7.json
owjs3901 May 18, 2026
f95c57a
Update math_8.json
owjs3901 May 18, 2026
3f757f0
Update math_8.json
owjs3901 May 18, 2026
d410774
Update math_9.json
owjs3901 May 18, 2026
f996f73
Update math_10.json
owjs3901 May 18, 2026
34a8ddc
Update math_11.json
owjs3901 May 18, 2026
5ce5324
Update math 12
owjs3901 May 18, 2026
d79712e
Update math_13.json
owjs3901 May 18, 2026
e248eb3
Update math_14.json
owjs3901 May 18, 2026
338017d
Update math_15.json
owjs3901 May 18, 2026
e83d112
Update math_16.json
owjs3901 May 18, 2026
947a93c
Update math_16.json
owjs3901 May 18, 2026
c86eb43
Update math_17.json
owjs3901 May 18, 2026
1d5f179
Update math_18.json
owjs3901 May 18, 2026
a6cb4f0
Update math_18.json
owjs3901 May 18, 2026
af1f0a3
Update math_19.json
owjs3901 May 18, 2026
9e4d109
Update math_20.json
owjs3901 May 18, 2026
6f18b4f
Update math_20.json
owjs3901 May 18, 2026
f537607
Update math_21.json
owjs3901 May 18, 2026
6a98404
Update math_22.json
owjs3901 May 18, 2026
f5ed518
Update math_23.json
owjs3901 May 18, 2026
ec15d37
Update math_24.json
owjs3901 May 18, 2026
cd2fb04
Update math_25.json
owjs3901 May 18, 2026
8de0479
Update math_26.json
owjs3901 May 18, 2026
f662d82
Update math_27.json
owjs3901 May 18, 2026
2236598
Update math_28.json
owjs3901 May 18, 2026
3e5fe5b
Update math_29.json
owjs3901 May 18, 2026
dbc4c23
Update math_30.json
owjs3901 May 18, 2026
30c6bf5
Update math_31.json
owjs3901 May 18, 2026
8cdeead
Update math_32.json
owjs3901 May 18, 2026
3dc42d6
Update math_33.json
owjs3901 May 18, 2026
7cca459
Update math_34.json
owjs3901 May 18, 2026
7ec0dfc
Update math_35.json
owjs3901 May 18, 2026
3bb4b8f
Update math_36.json
owjs3901 May 18, 2026
ea215b2
Update math_37.json
owjs3901 May 19, 2026
63d650d
Update math_38.json
owjs3901 May 19, 2026
d13c9aa
Update math_39.json
owjs3901 May 19, 2026
f78a518
Update math_40.json
owjs3901 May 19, 2026
1a66d66
Update math_41.json
owjs3901 May 19, 2026
77fee6a
Update math_42.json
owjs3901 May 19, 2026
bd0404d
Update math_43.json
owjs3901 May 19, 2026
afb96d9
Update math_44.json
owjs3901 May 19, 2026
1d7088b
Update math_45.json
owjs3901 May 19, 2026
a0f5d05
Update math_46.json
owjs3901 May 19, 2026
be52e24
Create math_46_b1.json
owjs3901 May 19, 2026
2e36150
Update math_47.json
owjs3901 May 19, 2026
9743914
Update math_48.json
owjs3901 May 19, 2026
bfea829
Update math_49.json
owjs3901 May 19, 2026
75a45ff
Update math_49.json
owjs3901 May 19, 2026
f74453f
Update math_50.json
owjs3901 May 19, 2026
deaa0f4
Update math_51.json
owjs3901 May 19, 2026
14aee4a
Update math_52.json
owjs3901 May 19, 2026
cccca12
Update math_53.json
owjs3901 May 19, 2026
07dc958
Update math_54.json
owjs3901 May 19, 2026
f50badb
Update math_55.json
owjs3901 May 19, 2026
eadaa27
Update math_56.json
owjs3901 May 19, 2026
b4c6f45
Update math_57.json
owjs3901 May 19, 2026
267b779
Update math_58.json
owjs3901 May 19, 2026
69dfa06
Update math_59.json
owjs3901 May 19, 2026
eedd2d5
Update math_60.json
owjs3901 May 19, 2026
7973eba
Update math_61.json
owjs3901 May 19, 2026
5cd2124
Update math_62.json
owjs3901 May 19, 2026
041d053
Update math_63.json
owjs3901 May 19, 2026
9d5a90d
Update math_64.json
owjs3901 May 19, 2026
a9b3829
Update math_65.json
owjs3901 May 19, 2026
28da520
Update math_66.json
owjs3901 May 19, 2026
357335e
Update testcase
owjs3901 May 19, 2026
02c0d4d
Update .gitignore
owjs3901 May 19, 2026
fa49277
Update logic
owjs3901 May 20, 2026
38d75ad
Add trace
owjs3901 May 20, 2026
acbee0a
Update testcase
owjs3901 May 20, 2026
b169ee7
Update testcase
owjs3901 May 20, 2026
92ed36e
Update rule_2.json
owjs3901 May 20, 2026
755bcb1
Update rule_3.json
owjs3901 May 20, 2026
9ca3785
Update rule_6.json
owjs3901 May 20, 2026
ead2bbf
Update rule_8.json
owjs3901 May 20, 2026
239e004
Update rule_9.json
owjs3901 May 20, 2026
3f7b55d
Update rule_10.json
owjs3901 May 20, 2026
f83e491
Update rule_11_b1.json
owjs3901 May 20, 2026
e2a0be7
Update 12
owjs3901 May 20, 2026
fa30706
Update rule_13.json
owjs3901 May 20, 2026
f633b1a
Update rule 14
owjs3901 May 20, 2026
80ef42d
Update rule_14_b1.json
owjs3901 May 20, 2026
811af67
Update rule_15.json
owjs3901 May 20, 2026
b5890c5
Update rule_16.json
owjs3901 May 20, 2026
a799044
Update rule_17.json
owjs3901 May 20, 2026
9de8a59
Update rule_18.json
owjs3901 May 20, 2026
a7bb537
Update rule_18_b1.json
owjs3901 May 20, 2026
69c99ad
Update math_66.json
owjs3901 May 20, 2026
aa20a96
Update rule_28.json
owjs3901 May 20, 2026
44b0851
Update rule_29.json
owjs3901 May 20, 2026
e12df01
Fix rule_33 internal values and remove limitation
owjs3901 May 20, 2026
0690189
Update rule_34.json
owjs3901 May 20, 2026
99cc801
Update rule_35.json
owjs3901 May 20, 2026
d44e0b6
Update rule_42.json
owjs3901 May 20, 2026
0c2b624
Update rule_44.json
owjs3901 May 20, 2026
6069b4e
Update rule_44_b1.json
owjs3901 May 20, 2026
daa2638
Update rule_46.json
owjs3901 May 20, 2026
98f29f7
Update rule_47.json
owjs3901 May 20, 2026
0f222cb
Update rule_48.json
owjs3901 May 20, 2026
98f7751
Update rule_49.json
owjs3901 May 20, 2026
11c7c4a
Update rule_51.json
owjs3901 May 20, 2026
6a931af
Update rule_51_b2.json
owjs3901 May 20, 2026
03e7589
Update rule_52.json
owjs3901 May 20, 2026
34e348d
Update rule_53_b1.json
owjs3901 May 20, 2026
07a4f4e
Update rule_54.json
owjs3901 May 20, 2026
02a37da
Update rule_59.json
owjs3901 May 20, 2026
63e55ce
Add testcase
owjs3901 May 20, 2026
7cf8f4c
Update rule_63.json
owjs3901 May 20, 2026
5e3ea04
Update
owjs3901 May 20, 2026
8fe0aca
Fix case
owjs3901 May 20, 2026
74c29a1
Support more
owjs3901 May 20, 2026
6712602
Update rule_61.json
owjs3901 May 20, 2026
a65c71e
Fix rule 61
owjs3901 May 20, 2026
6625490
Support mark
owjs3901 May 21, 2026
5825382
Update testcase
owjs3901 May 21, 2026
19c84ae
Update en shortform
owjs3901 May 21, 2026
989cb89
Refactor
owjs3901 May 22, 2026
41c2943
Update example and bench
owjs3901 May 22, 2026
994caa5
Update world testcase
owjs3901 May 22, 2026
696789d
Update jeomsarang bench
owjs3901 May 22, 2026
7b4eb00
Update agents.md
owjs3901 May 22, 2026
9e45ec7
Update dep
owjs3901 May 22, 2026
846f6cf
Merge remote-tracking branch 'origin/main' into update-coverage
owjs3901 May 22, 2026
ee044b0
Fix file
owjs3901 May 22, 2026
4cbf5b2
Fix landing
owjs3901 May 22, 2026
0513853
Update fmt
owjs3901 May 22, 2026
f92c140
Update package
owjs3901 May 22, 2026
cbc40e0
Add testcase
owjs3901 May 22, 2026
ae0c2b0
Fix format
owjs3901 May 22, 2026
0026c1e
Split code
owjs3901 May 22, 2026
5c0d2b0
Add testcase
owjs3901 May 22, 2026
8142bee
Update lib
owjs3901 May 22, 2026
fa0e755
Add testcase
owjs3901 May 22, 2026
22cd406
Add testcase
owjs3901 May 22, 2026
5cec95f
Add coverage case
owjs3901 May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
53 changes: 33 additions & 20 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches:
- main
pull_request_target:
pull_request_target:
branches:
- main
permissions: write-all
Expand All @@ -18,10 +18,10 @@ jobs:
fail-fast: false
matrix:
python-version:
- '3.11'
- '3.12'
- '3.13'
- '3.14'
- "3.11"
- "3.12"
- "3.13"
- "3.14"
platform:
- ubuntu-latest
- windows-latest
Expand Down Expand Up @@ -55,8 +55,23 @@ jobs:
run: bun run build
- name: Lint
run: bun run lint
- name: reformat
run: |
echo 'max_width = 100000' > .rustfmt.toml
echo 'tab_spaces = 4' >> .rustfmt.toml
echo 'newline_style = "Unix"' >> .rustfmt.toml
echo 'fn_call_width = 100000' >> .rustfmt.toml
echo 'fn_params_layout = "Compressed"' >> .rustfmt.toml
echo 'chain_width = 100000' >> .rustfmt.toml
echo 'merge_derives = true' >> .rustfmt.toml
echo 'use_small_heuristics = "Default"' >> .rustfmt.toml
cargo fmt
- name: Test
run: bun run test
- name: Format Rollback
run: |
rm -rf .rustfmt.toml
cargo fmt
- name: Upload to codecov.io
uses: codecov/codecov-action@v5
with:
Expand Down Expand Up @@ -177,7 +192,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.14'
python-version: "3.14"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install maturin
Expand Down Expand Up @@ -252,8 +267,8 @@ jobs:
runs-on: ${{ matrix.settings.host }}
env:
# DEBUG: napi:*
MACOSX_DEPLOYMENT_TARGET: '10.13'
CARGO_INCREMENTAL: '1'
MACOSX_DEPLOYMENT_TARGET: "10.13"
CARGO_INCREMENTAL: "1"
steps:
- uses: actions/checkout@v5
- name: Setup node
Expand All @@ -266,7 +281,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.14'
python-version: "3.14"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install maturin
Expand Down Expand Up @@ -304,7 +319,7 @@ jobs:
path: |
packages/node/pkg/*
if-no-files-found: error

# python
python-build:
runs-on: ${{ matrix.runner }}
Expand Down Expand Up @@ -393,7 +408,6 @@ jobs:
name: wheels-${{ matrix.os }}-${{ matrix.target }}
path: packages/python/dist


node-publish:
name: Node Publish
runs-on: ubuntu-latest
Expand All @@ -412,7 +426,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.14'
python-version: "3.14"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install maturin
Expand Down Expand Up @@ -441,10 +455,10 @@ jobs:
# working-directory: packages/node
- name: Publish
run: |
# bun install -g @napi-rs/cli
bun run build
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
npm publish --access public
# bun install -g @napi-rs/cli
bun run build
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
npm publish --access public
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand All @@ -455,7 +469,6 @@ jobs:
# upload_url: ${{ fromJson(needs.changepacks.outputs.release_assets_urls)['packages/node/package.json'] }}
# asset_path: packages/node/pkg/*


python-publish:
name: Python Publish
runs-on: ubuntu-latest
Expand All @@ -475,7 +488,7 @@ jobs:
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-path: 'wheels-*/*'
subject-path: "wheels-*/*"
- name: Publish to PyPI
uses: PyO3/maturin-action@main
env:
Expand All @@ -489,7 +502,7 @@ jobs:
uses: owjs3901/upload-github-release-asset@main
with:
upload_url: ${{ fromJson(needs.changepacks.outputs.release_assets_urls)['packages/python/pyproject.toml'] }}
asset_path: '*/*.whl'
asset_path: "*/*.whl"

upload-assets:
needs: changepacks
Expand Down Expand Up @@ -668,7 +681,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
dotnet-version: "9.0.x"

- name: Download all native artifacts
uses: actions/download-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/testcase-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ on:
permissions:
contents: read
issues: write
pull-requests: write

jobs:
testcase-report:
Expand Down
30 changes: 28 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,35 @@ coverage
cobertura.xml
codecov
codecov.*
*.profraw
.DS_Store
._.DS_Store
**/.DS_Store
._.DS_Store
**/.DS_Store
**/._.DS_Store
.claude
.omc
.omo
.sisyphus/
.audit
trace_out.txt

# Heap profiling output from `cargo bench --bench memory_dhat --features dhat-heap`.
# Generated artifact, not source. Regenerate locally if needed.
dhat-heap.json
**/dhat-heap.json

# wasm-pack post-optimize transient (W11 build pipeline writes pkg/index_bg_opt.wasm
# then renames it over pkg/index_bg.wasm; if interrupted, the temp may linger).
# pkg/ itself is already covered by packages/node/.gitignore.
**/index_bg_opt.wasm

# Playwright MCP output (screenshots, snapshots, console logs).
.playwright-mcp/
**/.playwright-mcp/

# cargo-mutants output directories — generated per-run mutation reports.
# `mutants.toml` (the config) IS tracked; only outputs are ignored.
mutants.out/
mutants.out.old/
**/mutants.out/
**/mutants.out.old/
5 changes: 0 additions & 5 deletions .oxlintrc.json

This file was deleted.

113 changes: 112 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,21 @@ bun test test_cases/ # 테스트케이스 무결성 검증만
"note": "설명 (선택, 동일 input이 여럿이거나 맥락 필요 시에만)",
"internal": "점자 내부표기",
"expected": "브라유셀 인덱스 연결 문자열",
"unicode": "점자 유니코드 문자열"
"unicode": "점자 유니코드 문자열",
"world": "경쟁사(World) 점역 결과 (참고용, 수정 금지)",
"jeomsarang": "경쟁사(점사랑) 점역 결과 (참고용, 수정 금지)"
}
```

### ⚠️ `world` / `jeomsarang` 필드 — 경쟁사 benchmark (NEVER MODIFY, NEVER COMPARE)

- `world`, `jeomsarang`은 **타 업체 점역 프로그램의 결과**를 그대로 보존한 참고용 필드다.
- **braillify의 정답이 아니다.** braillify의 정답은 오직 `unicode` (= `expected`)이며, PDF 규정에 근거한다.
- **절대로 수정하지 않는다.** input/internal을 정정하더라도 `world`/`jeomsarang`은 원본 그대로 둔다.
- **testcase 검수의 기준으로 사용하지 않는다.** `world`/`jeomsarang`이 우리 `unicode`와 다르더라도 testcase 오류 근거가 아니며, 그것들이 틀린 것은 braillify와 무관하다.
- **인코더 정답 비교 대상이 아니다.** `cargo test test_by_testcase`는 인코더 결과를 `expected`/`unicode`와만 비교한다.
- 이 필드의 존재 의도는 외부 점역 결과와의 차이를 관찰하기 위한 **읽기 전용 비교 자료**이지, 별도 지표가 되어서는 안 된다.

### internal → expected/unicode 변환

`braillove-case-collector/converter.py`의 패턴을 따른다:
Expand Down Expand Up @@ -86,6 +97,16 @@ braille: ⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖
}
```

#### ⚠️ 분수는 무조건 LaTeX `\frac{}{}` 표기 (NON-NEGOTIABLE)

수식의 **분수는 반드시 LaTeX `\frac{numerator}{denominator}` 형식만 사용**한다. `/`(슬래시)는 분수 표기와 별개의 표현이며 두 가지를 혼용하지 않는다.

- ✅ `$\frac{e^x-e^{-x}}{2}$` — 분수
- ❌ `(e^x-e^{-x})/2` — 슬래시(분수 아님)
- ❌ `$(e^x-e^{-x})/2$` — LaTeX 안의 슬래시도 분수 아님

`/`는 단순 슬래시 기호 점역으로 처리되며, 분수의 의미를 가지려면 `\frac{}{}`로 명시해야 한다. testcase 작성 시 분수 의미가 있는 모든 표현을 LaTeX로 통일한다. (수학 분수는 한국 점자 점역 시 분모를 분자보다 먼저 점역하므로 `\frac{a}{b}` → `b/a` 점역 결과가 나온다.)

주요 LaTeX 변환:

| 수식 | LaTeX |
Expand All @@ -104,3 +125,93 @@ braille: ⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖
### 대문자 수학 변수

수학 점자에서 대문자 변수(A, B, N 등)를 사용하는 internal은 기본 64셀 패턴에 포함되지 않는다. 이런 엔트리는 `expected`/`unicode`가 빈 문자열이며, 무결성 테스트에서 자동으로 skip된다.

## 구현 원칙

### 일반화 필수, 꼼수 금지 (NON-NEGOTIABLE)

변환 로직은 **PDF 규정에 근거한 일반 알고리즘**으로 작성한다. 테스트 통과가 목적이 아니라, **모든 변형 입력을 규정대로 변환하는 것**이 목적이다.

- 테스트 케이스는 가능한 입력의 작은 부분집합일 뿐이다
- 알고리즘이 옳다면 테스트는 자연히 통과한다
- "테스트와 결과가 다르니 코드를 맞춘다"가 아니라, **테스트와 결과가 다르면 알고리즘 또는 테스트 둘 중 하나가 틀린 것이다**
- 테스트에 없는 새로운 입력이 들어와도 동일한 알고리즘으로 정확히 처리되어야 한다

### 금지된 꼼수 (BLOCKING — 발견 즉시 재작성)

#### 1. 입력 → 출력 직접 매핑

```rust
// 금지
if input == "안녕하세요" { return "⠁⠉⠊..."; }

match input {
"안녕" => "...",
"학교" => "...",
_ => fallback(),
}
```

#### 2. 테스트 케이스 룩업 테이블

```rust
// 금지 — 테스트 입력/출력을 그대로 박아넣은 것
const KNOWN: &[(&str, &str)] = &[
("안녕", "⠁⠉..."),
("학교", "⠚⠁..."),
];
```

#### 3. expected 역산

테스트 JSON의 `expected`/`unicode` 값을 보고 **그 값이 나오도록 코드를 작성하는 것**. 알고리즘은 오직 `docs/2024 개정 한국 점자 규정.pdf`의 규정에서만 도출한다.

#### 4. 테스트 파일 의존

변환 로직이 `test_cases/` 경로의 파일을 읽거나 import하거나 참조하는 코드 일체. 테스트 데이터는 검증 단계에서만 쓰인다.

#### 5. 케이스별 분기 폭증

같은 종류의 처리를 입력 단위마다 if/else 또는 match로 늘어놓는 것. 같은 규정이 적용되는 입력은 **하나의 일반 함수**로 처리한다.

```rust
// 금지 — 음절별로 결과를 박아넣는 패턴
fn convert_syllable(s: &str) -> &str {
match s {
"각" => "⠊⠁⠁",
"간" => "⠊⠁⠉",
"갈" => "⠊⠁⠂",
// ... 수천 줄
}
}

// 올바름 — 초성/중성/종성 분해 후 규정대로 조합
fn convert_syllable(s: char) -> Vec<BrailleCell> {
let (cho, jung, jong) = decompose_hangul(s);
let mut out = vec![];
out.extend(cho_to_braille(cho));
out.extend(jung_to_braille(jung));
if let Some(j) = jong { out.extend(jong_to_braille(j)); }
out
}
```

> **예외:** 단일 자모/기호의 점형 정의(예: ㄱ → ⠈, 숫자 표시 → ⠼)는 PDF가 명시한 기본 매핑이므로 허용된다. **음절/단어/구절 단위의 매핑은 모두 금지.**

### 올바른 구현 방향

1. **PDF 규정 → 알고리즘** — 각 항(제N항)을 함수로 분리하고, 함수 doc에 근거 항 번호를 명시한다
2. **계층 분해** — 자모 → 음절 → 단어 → 문장 순으로 단계화하여 각 층은 자기 책임만 진다
3. **테스트는 검증 도구** — 알고리즘이 PDF 규정과 일치하는지 확인하는 용도이지, 코드가 맞춰야 할 정답표가 아니다
4. **테스트 추가 시 코드 수정 불필요** — 같은 규정에 속하는 새 예제는 이미 알고리즘이 처리하고 있어야 한다. 새 예제 추가만으로 코드 변경이 필요하다면 알고리즘이 일반화되지 않은 것이다

### 자가 검증 체크리스트

PR/커밋 전 다음을 모두 확인한다:

- [ ] 변환 로직 안에 `test_cases/` 경로 문자열이 없다
- [ ] 테스트의 `input` 문자열이 코드에 리터럴로 등장하지 않는다 (단일 자모/기호 제외)
- [ ] 테스트의 `expected`/`unicode` 문자열이 코드에 리터럴로 등장하지 않는다
- [ ] 같은 규정의 새 예제를 추가해도 코드 수정 없이 통과한다
- [ ] 음절/단어 단위 분기가 자모 단위 일반 함수로 통합되어 있다
- [ ] 모든 분기와 매핑은 PDF의 특정 항을 근거로 추적 가능하다
Loading
Loading