증상
`samples/exam_science.hwp` 페이지 4 20번 응답 paragraph " 는? (단, 는 임의의 원소 기호이다.) [3점]" 의 인라인 수식이 잘못 렌더됨.
작업지시자 보고: "수식이 잘못나타남"
위치
pi=128 (page 4 우측 col), 2 개 inline 수식:
--- 문단 0.128 --- cc=46 text_len=29 controls=2
[0] 수식: script=\"{b} over {a timesm}\" tac=true w=3108 h=2580 (분수, TALL)
[1] 수식: script=\"rm X simZ\" tac=true w=2708 h=1125
ls[0]: vpos=67483 lh=2580 sw=30562 cs=1130
paragraph 위치는 정상 (Tasks #568/#573 fix 와 무관 — 인라인 표 없음, narrow segment_width 없음).
SVG 측 — 수식 위치는 정상이나 콘텐츠 잘못 렌더
- ctrl[0] "{b} over {a timesm}" 분수 transform: (560.87, 1040.49) ✓ 정상 위치
- ctrl[1] "rm X simZ" transform: (650.31, 1045.69) ✓ 정상 위치
잘못 렌더된 콘텐츠:
ctrl[0] 분수의 분모:
<text x=\"2.93\" font-style=\"italic\">a</text>
<text x=\"11.40\" font-style=\"italic\">timesm</text>
→ "a × m" 이어야 하나 "a timesm" (italic 식별자) 으로 표시
ctrl[1]:
<text x=\"0.00\">X</text> ← rm 모디파이어로 upright 정상
<text x=\"10.01\" font-style=\"italic\">simZ</text>
→ "X ~ Z" (또는 "X ≈ Z" 등 sim 연산자) 이어야 하나 "X simZ" (italic 식별자) 으로 표시
본질 결함
src/renderer/equation/tokenizer.rs::read_command (L101-124) 가 keyword prefix-split 을 "rm / it / bold" 3 개만 처리:
fn read_command(&mut self) -> Token {
for kw in [\"bold\", \"it\", \"rm\"] { // ← prefix-split 대상이 3개 뿐
if self.matches_at(kw) {
let after = self.peek(kw.len());
if matches!(after, Some(c) if c.is_ascii_alphanumeric()) {
self.pos += kw.len();
return Token::new(TokenType::Command, kw, start);
}
}
}
// 그 외: 연속 alphanumeric 을 단일 토큰으로 — 결함 발현
while let Some(ch) = self.current() {
if ch.is_ascii_alphanumeric() { value.push(ch); ... } else { break; }
}
}
결과:
- "timesm" 입력 → 단일 Command 토큰 "timesm" (기대: "times" + "m")
- "simZ" 입력 → 단일 Command 토큰 "simZ" (기대: "sim" + "Z")
"times" 는 `symbols.rs::lookup_symbol("times")` 가 "×" 로 매핑되는 known keyword. "sim" 도 마찬가지. 그러나 토크나이저가 keyword 경계를 인식하지 못해 단일 식별자로 처리됨.
정정 방향 후보
안 A — read_command 의 prefix-split 키워드 목록 확장
"bold/it/rm" 외에 `symbols.rs` 에 등록된 known keyword (times, sim, over, sqrt, alpha, beta, cdot, ... 등 수십 개) 를 모두 prefix-split 대상으로 추가.
단점: 키워드 목록 유지보수 부담. `lookup_symbol` / `symbols.rs` 와 동기화 필요.
안 B — Longest-prefix matching against known keyword set
토큰 시작 시 `symbols.rs` 등록 keyword 와 longest-prefix match 시도. 매치되고 다음 char 가 alphanumeric 이면 prefix 만 분리.
장점: 키워드 목록 단일화 (symbols.rs).
단점: 매번 lookup 비용. 그리스 문자 등 짧은 keyword 가 다른 식별자에 잘못 매치될 가능성 (예: "alphabet" → "alpha" + "bet")?
안 C — Word boundary heuristic
"timesm" 처럼 keyword 가 contiguous text 에 묻혀있는 케이스만 휴리스틱으로 분리 (예: 입력이 "keyword + non-keyword" 패턴일 때만 분리).
단점: 휴리스틱 — 메모리 `feedback_rule_not_heuristic` 정합 위반 가능. 룰 vs 휴리스틱 자문 필요.
→ Stage 1 진단 후 결정.
영향 범위
광범위 sweep 으로 다른 fixture 의 수식 script 점검 필요:
- exam_science.hwp 의 다른 paragraph 도 동일 패턴 가능 (예: pi=49 "는 와 으로" + 수식 등)
- exam_eng / exam_kor / exam_math 등 시험지 fixture
- atop-equation-01.hwp / equation-lim.hwp 등 수식 전용 fixture
우선순위
시각 정확성 — 수식 의미 변경 (곱셈 연산자 / 유사 연산자 표시 누락). M100 milestone.
참고
증상
`samples/exam_science.hwp` 페이지 4 20번 응답 paragraph " 는? (단, 는 임의의 원소 기호이다.) [3점]" 의 인라인 수식이 잘못 렌더됨.
작업지시자 보고: "수식이 잘못나타남"
위치
pi=128 (page 4 우측 col), 2 개 inline 수식:
paragraph 위치는 정상 (Tasks #568/#573 fix 와 무관 — 인라인 표 없음, narrow segment_width 없음).
SVG 측 — 수식 위치는 정상이나 콘텐츠 잘못 렌더
잘못 렌더된 콘텐츠:
ctrl[0] 분수의 분모:
→ "a × m" 이어야 하나 "a timesm" (italic 식별자) 으로 표시
ctrl[1]:
→ "X ~ Z" (또는 "X ≈ Z" 등 sim 연산자) 이어야 하나 "X simZ" (italic 식별자) 으로 표시
본질 결함
src/renderer/equation/tokenizer.rs::read_command(L101-124) 가 keyword prefix-split 을 "rm / it / bold" 3 개만 처리:결과:
"times" 는 `symbols.rs::lookup_symbol("times")` 가 "×" 로 매핑되는 known keyword. "sim" 도 마찬가지. 그러나 토크나이저가 keyword 경계를 인식하지 못해 단일 식별자로 처리됨.
정정 방향 후보
안 A —
read_command의 prefix-split 키워드 목록 확장"bold/it/rm" 외에 `symbols.rs` 에 등록된 known keyword (times, sim, over, sqrt, alpha, beta, cdot, ... 등 수십 개) 를 모두 prefix-split 대상으로 추가.
단점: 키워드 목록 유지보수 부담. `lookup_symbol` / `symbols.rs` 와 동기화 필요.
안 B — Longest-prefix matching against known keyword set
토큰 시작 시 `symbols.rs` 등록 keyword 와 longest-prefix match 시도. 매치되고 다음 char 가 alphanumeric 이면 prefix 만 분리.
장점: 키워드 목록 단일화 (symbols.rs).
단점: 매번 lookup 비용. 그리스 문자 등 짧은 keyword 가 다른 식별자에 잘못 매치될 가능성 (예: "alphabet" → "alpha" + "bet")?
안 C — Word boundary heuristic
"timesm" 처럼 keyword 가 contiguous text 에 묻혀있는 케이스만 휴리스틱으로 분리 (예: 입력이 "keyword + non-keyword" 패턴일 때만 분리).
단점: 휴리스틱 — 메모리 `feedback_rule_not_heuristic` 정합 위반 가능. 룰 vs 휴리스틱 자문 필요.
→ Stage 1 진단 후 결정.
영향 범위
광범위 sweep 으로 다른 fixture 의 수식 script 점검 필요:
우선순위
시각 정확성 — 수식 의미 변경 (곱셈 연산자 / 유사 연산자 표시 누락). M100 milestone.
참고