diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/luatexdir/tex/expand.c | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/expand.c')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/expand.c | 967 |
1 files changed, 967 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.c b/Build/source/texk/web2c/luatexdir/tex/expand.c new file mode 100644 index 00000000000..e85c8f3bdb6 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/expand.c @@ -0,0 +1,967 @@ +/* + +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/>. + +*/ + +#include "ptexlib.h" + +/*tex + + 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|. + + 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. + +*/ + +static int expand_depth_count = 0; + +/*tex + + 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. + +*/ + +int is_in_csname = 0; + +void expand(void) +{ + /*tex token that is being ``expanded after'' */ + halfword t; + /*tex for list manipulation */ + halfword p; + /*tex for a local token list pointer */ + halfword cur_ptr; + /*tex to save the global quantity |cur_val| */ + int cv_backup; + /*tex to save |cur_val_level|, etc. */ + int cvl_backup, radix_backup, co_backup; + /*tex to save |link(backup_head)| */ + halfword backup_backup; + /*tex temporary storage of |scanner_status| */ + int save_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) { + /*tex Expand a nonmacro. */ + if (tracing_commands_par > 1) + show_cur_cmd_chr(); + switch (cur_cmd) { + case top_bot_mark_cmd: + /*tex 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) { + /*tex + + 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 { + /*tex + + 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) { + /*tex + + 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(); + /*tex 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 { + /*tex + + 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. + + 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(); + /*tex 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 if (suppress_primitive_error_par == 0) { + 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: + /*tex Manufacture a control sequence name. */ + if (cur_chr == 0) { + manufacture_csname(0); + } else if (cur_chr == 1) { + inject_last_tested_cs(); + } else { + manufacture_csname(1); + } + break; + case convert_cmd: + conv_toks(); + break; + case the_cmd: + ins_the_toks(); + break; + case if_test_cmd: + /*tex An experiment. */ + if (cur_chr == if_condition_code) { + break; + } + /*tex End of experiment. */ + conditional(); + break; + case fi_or_else_cmd: + /*tex + + 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 (tracing_ifs_par > 0) + if (tracing_commands_par <= 1) + show_cur_cmd_chr(); + if (cur_chr > if_limit) { + if (if_limit == if_code) { + /*tex Condition is not yet evaluated. */ + insert_relax(); + } 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) { + /*tex Skip to \.{\\fi}. */ + pass_text(); + } + pop_condition_stack(); + } + break; + case input_cmd: + /*tex 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; + case lua_expandable_call_cmd: + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_chr); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + break; + case lua_local_call_cmd: + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + lua_rawgeti(Luas, LUA_REGISTRYINDEX, cur_chr); + lua_call(Luas,0,0); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + break; + case variable_cmd: + do_variable(); + break; + case feedback_cmd: + do_feedback(); + break; + default: + /*tex 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 { + /*tex + + 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); +} + +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(); +} + +void manufacture_csname(boolean use) +{ + halfword p, q, r; + lstring *ss; + r = get_avail(); + p = r; + is_in_csname += 1; + do { + get_x_token(); + if (cur_cs == 0) + store_new_token(cur_tok); + } while (cur_cs == 0); + if (cur_cmd != end_cs_name_cmd) { + /*tex Complain about missing \.{\\endcsname}. */ + complain_missing_csname(); + } + /*tex Look up the characters of list |r| in the hash table, and set |cur_cs|. */ + ss = tokenlist_to_lstring(r, true); + is_in_csname -= 1; + if (use) { + if (ss->l > 0) { + cur_cs = string_lookup((char *) ss->s, ss->l); + } else { + cur_cs = null_cs; + } + last_cs_name = cur_cs ; + free_lstring(ss); + flush_list(r); + if (cur_cs == null_cs) { + /*tex skip */ + } else if (eq_type(cur_cs) == undefined_cs_cmd) { + /*tex skip */ + } else { + cur_tok = cur_cs + cs_token_flag; + back_input(); + } + } else { + if (ss->l > 0) { + no_new_control_sequence = false; + cur_cs = string_lookup((char *) ss->s, ss->l); + no_new_control_sequence = true; + } else { + /*tex the list is empty */ + cur_cs = null_cs; + } + last_cs_name = cur_cs ; + free_lstring(ss); + flush_list(r); + if (eq_type(cur_cs) == undefined_cs_cmd) { + /*tex The |save_stack| might change! */ + eq_define(cur_cs, relax_cmd, too_big_char); + }; + /*tex The control sequence will now match \.{\\relax} */ + cur_tok = cur_cs + cs_token_flag; + back_input(); + } +} + +void inject_last_tested_cs(void) +{ + if (last_cs_name != null_cs) { + cur_cs = last_cs_name; + cur_tok = last_cs_name + cs_token_flag; + back_input(); + } +} + +/*tex + + Sometimes the expansion looks too far ahead, so we want to insert a harmless + \.{\\relax} into the user's input. + +*/ + +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; +} + + +/*tex + + 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. + +*/ + +void get_x_token(void) +{ + /*tex This code sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros. */ + RESTART: + get_next(); + 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; + /*tex Now |cur_chr=null_list|. */ + goto DONE; + } + } 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; +} + +/*tex + + The |get_x_token| procedure is equivalent to two consecutive procedure calls: + |get_next; x_token|. It's |get_x_token| without the initial |get_next|. + +*/ + +void x_token(void) +{ + while (cur_cmd > max_command_cmd) { + expand(); + get_next(); + } + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + +/*tex + + 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|. + +*/ + +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; + +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; + } +} + +/*tex + + 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. + +*/ + +/*tex Governs the acceptance of \.{\\par}: */ + +int long_state; + +/*tex + + 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.) + +*/ + +/*tex The arguments supplied to a macro: */ + +halfword pstack[9]; + +/*tex + + 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. + +*/ + +/*tex This invokes a user-defined control sequence. */ + +void macro_call(void) +{ + /*tex current node in the macro's token list */ + halfword r; + /*tex current node in parameter token list being built */ + halfword p = null; + /*tex new node being put into the token list */ + halfword q; + /*tex backup pointer for parameter matching */ + halfword s; + /*tex cycle pointer for backup recovery */ + halfword t; + /*tex auxiliary pointers for backup recovery */ + halfword u, v; + /*tex one step before the last |right_brace| token */ + halfword rbrace_ptr = null; + /*tex the number of parameters scanned */ + int n = 0; + /*tex unmatched left braces in current parameter */ + halfword unbalance; + /*tex the number of tokens or groups (usually) */ + halfword m = 0; + /*tex start of the token list */ + halfword ref_count; + /*tex |scanner_status| upon entry */ + int save_scanner_status = scanner_status; + /*tex |warning_index| upon entry */ + halfword save_warning_index = warning_index; + /*tex character used in parameter */ + int match_chr = 0; + warning_index = cur_cs; + ref_count = cur_chr; + r = token_link(ref_count); + if (tracing_macros_par > 0) { + /*tex Show the text of the macro being expanded. */ + begin_diagnostic(); + print_input_level(); + // 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) { + /*tex + + 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; + } + /*tex + + 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: + /*tex Set |cur_tok| to the next token of input: */ + get_token(); + if (cur_tok == token_info(r)) { + /*tex + + 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; + } + + } + /*tex + + 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) { + /*tex 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; + /*tex At this point, no tokens are recently matched. */ + } + } + if (cur_tok == par_token) + if (long_state != long_call_cmd) + if (!suppress_long_error_par) { + goto RUNAWAY; + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + /*tex 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 (!suppress_long_error_par) { + 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 { + /*tex 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; + /*tex A white lie; the \.{\\par} won't always trigger a runaway. */ + } + } else { + /*tex + + Store the current token, but |goto continue| if it is a blank + space that would become an undelimited parameter. + + */ + if (cur_tok == space_token && token_info(r) <= end_match_token && 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 (tracing_macros_par > 0) { + begin_diagnostic(); + print_input_level(); + // print_nl(match_chr); + print(match_chr); + print_int(n); + tprint("<-"); + show_token_list(pstack[n - 1], null, 1000); + end_diagnostic(false); + } + } + /*tex + + Now |info(r)| is a token whose command code is either |match| or + |end_match|. + + */ + } while (token_info(r) != end_match_token); + } + /*tex + + 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)) { + /*tex Conserve stack space. */ + end_token_list(); + } + 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: + /*tex + + 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; +} |