@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
@ 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
@ 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
@!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
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}$.
@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
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
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