1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
macro_rules! encode_curly_single_quote_arm {
($tokens:ident, $out:ident, $prev_was_number:ident, $numeric_mode:ident, $sq_roles:ident, $i:ident) => {
{
// §7.6 curly single quotation mark vs apostrophe, resolved by
// the matched-pair analysis in `single_quote_roles`: an opening
// mark → ⠠⠦, a closing mark → ⠠⠴, an apostrophe → ⠄. The straight
// `'` is ambiguous and stays an apostrophe on the default path.
match $sq_roles[$i] {
SingleQuote::Open => {
// §7.6.10: a detached opening single quote (a space sits
// between it and the text it bounds) takes a grade-1
// indicator ⠰ so the ⠠⠦ is not misread.
if matches!($tokens.get($i + 1), Some(EnglishToken::Space)) {
$out.push(GRADE1);
}
$out.extend([decode_unicode('⠠'), decode_unicode('⠦')]);
}
SingleQuote::Close => {
// §7.6.10 / §2.6.5: a detached closing single quote takes
// the grade-1 indicator when its LEFT side is anchoring
// (space/edge) and its RIGHT side ultimately reaches a
// §2.6.1 boundary via §2.6.3 transparent symbols. The
// stripping loop lets a bracket + edge (`... ')` ) still
// trigger the indicator, matching §7.6.10's example
// `(‘To be or not ... ’)` → `... ⠰⠠⠴⠐⠜`.
let space_before =
$i > 0 && matches!($tokens.get($i - 1), Some(EnglishToken::Space));
let mut r = $i;
while r + 1 < $tokens.len()
&& matches!(
$tokens.get(r + 1),
Some(EnglishToken::Symbol(
')' | ']' | '}' | '.' | ',' | ':' | ';'
))
)
{
r += 1;
}
let reaches_boundary = matches!(
$tokens.get(r + 1),
None | Some(EnglishToken::Space | EnglishToken::LineBreak)
);
if space_before
&& reaches_boundary
&& !matches!($tokens.get($i + 1), Some(EnglishToken::Symbol('.')))
{
$out.push(GRADE1);
}
$out.extend([decode_unicode('⠠'), decode_unicode('⠴')]);
}
SingleQuote::Apostrophe => $out.push(decode_unicode('⠄')),
}
$prev_was_number = false;
$numeric_mode = false;
}
};
}