diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/expand.w')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/expand.w | 789 |
1 files changed, 789 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.w b/Build/source/texk/web2c/luatexdir/tex/expand.w new file mode 100644 index 00000000000..79072f7021e --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/expand.w @@ -0,0 +1,789 @@ +% expand.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: expand.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/expand.w $"; + + +@ Only a dozen or so command codes |>max_command| can possibly be returned by +|get_next|; in increasing order, they are |undefined_cs|, |expand_after|, +|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|, +|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and +|end_template|.{\emergencystretch=40pt\par} + +Sometimes, recursive calls to the following |expand| routine may +cause exhaustion of the run-time calling stack, resulting in +forced execution stops by the operating system. To diminish the chance +of this happening, a counter is used to keep track of the recursion +depth, in conjunction with a constant called |expand_depth|. + +Note that this does not catch all possible infinite recursion loops, +just the ones that exhaust the application calling stack. The +actual maximum value of |expand_depth| is outside of our control, but +the initial setting of |100| should be enough to prevent problems. +@^system dependencies@> + +@c +static int expand_depth_count = 0; + + +@ The |expand| subroutine is used when |cur_cmd>max_command|. It removes a +``call'' or a conditional or one of the other special operations just +listed. It follows that |expand| might invoke itself recursively. In all +cases, |expand| destroys the current token, but it sets things up so that +the next |get_next| will deliver the appropriate next token. The value of +|cur_tok| need not be known when |expand| is called. + +Since several of the basic scanning routines communicate via global variables, +their values are saved as local variables of |expand| so that +recursive calls don't invalidate them. +@^recursion@> + +@c +boolean is_in_csname = false; + +@ @c +void expand(void) +{ + halfword t; /* token that is being ``expanded after'' */ + halfword p; /* for list manipulation */ + halfword cur_ptr; /* for a local token list pointer */ + int cv_backup; /* to save the global quantity |cur_val| */ + int cvl_backup, radix_backup, co_backup; /* to save |cur_val_level|, etc. */ + halfword backup_backup; /* to save |link(backup_head)| */ + int save_scanner_status; /* temporary storage of |scanner_status| */ + incr(expand_depth_count); + if (expand_depth_count >= expand_depth) + overflow("expansion depth", (unsigned) expand_depth); + cv_backup = cur_val; + cvl_backup = cur_val_level; + radix_backup = radix; + co_backup = cur_order; + backup_backup = token_link(backup_head); + RESWITCH: + if (cur_cmd < call_cmd) { + /* Expand a nonmacro */ + if (int_par(tracing_commands_code) > 1) + show_cur_cmd_chr(); + switch (cur_cmd) { + case top_bot_mark_cmd: + /* Insert the appropriate mark text into the scanner */ + t = cur_chr % marks_code; + if (cur_chr >= marks_code) + scan_mark_num(); + else + cur_val = 0; + switch (t) { + case first_mark_code: + cur_ptr = first_mark(cur_val); + break; + case bot_mark_code: + cur_ptr = bot_mark(cur_val); + break; + case split_first_mark_code: + cur_ptr = split_first_mark(cur_val); + break; + case split_bot_mark_code: + cur_ptr = split_bot_mark(cur_val); + break; + default: + cur_ptr = top_mark(cur_val); + break; + } + if (cur_ptr != null) + begin_token_list(cur_ptr, mark_text); + break; + case expand_after_cmd: + if (cur_chr == 0) { + /* Expand the token after the next token */ + /* It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}. */ + get_token(); + t = cur_tok; + get_token(); + if (cur_cmd > max_command_cmd) + expand(); + else + back_input(); + cur_tok = t; + back_input(); + + } else { /* \\unless */ + /* Negate a boolean conditional and |goto reswitch| */ + /* The result of a boolean condition is reversed when the conditional is + preceded by \.{\\unless}. */ + get_token(); + if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) { + cur_chr = cur_chr + unless_code; + goto RESWITCH; + } + print_err("You can't use `\\unless' before `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help1("Continue, and I'll forget that it ever happened."); + back_error(); + } + break; + case no_expand_cmd: + if (cur_chr == 0) { + /* Suppress expansion of the next token */ + /* The implementation of \.{\\noexpand} is a bit trickier, because it is + necessary to insert a special `|dont_expand|' marker into \TeX's reading + mechanism. This special marker is processed by |get_next|, but it does + not slow down the inner loop. + + Since \.{\\outer} macros might arise here, we must also + clear the |scanner_status| temporarily. + */ + + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + t = cur_tok; + back_input(); /* now |start| and |loc| point to the backed-up token |t| */ + if (t >= cs_token_flag) { + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_dont_expand); + set_token_link(p, iloc); + istart = p; + iloc = p; + } + + } else { + /* Implement \.{\\primitive} */ + /* + The \.{\\primitive} handling. If the primitive meaning of the next + token is an expandable command, it suffices to replace the current + token with the primitive one and restart |expand|. + + Otherwise, the token we just read has to be pushed back, as well + as a token matching the internal form of \.{\\primitive}, that is + sneaked in as an alternate form of |ignore_spaces|. + + An implementation problem surfaces: There really is no |cur_cs| + attached to the inserted primitive command, so it is safer to set + |cur_cs| to zero. |cur_tok| has a similar problem. And for the + non-expanded branch, simply pushing back a token that matches the + correct internal command does not work, because that approach would + not survive roundtripping to a temporary file or even a token list. + + In a next version, it would be smart to create |frozen_| versions of + all the primitives. Then, this problem would not happen, at the + expense of a few hundred extra control sequences. + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + t = get_prim_eq_type(cur_cs); + if (t > max_command_cmd) { + cur_cmd = t; + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + cur_cs = 0; + goto RESWITCH; + } else { + back_input(); /* now |loc| and |start| point to a one-item list */ + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_primitive); + set_token_link(p, iloc); + iloc = p; + istart = p; + } + } else { + print_err("Missing primitive name"); + help2 + ("The control sequence marked <to be read again> does not", + "represent any known primitive."); + back_error(); + } + + } + break; + case cs_name_cmd: + /* Manufacture a control sequence name; */ + manufacture_csname(); + break; + case convert_cmd: + conv_toks(); /* this procedure is discussed in Part 27 below */ + break; + case the_cmd: + ins_the_toks(); /* this procedure is discussed in Part 27 below */ + break; + case if_test_cmd: + conditional(); /* this procedure is discussed in Part 28 below */ + break; + case fi_or_else_cmd: + /* Terminate the current conditional and skip to \.{\\fi} */ + /* The processing of conditionals is complete except for the following + code, which is actually part of |expand|. It comes into play when + \.{\\or}, \.{\\else}, or \.{\\fi} is scanned. */ + + if (int_par(tracing_ifs_code) > 0) + if (int_par(tracing_commands_code) <= 1) + show_cur_cmd_chr(); + if (cur_chr > if_limit) { + if (if_limit == if_code) { + insert_relax(); /* condition not yet evaluated */ + } else { + print_err("Extra "); + print_cmd_chr(fi_or_else_cmd, cur_chr); + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } + } else { + while (cur_chr != fi_code) + pass_text(); /* skip to \.{\\fi} */ + pop_condition_stack(); + } + + break; + case input_cmd: + /* Initiate or terminate input from a file */ + if (cur_chr == 1) + force_eof = true; + else if (cur_chr == 2) + pseudo_start(); + else if (cur_chr == 3) { + pseudo_start(); + iname = 19; + } else if (name_in_progress) + insert_relax(); + else + start_input(); + break; + default: + /* Complain about an undefined macro */ + print_err("Undefined control sequence"); + help5("The control sequence at the end of the top line", + "of your error message was never \\def'ed. If you have", + "misspelled it (e.g., `\\hobx'), type `I' and the correct", + "spelling (e.g., `I\\hbox'). Otherwise just continue,", + "and I'll forget about whatever was undefined."); + error(); + break; + } + } else if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + /* Insert a token containing |frozen_endv| */ + /* An |end_template| command is effectively changed to an |endv| command + by the following code. (The reason for this is discussed below; the + |frozen_end_template| at the end of the template has passed the + |check_outer_validity| test, so its mission of error detection has been + accomplished.) + */ + cur_tok = cs_token_flag + frozen_endv; + back_input(); + + } + cur_val = cv_backup; + cur_val_level = cvl_backup; + radix = radix_backup; + cur_order = co_backup; + set_token_link(backup_head, backup_backup); + decr(expand_depth_count); +} + +@ @c +void complain_missing_csname(void) +{ + print_err("Missing \\endcsname inserted"); + help2("The control sequence marked <to be read again> should", + "not appear between \\csname and \\endcsname."); + back_error(); +} + +@ @c +void manufacture_csname(void) +{ + halfword p, q, r; + lstring *ss; + r = get_avail(); + p = r; /* head of the list of characters */ + is_in_csname = true; + do { + get_x_token(); + if (cur_cs == 0) + store_new_token(cur_tok); + } while (cur_cs == 0); + if (cur_cmd != end_cs_name_cmd) { + /* Complain about missing \.{\\endcsname} */ + complain_missing_csname(); + } + is_in_csname = false; + /* Look up the characters of list |r| in the hash table, and set |cur_cs| */ + + ss = tokenlist_to_lstring(r, true); + if (ss->l > 0) { + no_new_control_sequence = false; + cur_cs = string_lookup((char *) ss->s, ss->l); + no_new_control_sequence = true; + } else { + cur_cs = null_cs; /* the list is empty */ + } + xfree(ss->s); + flush_list(r); + if (eq_type(cur_cs) == undefined_cs_cmd) { + eq_define(cur_cs, relax_cmd, too_big_char); /* N.B.: The |save_stack| might change */ + }; /* the control sequence will now match `\.{\\relax}' */ + cur_tok = cur_cs + cs_token_flag; + back_input(); +} + + +@ Sometimes the expansion looks too far ahead, so we want to insert +a harmless \.{\\relax} into the user's input. + +@c +void insert_relax(void) +{ + cur_tok = cs_token_flag + cur_cs; + back_input(); + cur_tok = cs_token_flag + frozen_relax; + back_input(); + token_type = inserted; +} + + +@ Here is a recursive procedure that is \TeX's usual way to get the +next token of input. It has been slightly optimized to take account of +common cases. + +@c +void get_x_token(void) +{ /* sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros */ + RESTART: + get_token_lua(); + if (cur_cmd <= max_command_cmd) + goto DONE; + if (cur_cmd >= call_cmd) { + if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + cur_cs = frozen_endv; + cur_cmd = endv_cmd; + goto DONE; /* |cur_chr=null_list| */ + } + } else { + expand(); + } + goto RESTART; + DONE: + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + + +@ The |get_x_token| procedure is equivalent to two consecutive +procedure calls: |get_next; x_token|. + +@c +void x_token(void) +{ /* |get_x_token| without the initial |get_next| */ + while (cur_cmd > max_command_cmd) { + expand(); + get_token_lua(); + } + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + + +@ A control sequence that has been \.{\\def}'ed by the user is expanded by +\TeX's |macro_call| procedure. + +Before we get into the details of |macro_call|, however, let's consider the +treatment of primitives like \.{\\topmark}, since they are essentially +macros without parameters. The token lists for such marks are kept in five +global arrays of pointers; we refer to the individual entries of these +arrays by symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc. +is either |null| or a pointer to the reference count of a token list. + +The variable |biggest_used_mark| is an aid to try and keep the code +somehwat efficient without too much extra work: it registers the +highest mark class ever instantiated by the user, so the loops +in |fire_up| and |vsplit| do not have to traverse the full range +|0..biggest_mark|. + +@c +halfword top_marks_array[(biggest_mark + 1)]; +halfword first_marks_array[(biggest_mark + 1)]; +halfword bot_marks_array[(biggest_mark + 1)]; +halfword split_first_marks_array[(biggest_mark + 1)]; +halfword split_bot_marks_array[(biggest_mark + 1)]; +halfword biggest_used_mark; + +@ @c +void initialize_marks(void) +{ + int i; + biggest_used_mark = 0; + for (i = 0; i <= biggest_mark; i++) { + top_mark(i) = null; + first_mark(i) = null; + bot_mark(i) = null; + split_first_mark(i) = null; + split_bot_mark(i) = null; + } +} + + +@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is +scanning a control sequence whose |cur_cmd| is either |call|, |long_call|, +|outer_call|, or |long_outer_call|. The control sequence definition +appears in the token list whose reference count is in location |cur_chr| +of |mem|. + +The global variable |long_state| will be set to |call| or to |long_call|, +depending on whether or not the control sequence disallows \.{\\par} +in its parameters. The |get_next| routine will set |long_state| to +|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer} +control sequence occurs in the midst of an argument. + +@c +int long_state; /* governs the acceptance of \.{\\par} */ + +@ The parameters, if any, must be scanned before the macro is expanded. +Parameters are token lists without reference counts. They are placed on +an auxiliary stack called |pstack| while they are being scanned, since +the |param_stack| may be losing entries during the matching process. +(Note that |param_stack| can't be gaining entries, since |macro_call| is +the only routine that puts anything onto |param_stack|, and it +is not recursive.) + +@c +halfword pstack[9]; /* arguments supplied to a macro */ + + +@ After parameter scanning is complete, the parameters are moved to the +|param_stack|. Then the macro body is fed to the scanner; in other words, +|macro_call| places the defined text of the control sequence at the +top of\/ \TeX's input stack, so that |get_next| will proceed to read it +next. + +The global variable |cur_cs| contains the |eqtb| address of the control sequence +being expanded, when |macro_call| begins. If this control sequence has not been +declared \.{\\long}, i.e., if its command code in the |eq_type| field is +not |long_call| or |long_outer_call|, its parameters are not allowed to contain +the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro +call is aborted, and the \.{\\par} will be rescanned. + +@c +void macro_call(void) +{ /* invokes a user-defined control sequence */ + halfword r; /* current node in the macro's token list */ + halfword p = null; /* current node in parameter token list being built */ + halfword q; /* new node being put into the token list */ + halfword s; /* backup pointer for parameter matching */ + halfword t; /* cycle pointer for backup recovery */ + halfword u, v; /* auxiliary pointers for backup recovery */ + halfword rbrace_ptr = null; /* one step before the last |right_brace| token */ + int n = 0; /* the number of parameters scanned */ + halfword unbalance; /* unmatched left braces in current parameter */ + halfword m = 0; /* the number of tokens or groups (usually) */ + halfword ref_count; /* start of the token list */ + int save_scanner_status = scanner_status; /* |scanner_status| upon entry */ + halfword save_warning_index = warning_index; /* |warning_index| upon entry */ + int match_chr = 0; /* character used in parameter */ + warning_index = cur_cs; + ref_count = cur_chr; + r = token_link(ref_count); + if (int_par(tracing_macros_code) > 0) { + /* Show the text of the macro being expanded */ + begin_diagnostic(); + print_ln(); + print_cs(warning_index); + token_show(ref_count); + end_diagnostic(false); + } + if (token_info(r) == protected_token) + r = token_link(r); + if (token_info(r) != end_match_token) { + /* Scan the parameters and make |link(r)| point to the macro body; but + |return| if an illegal \.{\\par} is detected */ + /* At this point, the reader will find it advisable to review the explanation + of token list format that was presented earlier, since many aspects of that + format are of importance chiefly in the |macro_call| routine. + + The token list might begin with a string of compulsory tokens before the + first |match| or |end_match|. In that case the macro name is supposed to be + followed by those tokens; the following program will set |s=null| to + represent this restriction. Otherwise |s| will be set to the first token of + a string that will delimit the next parameter. + */ + + scanner_status = matching; + unbalance = 0; + long_state = eq_type(cur_cs); + if (long_state >= outer_call_cmd) + long_state = long_state - 2; + do { + set_token_link(temp_token_head, null); + if ((token_info(r) >= end_match_token) + || (token_info(r) < match_token)) { + s = null; + } else { + match_chr = token_info(r) - match_token; + s = token_link(r); + r = s; + p = temp_token_head; + m = 0; + } + /* Scan a parameter until its delimiter string has been found; or, if |s=null|, + simply scan the delimiter string; */ + + /* If |info(r)| is a |match| or |end_match| command, it cannot be equal to + any token found by |get_token|. Therefore an undelimited parameter---i.e., + a |match| that is immediately followed by |match| or |end_match|---will + always fail the test `|cur_tok=info(r)|' in the following algorithm. */ + CONTINUE: + get_token(); /* set |cur_tok| to the next token of input */ + if (cur_tok == token_info(r)) { + /* Advance |r|; |goto found| if the parameter delimiter has been + fully matched, otherwise |goto continue| */ + /* A slightly subtle point arises here: When the parameter delimiter ends + with `\.{\#\{}', the token list will have a left brace both before and + after the |end_match|\kern-.4pt. Only one of these should affect the + |align_state|, but both will be scanned, so we must make a correction. + */ + r = token_link(r); + if ((token_info(r) >= match_token) + && (token_info(r) <= end_match_token)) { + if (cur_tok < left_brace_limit) + decr(align_state); + goto FOUND; + } else { + goto CONTINUE; + } + + } + /* Contribute the recently matched tokens to the current parameter, and + |goto continue| if a partial match is still in effect; but abort if |s=null| */ + + /* When the following code becomes active, we have matched tokens from |s| to + the predecessor of |r|, and we have found that |cur_tok<>info(r)|. An + interesting situation now presents itself: If the parameter is to be + delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}', + we want to contribute one `\.a' to the current parameter and resume + looking for a `\.b'. The program must account for such partial matches and + for others that can be quite complex. But most of the time we have |s=r| + and nothing needs to be done. + + Incidentally, it is possible for \.{\\par} tokens to sneak in to certain + parameters of non-\.{\\long} macros. For example, consider a case like + `\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed + by an exclamation point. In such situations it does not seem appropriate + to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of + the rules. */ + + if (s != r) { + if (s == null) { + /* Report an improper use of the macro and abort */ + print_err("Use of "); + sprint_cs(warning_index); + tprint(" doesn't match its definition"); + help4 + ("If you say, e.g., `\\def\\a1{...}', then you must always", + "put `1' after `\\a', since control sequence names are", + "made up of letters only. The macro here has not been", + "followed by the required stuff, so I'm ignoring it."); + error(); + goto EXIT; + + } else { + t = s; + do { + store_new_token(token_info(t)); + incr(m); + u = token_link(t); + v = s; + while (1) { + if (u == r) { + if (cur_tok != token_info(v)) { + goto DONE; + } else { + r = token_link(v); + goto CONTINUE; + } + } + if (token_info(u) != token_info(v)) + goto DONE; + u = token_link(u); + v = token_link(v); + } + DONE: + t = token_link(t); + } while (t != r); + r = s; /* at this point, no tokens are recently matched */ + } + } + + if (cur_tok == par_token) + if (long_state != long_call_cmd) + if (!int_par(suppress_long_error_code)) { + goto RUNAWAY; + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + /* Contribute an entire group to the current parameter */ + unbalance = 1; + while (1) { + fast_store_new_token(cur_tok); + get_token(); + if (cur_tok == par_token) { + if (long_state != long_call_cmd) { + if (!int_par(suppress_long_error_code)) { + goto RUNAWAY; + + } + } + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + break; + } + } + } + rbrace_ptr = p; + store_new_token(cur_tok); + + } else { + /* Report an extra right brace and |goto continue| */ + back_input(); + print_err("Argument of "); + sprint_cs(warning_index); + tprint(" has an extra }"); + help6 + ("I've run across a `}' that doesn't seem to match anything.", + "For example, `\\def\\a#1{...}' and `\\a}' would produce", + "this error. If you simply proceed now, the `\\par' that", + "I've just inserted will cause me to report a runaway", + "argument that might be the root of the problem. But if", + "your `}' was spurious, just type `2' and it will go away."); + incr(align_state); + long_state = call_cmd; + cur_tok = par_token; + ins_error(); + goto CONTINUE; + /* a white lie; the \.{\\par} won't always trigger a runaway */ + } + } else { + /* Store the current token, but |goto continue| if it is + a blank space that would become an undelimited parameter */ + if (cur_tok == space_token) + if (token_info(r) <= end_match_token) + if (token_info(r) >= match_token) + goto CONTINUE; + store_new_token(cur_tok); + + } + incr(m); + if (token_info(r) > end_match_token) + goto CONTINUE; + if (token_info(r) < match_token) + goto CONTINUE; + FOUND: + if (s != null) { + /* Tidy up the parameter just scanned, and tuck it away */ + /* If the parameter consists of a single group enclosed in braces, we must + strip off the enclosing braces. That's why |rbrace_ptr| was introduced. */ + if ((m == 1) && (token_info(p) < right_brace_limit) + && (p != temp_token_head)) { + set_token_link(rbrace_ptr, null); + free_avail(p); + p = token_link(temp_token_head); + pstack[n] = token_link(p); + free_avail(p); + } else { + pstack[n] = token_link(temp_token_head); + } + incr(n); + if (int_par(tracing_macros_code) > 0) { + begin_diagnostic(); + print_nl(match_chr); + print_int(n); + tprint("<-"); + show_token_list(pstack[n - 1], null, 1000); + end_diagnostic(false); + } + + } + + /* now |info(r)| is a token whose command code is either |match| or |end_match| */ + } while (token_info(r) != end_match_token); + + } + /* Feed the macro body and its parameters to the scanner */ + /* Before we put a new token list on the input stack, it is wise to clean off + all token lists that have recently been depleted. Then a user macro that ends + with a call to itself will not require unbounded stack space. */ + while ((istate == token_list) && (iloc == null) + && (token_type != v_template)) + end_token_list(); /* conserve stack space */ + begin_token_list(ref_count, macro); + iname = warning_index; + iloc = token_link(r); + if (n > 0) { + if (param_ptr + n > max_param_stack) { + max_param_stack = param_ptr + n; + if (max_param_stack > param_size) + overflow("parameter stack size", (unsigned) param_size); + } + for (m = 0; m <= n - 1; m++) + param_stack[param_ptr + m] = pstack[m]; + param_ptr = param_ptr + n; + } + goto EXIT; + RUNAWAY: + /* Report a runaway argument and abort */ + /* If |long_state=outer_call|, a runaway argument has already been reported. */ + if (long_state == call_cmd) { + runaway(); + print_err("Paragraph ended before "); + sprint_cs(warning_index); + tprint(" was complete"); + help3("I suspect you've forgotten a `}', causing me to apply this", + "control sequence to too much text. How can we recover?", + "My plan is to forget the whole thing and hope for the best."); + back_error(); + } + pstack[n] = token_link(temp_token_head); + align_state = align_state - unbalance; + for (m = 0; m <= n; m++) + flush_list(pstack[m]); + + EXIT: + scanner_status = save_scanner_status; + warning_index = save_warning_index; +} |