@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@>@@;@/ @y @ Here is a procedure that displays the contents of |eqtb[n]| symbolically. @p@t\4@>@@;@/ 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 @; 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 @; minimal_demerits[line_class]:=awful_bad; end; @z @x @= 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 @; tats@;@/ end @y @= 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 @; 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 @; @y else @; 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 dnull 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. @= begin s:=pre_break(cur_p); disc_width:=0; if s=null then try_break(ex_hyphen_penalty,hyphenated) else begin repeat @; 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. @= begin if hyphen_class(cur_p)>0 then begin s:=pre_break(cur_p); disc_width:=0; while s<>null do begin @; 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 @= for j:=l_hyf to hn-r_hyf do if odd(hyf[j]) then goto found1; return; found1: @y @= 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 j0 then cur_rh:=hchar@+else cur_rh:=non_char; @z @x else begin if hchar0 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 @ @= n:=0; p:=null; @y @ @= 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 @ @y if cur_chr="-" then begin at_hyphen:=true; @; 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. @= 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. @= 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