diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /systems/tex-extensions/clasen/hyphenation/hyphenation.ch |
Initial commit
Diffstat (limited to 'systems/tex-extensions/clasen/hyphenation/hyphenation.ch')
-rw-r--r-- | systems/tex-extensions/clasen/hyphenation/hyphenation.ch | 883 |
1 files changed, 883 insertions, 0 deletions
diff --git a/systems/tex-extensions/clasen/hyphenation/hyphenation.ch b/systems/tex-extensions/clasen/hyphenation/hyphenation.ch new file mode 100644 index 0000000000..70adb89221 --- /dev/null +++ b/systems/tex-extensions/clasen/hyphenation/hyphenation.ch @@ -0,0 +1,883 @@ +@x +else begin print_err("I can't go on meeting you like this"); +@y +else begin print_err("I can't go on meeting you like this ("); + print(s); print_char(")"); +@z + +@x +@d disc_node=7 {|type| of a discretionary node} +@y +With extended hyphenation, discretionaries have an additional property, +the |hyphen_class| (which is an integer $0\leq\mathit{hyphen_class}\leq 9$). +The penalty charged for a break depends on the |hyphen_class|. + +@d disc_node=7 {|type| of a discretionary node} +@d disc_node_size=3 {number of words to allocate for a discretionary} +@d hyphen_class(#)==mem[#+2].int {the class of this hyphen} +@z + +@x +@p function new_disc:pointer; {creates an empty |disc_node|} +var p:pointer; {the new node} +begin p:=get_node(small_node_size); type(p):=disc_node; +replace_count(p):=0; pre_break(p):=null; post_break(p):=null; +new_disc:=p; +end; +@y +@p function new_disc(c:integer):pointer; {creates an empty |disc_node|} +var p:pointer; {the new node} +begin p:=get_node(disc_node_size); type(p):=disc_node; +replace_count(p):=0; pre_break(p):=null; post_break(p):=null; +hyphen_class(p):=c; new_disc:=p; +end; +@z + +@x +disc_node: begin short_display(pre_break(p)); + short_display(post_break(p));@/ +@y +disc_node: begin short_display(pre_break(p)); + if hyphen_classes_en and(hyphen_class(p)<>1) then print_int(hyphen_class(p)); + short_display(post_break(p));@/ +@z + +@x + disc_node: begin flush_node_list(pre_break(p)); + flush_node_list(post_break(p)); + end; +@y + disc_node: begin flush_node_list(pre_break(p)); + flush_node_list(post_break(p)); + free_node(p,disc_node_size); goto done; + end; +@z + +@x +disc_node: begin r:=get_node(small_node_size); + pre_break(r):=copy_node_list(pre_break(p)); + post_break(r):=copy_node_list(post_break(p)); + end; +@y +disc_node: begin r:=new_disc(hyphen_class(p)); + pre_break(r):=copy_node_list(pre_break(p)); + post_break(r):=copy_node_list(post_break(p)); + end; +@z + +@x +@d hyph_data=99 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )} +@y +@d hyph_data=99 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns}, + \.{\\hyphenpenalties} )} +@z + +@x + if m=hmode then if nest[p].pg_field <> @'40600000 then + begin print(" (language"); print_int(nest[p].pg_field mod @'200000); + print(":hyphenmin"); print_int(nest[p].pg_field div @'20000000); + print_char(","); print_int((nest[p].pg_field div @'200000) mod @'100); + print_char(")"); + end; +@y + if m=hmode then if nest[p].pg_field <> @'40601000 then begin + print(" (language"); print_int(nest[p].pg_field mod @'400); + print(":hyphenmin"); print_int(nest[p].pg_field div @'20000000); + print_char(","); print_int((nest[p].pg_field div @'200000) mod @'100); + if hyphen_classes_en then begin + print(":hyphenclasses"); + print_int((nest[p].pg_field div @'400) mod @'20); + end; + print_char(")"); + end; +@z + +@x +@d hyphen_penalty_code=3 {penalty for break after discretionary hyphen} +@d ex_hyphen_penalty_code=4 {penalty for break after explicit hyphen} +@y +@d hyphen_classes_code=3 {number of hyphenation classes} +@d ex_hyphen_class_code=4 {hyphenation class of automatically + inserted empty discretionaries} +@z + +@x +{hyphen classes int_pars go here} +@y +@d hyphen_classes_state_code=58 +@d hyphen_penalty_base=59 {10 penalties for breaks after discretionary hyphens, + numbered from 0 to 9} +@z + +@x +{hyphen count int_pars go here} +@y +@d hyphen_count_state_code=69 +@d three_hyphen_demerits_code=70 {demerits for three consecutive hyphens} +@d four_hyphen_demerits_code=71 {demerits for four consecutive hyphens} +@d five_hyphen_demerits_code=72 {demerits for five or more consecutive hyphens} +@z + +@x +@d hyphen_penalty==int_par(hyphen_penalty_code) +@d ex_hyphen_penalty==int_par(ex_hyphen_penalty_code) +@y +@d hyphen_classes_state==int_par(hyphen_classes_state_code) +@d hyphen_classes_en==(hyphen_classes_state>0) +@d hyphen_penalties(#)==int_par(hyphen_penalty_base+#) +@d default_hyphen_class=1 +@d hyphen_penalty==hyphen_penalties(default_hyphen_class) +@d ex_hyphen_penalty==hyphen_penalties(ex_hyphen_class) +@z + +@x +@d error_context_lines==int_par(error_context_lines_code) +@y +@d hyphen_count_state==int_par(hyphen_count_state_code) +@d hyphen_count_en==(hyphen_count_state>0) +@d error_context_lines==int_par(error_context_lines_code) +@d three_hyphen_demerits==int_par(three_hyphen_demerits_code) +@d four_hyphen_demerits==int_par(four_hyphen_demerits_code) +@d five_hyphen_demerits==int_par(five_hyphen_demerits_code) +@z + +@x +hyphen_penalty_code:print_esc("hyphenpenalty"); +ex_hyphen_penalty_code:print_esc("exhyphenpenalty"); +@y +hyphen_classes_code:print_esc("hyphenclasses"); +ex_hyphen_class_code:print_esc("exhyphenclass"); +@z + +@x +error_context_lines_code:print_esc("errorcontextlines"); +@y +error_context_lines_code:print_esc("errorcontextlines"); +hyphen_count_state_code:print_esc("hyphencountstate"); +three_hyphen_demerits_code:print_esc("threehyphendemerits"); +four_hyphen_demerits_code:print_esc("fourhyphendemerits"); +five_hyphen_demerits_code:print_esc("fivehyphendemerits"); +hyphen_classes_state_code:print_esc("hyphenclassesstate"); +@z + +@x +primitive("hyphenpenalty",assign_int,int_base+hyphen_penalty_code);@/ +@!@:hyphen_penalty_}{\.{\\hyphenpenalty} primitive@> +primitive("exhyphenpenalty",assign_int,int_base+ex_hyphen_penalty_code);@/ +@!@:ex_hyphen_penalty_}{\.{\\exhyphenpenalty} primitive@> +@y +primitive("hyphenclasses",assign_int,int_base+hyphen_classes_code);@/ +@!@:hyphen_classes_}{\.{\\hyphenclasses} primitive@> +primitive("exhyphenclass",assign_int,int_base+ex_hyphen_class_code);@/ +@!@:ex_hyphen_class_}{\.{\\exhyphenclass} primitive@> +@z + +@x +primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ +@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> +@y +primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ +@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> +primitive("threehyphendemerits",assign_int,int_base+three_hyphen_demerits_code);@/ +@!@:three_hyphen_demerits_}{\.{\\threehyphendemerits} primitive@> +primitive("fourhyphendemerits",assign_int,int_base+four_hyphen_demerits_code);@/ +@!@:four_hyphen_demerits_}{\.{\\fourhyphendemerits} primitive@> +primitive("fivehyphendemerits",assign_int,int_base+five_hyphen_demerits_code);@/ +@!@:five_hyphen_demerits_}{\.{\\fivehyphendemerits} primitive@> +primitive("hyphencountstate",assign_int,int_base+hyphen_count_state_code);@/ +@!@:hyphen_count_state_}{\.{\\hyphencountstate} primitive@> +primitive("hyphenpenalty",assign_int,int_base+hyphen_penalty_base+1);@/ +@!@:hyphen_penalty_}{\.{\\hyphenpenalty} primitive@> +primitive("exhyphenpenalty",assign_int,int_base+hyphen_penalty_base + +ex_hyphen_class);@/ +@!@:ex_hyphen_penalty_}{\.{\\exhyphenpenalty} primitive@> +primitive("hyphenclassesstate",assign_int,int_base+hyphen_classes_state_code);@/ +@!@:hyphen_classes_state_}{\.{\\hyphenclassesstate} primitive@> +@z + +@x +escape_char:="\"; end_line_char:=carriage_return; +@y +escape_char:="\"; end_line_char:=carriage_return; +hyphen_count_state:=0; +hyphen_classes_state:=0; +@z + +@x +@ Here is a procedure that displays the contents of |eqtb[n]| +symbolically. + +@p@t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/ +@y +@ Here is a procedure that displays the contents of |eqtb[n]| +symbolically. + +@p@t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/ +function is_enabled(@!b:boolean;@!j:quarterword;@!k:halfword):boolean; +begin + if not b then begin + print_err("Improper "); print_cmd_chr(j,k); + help1("Sorry, this optional feature has been disabled."); error; + end; + is_enabled:=b; +end; +@z + +@x +init_cur_lang:=prev_graf mod @'200000; +@y +init_cur_lang:=prev_graf mod @'400; +init_hyf_class:=(prev_graf div @'400) mod @'20; +@z + +@x +@d tight_fit=3 {fitness classification for lines shrinking 0.5 to 1.0 of their + shrinkability} +@y +@d hyphen_count_limit=6 {treat 6 consecutive hyphens like 5} +@d fitness_classes=4 {number of different fitness classes} +@d line_classes=24 {|hyphen_count_limit*fitness_classes|} +@d tight_fit=3 {fitness classification for lines shrinking 0.5 to 1.0 of their + shrinkability} +@z + +@x +@d active_node_size=3 {number of words in active nodes} +@d fitness==subtype {|very_loose_fit..tight_fit| on final line for this break} +@y +@d active_node_size=3 {number of words in active nodes} +@d line_fitness==subtype {|very_loose_fit..tight_fit| on final line for + this break; also contains information about number of consecutive hyphens} +@d fitness(#)==(line_fitness(#) mod fitness_classes) +@d hyphen_count(#)==(line_fitness(#) div fitness_classes) +@z + +@x +type(last_active):=hyphenated; line_number(last_active):=max_halfword; +subtype(last_active):=0; {the |subtype| is never examined by the algorithm} +@y +type(last_active):=hyphenated; line_number(last_active):=max_halfword; +line_fitness(last_active):=0; {the |fitness| is never examined by the + algorithm} +@z + +@x +@!fit_class:very_loose_fit..tight_fit; {possible fitness class of test line} +@y +@!fit_class:very_loose_fit..tight_fit; {possible fitness class of test line} +@!consec:0..5; {number of consecutive hyphens up to test line} +@!line_class:small_number; {|fit_class+fitness_classes*consec|} +@z + +@x +@!minimal_demerits:array[very_loose_fit..tight_fit] of integer; {best total + demerits known for current line class and position, given the fitness} +@!minimum_demerits:integer; {best total demerits known for current line class + and position} +@!best_place:array[very_loose_fit..tight_fit] of pointer; {how to achieve + |minimal_demerits|} +@!best_pl_line:array[very_loose_fit..tight_fit] of halfword; {corresponding + line number} +@y +@!minimal_demerits:array[0..line_classes] of integer; {best total + demerits known for current line class and position, given |fit_class+4*consec|} +@!minimum_demerits:integer; {best total demerits known for current line class + and position} +@!best_place:array[0..line_classes] of pointer; {how to achieve |minimal_demerits|} +@!best_pl_line:array[0..line_classes] of halfword; {corresponding line number} +@z + +@x +minimal_demerits[tight_fit]:=awful_bad; +minimal_demerits[decent_fit]:=awful_bad; +minimal_demerits[loose_fit]:=awful_bad; +minimal_demerits[very_loose_fit]:=awful_bad; +@y +for j:=0 to line_classes do minimal_demerits[j]:=awful_bad; +@z + +@x +for fit_class:=very_loose_fit to tight_fit do + begin if minimal_demerits[fit_class]<=minimum_demerits then + @<Insert a new active node + from |best_place[fit_class]| to |cur_p|@>; + minimal_demerits[fit_class]:=awful_bad; + end; +@y +for fit_class:=very_loose_fit to tight_fit do + for consec:=0 to hyphen_count_limit-1 do begin + line_class:=fit_class+fitness_classes*consec; + if minimal_demerits[line_class]<=minimum_demerits then + @<Insert a new active node from |best_place[line_class]| to |cur_p|@>; + minimal_demerits[line_class]:=awful_bad; + end; +@z + +@x +@<Insert a new active node from |best_place[fit_class]| to |cur_p|@>= +begin q:=get_node(passive_node_size); +link(q):=passive; passive:=q; cur_break(q):=cur_p; +@!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/ +prev_break(q):=best_place[fit_class];@/ +q:=get_node(active_node_size); break_node(q):=passive; +line_number(q):=best_pl_line[fit_class]+1; +fitness(q):=fit_class; type(q):=break_type; +total_demerits(q):=minimal_demerits[fit_class]; +link(q):=r; link(prev_r):=q; prev_r:=q; +@!stat if tracing_paragraphs>0 then + @<Print a symbolic description of the new break node@>; +tats@;@/ +end +@y +@<Insert a new active node from |best_place[line_class]| to |cur_p|@>= +begin q:=get_node(passive_node_size); +link(q):=passive; passive:=q; cur_break(q):=cur_p; +@!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/ +prev_break(q):=best_place[line_class];@/ +q:=get_node(active_node_size); break_node(q):=passive; +line_number(q):=best_pl_line[line_class]+1; +line_fitness(q):=line_class; type(q):=break_type; +total_demerits(q):=minimal_demerits[line_class]; +link(q):=r; link(prev_r):=q; prev_r:=q; +@!stat if tracing_paragraphs>0 then + @<Print a symbolic description of the new break node@>; +tats@;@/ +end +@z + +@x +if break_type=hyphenated then print_char("-"); +@y +if break_type=hyphenated then begin print("-("); print_int(consec); print_char(")"); end; +@z + +@x +else @<Set the value of |b| to the badness for shrinking the line, + and compute the corresponding |fit_class|@>; +@y +else @<Set the value of |b| to the badness for shrinking the line, + and compute the corresponding |fit_class|@>; +if break_type=unhyphenated then consec:=0 +else if hyphen_count(r)=hyphen_count_limit-1 then consec:=hyphen_count(r) +else consec:=hyphen_count(r)+1; +line_class:=fit_class+fitness_classes*consec; +@z + +@x +if d<=minimal_demerits[fit_class] then + begin minimal_demerits[fit_class]:=d; + best_place[fit_class]:=break_node(r); best_pl_line[fit_class]:=l; + if d<minimum_demerits then minimum_demerits:=d; + end +@y +if d<=minimal_demerits[line_class] then + begin minimal_demerits[line_class]:=d; + best_place[line_class]:=break_node(r); best_pl_line[line_class]:=l; + if d<minimum_demerits then minimum_demerits:=d; + end +@z + +@x +if (break_type=hyphenated)and(type(r)=hyphenated) then + if cur_p<>null then d:=d+double_hyphen_demerits + else d:=d+final_hyphen_demerits; +@y +if (break_type=hyphenated)and(hyphen_count(r)>0) then + if cur_p=null then d:=d+final_hyphen_demerits + else if not hyphen_count_en then d:=d+double_hyphen_demerits + else case hyphen_count(r) of + 1: d:=d+double_hyphen_demerits; + 2: d:=d+three_hyphen_demerits; + 3: d:=d+four_hyphen_demerits; + othercases d:=d+five_hyphen_demerits; + endcases; +@z + +@x +q:=get_node(active_node_size); +type(q):=unhyphenated; fitness(q):=decent_fit; +@y +q:=get_node(active_node_size); +type(q):=unhyphenated; line_fitness(q):=decent_fit+fitness_classes*0; +@z + +@x +@ The following code knows that discretionary texts contain +only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes. + +@<Try to break after a discretionary fragment...@>= +begin s:=pre_break(cur_p); disc_width:=0; +if s=null then try_break(ex_hyphen_penalty,hyphenated) +else begin repeat @<Add the width of node |s| to |disc_width|@>; + s:=link(s); + until s=null; + act_width:=act_width+disc_width; + try_break(hyphen_penalty,hyphenated); + act_width:=act_width-disc_width; + end; +@y +@ The following code knows that discretionary texts contain +only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes. +If |ex_hyphen_penalty=0|, we might encounter automatically inserted empty +discretionary nodes of class~0. We never break at these discretionaries, +nevertheless they are necessary to differentiate the output of +\.{\\showhyphens} for automatically inserted hyphens of classes 0 and~1. + +@<Try to break after a discretionary fragment...@>= +begin if hyphen_class(cur_p)>0 then begin + s:=pre_break(cur_p); disc_width:=0; + while s<>null do begin + @<Add the width of node |s| to |disc_width|@>; + s:=link(s); + end; + act_width:=act_width+disc_width; + try_break(hyphen_penalties(hyphen_class(cur_p)),hyphenated); + act_width:=act_width-disc_width; + end; +@z + +@x +begin t:=replace_count(q); +@y +begin t:=replace_count(q); hyphen_class(q):=1; +@z + +@x +cur_lang:=init_cur_lang; l_hyf:=init_l_hyf; r_hyf:=init_r_hyf; +@y +cur_lang:=init_cur_lang; l_hyf:=init_l_hyf; r_hyf:=init_r_hyf; +hyf_class:=init_hyf_class; +@z + +@x +@!hyf_bchar:halfword; {boundary character after $c_n$} +@y +@!hyf_bchar:halfword; {boundary character after $c_n$} +@!hyf_class,@!init_hyf_class:integer; +@z + +@x +@!hyf:array [0..64] of 0..9; {odd values indicate discretionary hyphens} +@y +@!hyf:array [0..64] of small_number; +@z + +@x +@<If no hyphens were found, |return|@>= +for j:=l_hyf to hn-r_hyf do if odd(hyf[j]) then goto found1; +return; +found1: +@y +@<If no hyphens were found, |return|@>= +for j:=l_hyf to hn-r_hyf do if hyf[j]>0 then goto found1; +return; +found1: +@z + +@x +@d set_cur_r==begin if j<n then cur_r:=qi(hu[j+1])@+else cur_r:=bchar; + if odd(hyf[j]) then cur_rh:=hchar@+else cur_rh:=non_char; +@y +@d set_cur_r==begin if j<n then cur_r:=qi(hu[j+1])@+else cur_r:=bchar; + if hyf[j]>0 then cur_rh:=hchar@+else cur_rh:=non_char; +@z + +@x + else begin if hchar<non_char then if odd(hyf[j]) then +@y + else begin if hchar<non_char then if hyf[j]>0 then +@z + +@x + while link(s)>null do s:=link(s); + if odd(hyf[j-1]) then +@y + while link(s)>null do s:=link(s); + if hyf[j-1]>0 then +@z + +@x +repeat r:=get_node(small_node_size); +@y +repeat r:=new_disc(hyf[hyphen_passed]); +@z + +@x +hyphen_passed:=j-1; link(hold_head):=null; +until not odd(hyf[j-1]) +@y +hyphen_passed:=j-1; link(hold_head):=null; +until hyf[j-1]=0 +@z + +@x +found: for j:=0 to l_hyf-1 do hyf[j]:=0; +for j:=0 to r_hyf-1 do hyf[hn-j]:=0 +@y +for j:=l_hyf to hn-r_hyf do hyf[j]:=hyf[j] mod hyf_class; +found: for j:=0 to l_hyf-1 do hyf[j]:=0; +for j:=0 to r_hyf-1 do hyf[hn-j]:=0 +@z + +@x +The words in the table point to lists in |mem| that specify hyphen positions +in their |info| fields. The list for $c_1\ldots c_n$ contains the number |k| if +the word $c_1\ldots c_n$ has a discretionary hyphen between $c_k$ and +$c_{k+1}$. +@y +The words in the table point to lists in |mem| that specify hyphen positions +and classes in their |type| and |subtype| fields. The list for $c_1\ldots c_n$ +contains the pair |k|, |l| if the word $c_1\ldots c_n$ has a discretionary +hyphen of class |l| between $c_k$ and $c_{k+1}$. + +@d hyphen_exception_pos==type +@d hyphen_exception_class==subtype +@z + +@x +while s<>null do + begin hyf[info(s)]:=1; s:=link(s); + end +@y +while s<>null do + begin hyf[hyphen_exception_pos(s)]:=hyphen_exception_class(s); s:=link(s); + end +@z + +@x +var n:0..64; {length of current word; not always a |small_number|} +@y +var n:0..64; {length of current word; not always a |small_number|} + at_hyphen:boolean; +@z + +@x +@ @<Enter as many...@>= +n:=0; p:=null; +@y +@ @<Enter as many...@>= +n:=0; p:=null; at_hyphen:=false; +@z + +@x + if cur_cmd=right_brace then return; + n:=0; p:=null; +@y + if cur_cmd=right_brace then return; + n:=0; p:=null; at_hyphen:=false; +@z + +@x +if cur_chr="-" then @<Append the value |n| to list |p|@> +@y +if cur_chr="-" then begin + at_hyphen:=true; + @<Append the value |n| to list |p|@>; +end else if hyphen_classes_en and at_hyphen and + ("0"<=cur_chr)and(cur_chr<="9") then begin + hyphen_exception_class(p):=cur_chr-"0"; + at_hyphen:=false; +end +@z + +@x +begin if n<63 then + begin q:=get_avail; link(q):=p; info(q):=n; p:=q; + end; +end +@y +begin if n<63 then + begin q:=get_avail; link(q):=p; + hyphen_exception_pos(q):=n; + hyphen_exception_class(q):=1; + p:=q; + end; +end +@z + +@x +@!digit_sensed:boolean; {should the next digit be treated as a letter?} +@y +@!digits_sensed:integer; {should the next digit be treated as a letter?} +@z + +@x +k:=0; hyf[0]:=0; digit_sensed:=false; +loop@+ begin get_x_token; +@y +k:=0; hyf[0]:=0; digits_sensed:=0; +loop@+ begin get_x_token; +@z + +@x + if cur_cmd=right_brace then goto done; + k:=0; hyf[0]:=0; digit_sensed:=false; +@y + if cur_cmd=right_brace then goto done; + k:=0; hyf[0]:=0; digits_sensed:=0; +@z + +@x +if digit_sensed or(cur_chr<"0")or(cur_chr>"9") then +@y +if (cur_chr<"0")or(cur_chr>"9")or + ((not hyphen_classes_en)and(digits_sensed>=1))or(digits_sensed>=2) then +@z + +@x + begin incr(k); hc[k]:=cur_chr; hyf[k]:=0; digit_sensed:=false; +@y + begin incr(k); hc[k]:=cur_chr; hyf[k]:=0; digits_sensed:=0; +@z + +@x +else if k<63 then + begin hyf[k]:=cur_chr-"0"; digit_sensed:=true; + end +@y +else if k<63 then begin + if 10*hyf[k]+cur_chr-"0"<=63 then begin + hyf[k]:=10*hyf[k]+cur_chr-"0"; incr(digits_sensed); + end else begin + print_err("Bad "); print_esc("patterns"); +@.Bad \\patterns@> + help1("(See Appendix H.)"); error; + end; +end +@z + +@x + if mode>0 then tail_append(new_disc); +@y + if mode>0 then tail_append(new_disc(ex_hyphen_class)); +@z + +@x +else norm_min:=h; +end; +@y +else norm_min:=h; +end; +@# +function hyphen_classes:small_number; +var h:integer; +begin + if not hyphen_classes_en then hyphen_classes:=2 + else begin + h:=int_par(hyphen_classes_code); + if h<2 then hyphen_classes:=2@+else if h>10 then hyphen_classes:=10@+ + else hyphen_classes:=h; + end; +end; + +function ex_hyphen_class:small_number; +var h:integer; +begin + if not hyphen_classes_en then ex_hyphen_class:=2 + else begin + h:=int_par(ex_hyphen_class_code); + if h<0 then ex_hyphen_class:=0@+else if h>9 then ex_hyphen_class:=9@+ + else ex_hyphen_class:=h; + end; +end; +@z + +@x +prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min)) + *@'200000+cur_lang; +@y +prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min)) + *@'200000+hyphen_classes*@'400+cur_lang; +@z + +@x +primitive("discretionary",discretionary,0); +@!@:discretionary_}{\.{\\discretionary} primitive@> +@y +primitive("discretionary",discretionary,0); +@!@:discretionary_}{\.{\\discretionary} primitive@> +primitive("gendiscretionary",discretionary,2); +@!@:gendiscretionary_}{\.{\\gendiscretionary} primitive@> +@z + +@x +discretionary: if chr_code=1 then + print_esc("-")@+else print_esc("discretionary"); +@y +discretionary: if chr_code=1 then + print_esc("-")@+else if chr_code=0 then print_esc("discretionary") + else print_esc("gendiscretionary"); +@z + +@x +@ The space factor does not change when we append a discretionary node, +but it starts out as 1000 in the subsidiary lists. + +@<Declare act...@>= +procedure append_discretionary; +var c:integer; {hyphen character} +begin tail_append(new_disc); +if cur_chr=1 then + begin c:=hyphen_char[cur_font]; + if c>=0 then if c<256 then pre_break(tail):=new_character(cur_font,c); + end +@y +@ The space factor does not change when we append a discretionary node, +but it starts out as 1000 in the subsidiary lists. We temporarily assign +hyphen class 0 to discretionaries generated by one of the original +primitives. This is necessary to simulate \TeX's behaviour on discretionaries +with empty pre-break text. They charge the \.{\\exhyphenpenalty}, so we +have to assign them to hyphen class |ex_hyphen_class|. Using +\.{\\gendiscretionary}, one can generate discretionaries with empty pre-break +text in other hyphen classes. + +@<Declare act...@>= +procedure append_discretionary; +var c:integer; {hyphen character} + class:integer; {hyphen class} +begin + class:=default_hyphen_class; + if cur_chr<2 then class:=0 + else if is_enabled(hyphen_classes_en,cur_cmd,cur_chr) then begin + scan_int; + if (1<=cur_val)and(cur_val<=9) then class:=cur_val + else begin + print_err("No such "); print_esc("hyphen class"); +@.No such \\hyphenpenalties@> + help2("Only numbers 1..9 are valid here.")@/ + ("Proceed; I'll take 1 instead."); + error; + end; + end; + tail_append(new_disc(class)); + if cur_chr=1 then begin + c:=hyphen_char[cur_font]; + if c>=0 then if c<256 then pre_break(tail):=new_character(cur_font,c); + if pre_break(tail)=null then hyphen_class(tail):=ex_hyphen_class + else hyphen_class(tail):=default_hyphen_class; + end +@z + +@x +@ The three discretionary lists are constructed somewhat as if they were +hboxes. A~subroutine called |build_discretionary| handles the transitions. +(This is sort of fun.) +@y +@ The three discretionary lists are constructed somewhat as if they were +hboxes. A~subroutine called |build_discretionary| handles the transitions. +(This is sort of fun.) + +When we enter this routine, |tail| is a discretionary node whose hyphen class +is zero, if it comes from \.{\\discretionary}. In that case we decide about +the proper hyphen class after reading the pre-break text. +@z + +@x +0:pre_break(tail):=p; +@y +0:begin pre_break(tail):=p; + if hyphen_class(tail)=0 then + if (p=null) then hyphen_class(tail):=ex_hyphen_class + else hyphen_class(tail):=default_hyphen_class; +end; +@z + +@x +primitive("patterns",hyph_data,1); +@!@:patterns_}{\.{\\patterns} primitive@> +@y +primitive("patterns",hyph_data,1); +@!@:patterns_}{\.{\\patterns} primitive@> +primitive("hyphenpenalties",hyph_data,2); +@!@:hyphenpenalties_}{\.{\\hyphenpenalties} primitive@> +@z + +@x +hyph_data: if chr_code=1 then print_esc("patterns") + else print_esc("hyphenation"); +@y +hyph_data: if chr_code=1 then print_esc("patterns") + else if chr_code=0 then print_esc("hyphenation") + else print_esc("hyphenpenalties"); +@z + +@x + else begin new_hyph_exceptions; goto done; + end; +@y + else if cur_chr=0 then begin + new_hyph_exceptions; + goto done; + end else if is_enabled(hyphen_classes_en,cur_cmd,cur_chr) then begin + scan_int; p:=cur_val; + scan_optional_equals; scan_int; + if (1<=p)and(p<=9) then begin + word_define(int_base+hyphen_penalty_base+p,cur_val); + end else begin + print_err("No such "); print_esc("hyphenpenalties"); +@.No such \\hyphenpenalties@> + help2("Only numbers 1..9 are valid here.")@/ + ("Proceed; I'll ignore the assignment I just read."); + error; + end; + end; +@z + +@x +@d what_lang(#)==link(#+1) {language number, in the range |0..255|} +@d what_lhm(#)==type(#+1) {minimum left fragment, in the range |1..63|} +@d what_rhm(#)==subtype(#+1) {minimum right fragment, in the range |1..63|} +@y +@d what_lhm(#)==mem[#+1].qqqq.b0 {minimum left fragment, in the range |1..63|} +@d what_rhm(#)==mem[#+1].qqqq.b1 {minimum right fragment, in the range |1..63|} +@d what_lang(#)==mem[#+1].qqqq.b2 {language number, in the range |0..255|} +@d what_hyf(#)==mem[#+1].qqqq.b3 {number of hyphen classes, in the range |2..10|} +@z + +@x + print_int(what_lhm(p)); print_char(","); + print_int(what_rhm(p)); print_char(")"); +@y + print_int(what_lhm(p)); print_char(","); + print_int(what_rhm(p)); print_char(")"); + if hyphen_classes_en then begin + print("(hyphen classes "); print_int(what_hyf(p)); + print_char(")"); + end; +@z + +@x +@ @d adv_past(#)==@+if subtype(#)=language_node then + begin cur_lang:=what_lang(#); l_hyf:=what_lhm(#); r_hyf:=what_rhm(#);@+end +@y +@ @d adv_past(#)==@+if subtype(#)=language_node then + begin cur_lang:=what_lang(#); l_hyf:=what_lhm(#); r_hyf:=what_rhm(#); + hyf_class:=what_hyf(#);@+end +@z + +@x +if l<>clang then + begin new_whatsit(language_node,small_node_size); + what_lang(tail):=l; clang:=l;@/ + what_lhm(tail):=norm_min(left_hyphen_min); + what_rhm(tail):=norm_min(right_hyphen_min); +@y +if l<>clang then + begin new_whatsit(language_node,small_node_size); + what_lang(tail):=l; clang:=l;@/ + what_lhm(tail):=norm_min(left_hyphen_min); + what_rhm(tail):=norm_min(right_hyphen_min); + what_hyf(tail):=hyphen_classes; +@z + +@x + what_lang(tail):=clang; + what_lhm(tail):=norm_min(left_hyphen_min); + what_rhm(tail):=norm_min(right_hyphen_min); +@y + what_lang(tail):=clang; + what_lhm(tail):=norm_min(left_hyphen_min); + what_rhm(tail):=norm_min(right_hyphen_min); + what_hyf(tail):=hyphen_classes; +@z + |