1 : /*
2 : * OpenBIOS - free your system!
3 : * ( FCode tokenizer )
4 : *
5 : * macros.c - macro initialization and functions.
6 : *
7 : * This program is part of a free implementation of the IEEE 1275-1994
8 : * Standard for Boot (Initialization Configuration) Firmware.
9 : *
10 : * Copyright (C) 2001-2005 by Stefan Reinauer <stepan@openbios.org>
11 : *
12 : * This program is free software; you can redistribute it and/or modify
13 : * it under the terms of the GNU General Public License as published by
14 : * the Free Software Foundation; version 2 of the License.
15 : *
16 : * This program is distributed in the hope that it will be useful,
17 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : * GNU General Public License for more details.
20 : *
21 : * You should have received a copy of the GNU General Public License
22 : * along with this program; if not, write to the Free Software
23 : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
24 : *
25 : */
26 :
27 : /* **************************************************************************
28 : * Modifications made in 2005 by IBM Corporation
29 : * (C) Copyright 2005 IBM Corporation. All Rights Reserved.
30 : * Modifications Author: David L. Paktor dlpaktor@us.ibm.com
31 : **************************************************************************** */
32 :
33 : /* **************************************************************************
34 : *
35 : * Support functions for the MACROS vocabulary, implemented
36 : * as a TIC-Headerlist type of data structure, and linked in
37 : * to the Global Vocabulary.
38 : *
39 : **************************************************************************** */
40 :
41 : /* **************************************************************************
42 : *
43 : * Functions Exported:
44 : * init_macros Initialize the link-pointers in the
45 : * initial "Built-In" portion of
46 : * the macros vocabulary
47 : * add_user_macro Add an entry to the macros vocabulary
48 : * skip_user_macro Consume a Macro definition if Ignoring
49 : *
50 : **************************************************************************** */
51 :
52 : #include <stdio.h>
53 : #include <stdlib.h>
54 : #if defined(__linux__) && ! defined(__USE_BSD)
55 : #define __USE_BSD
56 : #endif
57 : #include <string.h>
58 : #include <errno.h>
59 :
60 : #include "errhandler.h"
61 : #include "ticvocab.h"
62 : #include "stream.h"
63 : #include "scanner.h"
64 : #include "dictionary.h"
65 : #include "devnode.h"
66 : #include "macros.h"
67 :
68 : /* **************************************************************************
69 : *
70 : * Internal Static Variables
71 : * macros_tbl Initial array of "Built-In" Macros
72 : * number_of_builtin_macros Number of "Built-In" Macro entries.
73 : *
74 : **************************************************************************** */
75 :
76 : /* **************************************************************************
77 : *
78 : * Revision History:
79 : * Thu, 27 Oct 2005 by David L. Paktor
80 : * Identify the macros that resolve to a single word.
81 : * Remove them from here and enter them as synonymous entries
82 : * in the Tokens, Specials or Shared-words vocabularies.
83 : * Wed, 30 Nov 2005 by David L. Paktor
84 : * Allow user-definition of macros.
85 : * Fri, 06 Jan 2006 by David L. Paktor
86 : * Re-define the Macros as a TIC-Headerlist, and make them
87 : * part of the Global Vocabulary
88 : *
89 : **************************************************************************** */
90 :
91 :
92 :
93 : /* **************************************************************************
94 : *
95 : * Function name: macro_recursion_error
96 : * Synopsis: Function that will go temporarily into the FUNCT
97 : * field of a Macro's TIC-entry, to protect against
98 : * recursive macro invocations.
99 : *
100 : * Inputs:
101 : * Parameters:
102 : * pfield Param field of the TIC-entry; unused.
103 : * Global Variables:
104 : * statbuf The name being invoked erroneously.
105 : *
106 : * Outputs:
107 : * Returned Value: NONE
108 : * Printout:
109 : * Error Message.
110 : *
111 : * Error Detection:
112 : * If this function is called, it is an ERROR
113 : *
114 : * Extraneous Remarks:
115 : * This Tokenizer does not have the early-binding characterisitics
116 : * of FORTH; its Macros are strings, evaluated when invoked
117 : * rather than when they are defined. A reference to a name
118 : * that matches the macro would cause recursion, possibly
119 : * infinite. We will not allow that.
120 : *
121 : **************************************************************************** */
122 :
123 : static void macro_recursion_error( tic_param_t pfield)
124 1 : {
125 1 : tokenization_error( TKERROR,
126 : "Recursive invocation of macro named %s\n", statbuf);
127 1 : }
128 :
129 :
130 : /* **************************************************************************
131 : *
132 : * Function name: eval_mac_string
133 : * Synopsis: Function that goes into FUNCT field of a TIC-entry
134 : * in the Macros list. Protect against recursion.
135 : *
136 : * Inputs:
137 : * Parameters:
138 : * pfield Param field of the TIC-entry
139 : * Global Variables:
140 : * tic_found The TIC-entry that has just been found;
141 : * it's the entry for this Macro.
142 : *
143 : * Outputs:
144 : * Returned Value: NONE
145 : * Global Variables:
146 : * report_multiline Cleared to FALSE
147 : * Global Behavior:
148 : * The Macro will be evaluated as string input.
149 : *
150 : * Error Detection:
151 : * An attempt at recursion will be detected because the FUNCT field
152 : * of the Macro entry will have been temporarily be replaced by
153 : * macro_recursion_error()
154 : *
155 : * Process Explanation:
156 : * Save the address of the routine that is in the FUNCT field
157 : * of the entry for this Macro. (Hey! It's this routine...)
158 : * Replace the FUNCT field of the Macro entry with the Macro
159 : * Recursion Error Detection routine
160 : * Pass the address of the "resumption" routine and its argument
161 : * (the entry for this Macro), to push_source()
162 : * Recast the type of the parameter field to a string
163 : * Make it the new Input Source Buffer.
164 : * Suspend multi-line warning; see comment in body of add_user_macro()
165 : * The multi-line warning flag is kept by push_source()
166 : *
167 : * Still to be done:
168 : * If an error is encountered during Macro evaluation, display
169 : * supplemental information giving the name of the Macro
170 : * being run, and the file and line number in which it was
171 : * defined.
172 : * This will require changes to the way user Macros are added
173 : * and retained, and to the way error messages are displayed.
174 : *
175 : * Revision History:
176 : * Updated Thu, 23 Feb 2006 by David L. Paktor
177 : * Do not process Macros (or, for that matter, User-defined
178 : * Symbols or FLOADed files) with a routine that calls
179 : * its own instance of tokenize(), because the Macro
180 : * (etc.) might contain a phrase (such as the start of
181 : * a conditional) that must be terminated within the
182 : * body of a file, thus causing an undeserved Error.
183 : * Instead, they need to be handled in a more sophis-
184 : * ticated way, tied in with the operation of get_word()
185 : * perhaps, that will make a smooth transition between
186 : * the body of the Macro and the resumption of processing
187 : * the source file. The end-of-file will only be seen
188 : * at the end of an actual input file or when getting
189 : * a delimited string.
190 : * Updated Fri, 24 Feb 2006 by David L. Paktor
191 : * Re-integrate Recursion Error Detection with the above.
192 : *
193 : **************************************************************************** */
194 :
195 : /* **************************************************************************
196 : *
197 : * In order to integrate Recursion Error Detection with the smooth
198 : * transition to resumption of processing the source file, we
199 : * need to create a "resumption" routine that will restore the
200 : * normal behavior of the macro after it's completed, by re-
201 : * instating the address of the normal Macro-invocation routine;
202 : * that routine, of course, is the one that passes the address
203 : * of the "resumption" routine to push_source(). In order to
204 : * get around this chicken-and-egg dilemma, we will create a
205 : * local static variable into which the address of the normal
206 : * Macro-invocation routine will be stored. We actually only
207 : * need it once, but we'd rather avoid the overhead of checking,
208 : * every time, whether it has already been set; since it's always
209 : * the same, there's no harm in storing it every time.
210 : *
211 : **************************************************************************** */
212 :
213 : typedef void (*vfunct)(); /* Pointer to function returning void */
214 : static vfunct sav_mac_funct ;
215 :
216 :
217 : /* **************************************************************************
218 : *
219 : * This "resumption" routine will be called by pop_source()
220 : * The parameter is the Macro dictionary-entry whose behavior
221 : * is to be restored.
222 : *
223 : **************************************************************************** */
224 :
225 : static void mac_string_recovery( tic_hdr_t *macro_entry)
226 370 : {
227 370 : (*macro_entry).funct = sav_mac_funct;
228 370 : (*macro_entry).ign_func = sav_mac_funct;
229 370 : }
230 :
231 : /* **************************************************************************
232 : *
233 : * The normal Macro-invocation routine, at last...
234 : *
235 : **************************************************************************** */
236 : static void eval_mac_string( tic_param_t pfield)
237 370 : {
238 370 : int mac_str_len = strlen(pfield.chr_ptr);
239 : /* We can't use (*tic_found).pfld_size for the string length
240 : * because, if this is an alias for a macro, it will be zero...
241 : */
242 : /* We can change that by de-coupling the decision to free the
243 : * param-field from whether pfld_size is non-zero (by intro-
244 : * ducing yet another field into the tic_param_t struct),
245 : * but we're not doing that today...
246 : */
247 :
248 370 : sav_mac_funct = *tic_found->funct;
249 370 : (*tic_found).funct = macro_recursion_error;
250 370 : (*tic_found).ign_func = macro_recursion_error;
251 370 : push_source( mac_string_recovery, tic_found, FALSE);
252 370 : report_multiline = FALSE; /* Must be done AFTER call to push_source()
253 : * because report_multiline is part of
254 : * the state that push_source() saves.
255 : */
256 370 : init_inbuf( pfield.chr_ptr, mac_str_len);
257 370 : }
258 :
259 : /* **************************************************************************
260 : *
261 : * Builtin Macros do not need Recursion Error Detection.
262 : * Intermediate routine to convert parameter type.
263 : *
264 : **************************************************************************** */
265 : static void eval_builtin_mac( tic_param_t pfield)
266 99 : {
267 99 : eval_string( pfield.chr_ptr);
268 99 : }
269 : /* **************************************************************************
270 : *
271 : * Make a macro, because we might eliminate this layer later on.
272 : *
273 : **************************************************************************** */
274 : #define EVAL_MAC_FUNC eval_mac_string
275 : #define BUILTIN_MAC_FUNC eval_builtin_mac
276 :
277 : /* **************************************************************************
278 : *
279 : * Initialization macro definition
280 : *
281 : **************************************************************************** */
282 :
283 : #define BUILTIN_MACRO(nam, alias) BUILTIN_MAC_TIC(nam, BUILTIN_MAC_FUNC, alias )
284 :
285 : static tic_mac_hdr_t macros_tbl[] = {
286 : BUILTIN_MACRO( "(.)", "dup abs <# u#s swap sign u#>") ,
287 :
288 :
289 : BUILTIN_MACRO( "?", "@ .") ,
290 : BUILTIN_MACRO( "1+", "1 +") ,
291 : BUILTIN_MACRO( "1-", "1 -") ,
292 : BUILTIN_MACRO( "2+", "2 +") ,
293 : BUILTIN_MACRO( "2-", "2 -") ,
294 :
295 : BUILTIN_MACRO( "accept", "span @ -rot expect span @ swap span !") ,
296 : BUILTIN_MACRO( "allot", "0 max 0 ?do 0 c, loop") ,
297 : BUILTIN_MACRO( "blank", "bl fill") ,
298 : BUILTIN_MACRO( "carret", "h# d") ,
299 : BUILTIN_MACRO( ".d", "base @ swap h# a base ! . base !") ,
300 : BUILTIN_MACRO( "decode-bytes", ">r over r@ + swap r@ - rot r>") ,
301 : BUILTIN_MACRO( "3drop", "drop 2drop") ,
302 : BUILTIN_MACRO( "3dup", "2 pick 2 pick 2 pick") ,
303 : BUILTIN_MACRO( "erase", "0 fill") ,
304 : BUILTIN_MACRO( ".h", "base @ swap h# 10 base ! . base !") ,
305 : BUILTIN_MACRO( "linefeed", "h# a") ,
306 :
307 : BUILTIN_MACRO( "s.", "(.) type space") ,
308 : BUILTIN_MACRO( "space", "bl emit") ,
309 : BUILTIN_MACRO( "spaces", "0 max 0 ?do space loop") ,
310 : BUILTIN_MACRO( "(u.)", "<# u#s u#>") ,
311 : BUILTIN_MACRO( "?leave", "if leave then"),
312 : };
313 :
314 : static const int number_of_builtin_macros =
315 : sizeof(macros_tbl)/sizeof(tic_mac_hdr_t);
316 :
317 : /* **************************************************************************
318 : *
319 : * Function name: init_macros
320 : * Synopsis: Initialize the link-pointers in the "Built-In"
321 : * portion of the macros vocabulary, dynamically.
322 : *
323 : * Inputs:
324 : * Parameters:
325 : * tic_vocab_ptr Pointer to Global Vocab Pointer
326 : * Global Variables:
327 : * macros_tbl Initial "Built-In" Macros array
328 : * number_of_builtin_macros Number of "Built-In" Macro entries
329 : *
330 : * Outputs:
331 : * Returned Value: NONE
332 : * Global Variables:
333 : * The link-fields of the initial "Built-In" Macros array entries
334 : * will be filled in.
335 : * Supplied Pointers:
336 : * *tic_vocab_ptr Updated to "tail" of Macros array
337 : *
338 : **************************************************************************** */
339 :
340 : void init_macros( tic_hdr_t **tic_vocab_ptr )
341 154 : {
342 154 : init_tic_vocab( (tic_hdr_t *)macros_tbl,
343 : number_of_builtin_macros,
344 : tic_vocab_ptr );
345 154 : }
346 :
347 :
348 : /* **************************************************************************
349 : *
350 : * Function name: print_if_mac_err
351 : * Synopsis: Report a user-macro definition error, if so be.
352 : *
353 : * Inputs:
354 : * Parameters:
355 : * failure TRUE if error was detected
356 : * func_cpy STRDUP() of function name, for error message
357 : *
358 : * Outputs:
359 : * Returned Value: NONE
360 : * Memory Freed
361 : * Contents of func_cpy, error or not.
362 : * Printout:
363 : * Error message, if failure is TRUE.
364 : *
365 : **************************************************************************** */
366 : static void print_if_mac_err( bool failure, char *func_cpy)
367 113 : {
368 113 : if ( failure )
369 : {
370 2 : tokenization_error( TKERROR,
371 : "%s directive expects name and definition on the same line\n",
372 : strupr(func_cpy));
373 : }
374 113 : free( func_cpy);
375 113 : }
376 :
377 :
378 : /* **************************************************************************
379 : *
380 : * Function name: add_user_macro
381 : * Synopsis: Parse input and add a user-defined Macro.
382 : *
383 : * Associated Tokenizer directive: [MACRO]
384 : *
385 : * Inputs:
386 : * Parameters: NONE
387 : * Global Variables:
388 : * pc Input-source Scanning pointer
389 : * statbuf Symbol retrieved from input stream.
390 : * in_tokz_esc TRUE if in "Tokenizer-Escape" mode
391 : * current_definitions Pointer to Current Vocabulary pointer,
392 : * either Global or Current Device-Node
393 : * tokz_esc_vocab "Tokenizer Escape" Vocab pointer
394 : *
395 : * Outputs:
396 : * Returned Value: NONE
397 : * Global Variables:
398 : * *current_definitions { One of these will point }
399 : * tokz_esc_vocab { to the new entry }
400 : * Memory Allocated:
401 : * Copy of directive, for error message
402 : * Copy of Macro name
403 : * Copy of Macro body
404 : * Memory for the new entry will be allocated by support routine.
405 : * When Freed?
406 : * Copy of directive: When error might be reported.
407 : * Macro name, body and entry: Upon end of tokenization, or when
408 : * RESET-SYMBOLS is issued in the same mode and Scope as when
409 : * the Macro was defined.
410 : *
411 : * Error Detection:
412 : * At least two words in the input stream are expected to be on
413 : * the same line as the directive. The get_word_in_line()
414 : * and get_rest_of_line() routines will check for that;
415 : * we will issue the Error Message for either condition.
416 : * Check if the Macro name is a duplicate; warn_if_duplicate()
417 : * routine will issue message.
418 : *
419 : * Process Explanation:
420 : * We start just after the directive has been recognized.
421 : * Get one word in line -- this is the macro name
422 : * Get input to end of line. This is the "body" of the macro.
423 : * Add the Macro to the Current vocab, using support routine.
424 : * Set the definer field to MACRO_DEF and the Function to the
425 : * same one that's used for the built-in macros.
426 : * User-defined Macros may need to be processed while ignoring
427 : * (because they might include conditional-operators, etc.)
428 : * We will set the ign_func the same as the active function.
429 : *
430 : * To be considered:
431 : * Do we want to do further filtration?
432 : * Remove comments?
433 : * Compress whitespace?
434 : * Allow backslash at end of line to continue to next line?
435 : *
436 : * Extraneous Remarks:
437 : * The scope of User-Macro definitions will follow the same rules
438 : * as all other definition types: if Device-Definitions are
439 : * in effect, the scope of the new Macro definition will be
440 : * confined to the current Device-Node; if Global-Definitions
441 : * are in effect when it is defined, its scope will be Global;
442 : * if it was declared when we were in "Tokenizer Escape" mode,
443 : * then its scope will be limited to "Tokenizer Escape" mode.
444 : *
445 : **************************************************************************** */
446 :
447 : /* This pointer is exported to this file only */
448 : extern tic_hdr_t *tokz_esc_vocab ;
449 :
450 : void add_user_macro( void)
451 93 : {
452 : char *macroname;
453 : char *macrobody;
454 93 : bool failure = TRUE;
455 :
456 : /* Copy of function name, for error message */
457 93 : char *func_cpy = strdup( statbuf);
458 :
459 93 : if ( get_word_in_line( NULL ) )
460 : {
461 : /* This is the Macro name */
462 92 : macroname = strdup( statbuf);
463 :
464 92 : if ( INVERSE(get_rest_of_line() ) )
465 : {
466 : /* No body on line */
467 1 : free( macroname);
468 :
469 : }else{
470 : /* We have valid Macro body on line */
471 91 : int mac_body_len = 0;
472 :
473 91 : tic_hdr_t **target_vocab = current_definitions;
474 91 : if ( in_tokz_esc ) target_vocab = &tokz_esc_vocab ;
475 :
476 91 : warn_if_duplicate( macroname);
477 91 : trace_creation( MACRO_DEF, macroname);
478 :
479 : /* Tack on a new-line, so that a remark will appear
480 : * to be properly terminated. This might trigger
481 : * an undeserved multi-line warning if the Macro
482 : * is an improperly terminated quote; we will work
483 : * around that problem by temporarily suspending
484 : * multi-line warnings during macro processing.
485 : */
486 91 : strcat( statbuf, "\n");
487 91 : macrobody = strdup( statbuf);
488 91 : mac_body_len = strlen(macrobody);
489 :
490 91 : add_tic_entry( macroname, EVAL_MAC_FUNC,
491 : (TIC_P_DEFLT_TYPE)macrobody,
492 : MACRO_DEF, mac_body_len,
493 : EVAL_MAC_FUNC, target_vocab );
494 91 : failure = FALSE;
495 : }
496 : }
497 :
498 93 : print_if_mac_err( failure, func_cpy);
499 93 : }
500 :
501 : /* **************************************************************************
502 : *
503 : * Function name: skip_user_macro
504 : * Synopsis: Consume the text of a user-defined Macro from the
505 : * Input Stream, with no processing. (Called when
506 : * a user-Macro definer occurs in a segment that
507 : * is being Ignored.)
508 : *
509 : * Inputs:
510 : * Parameters:
511 : * pfield "Parameter field" pointer, to satisfy
512 : * the calling convention, but not used
513 : * Global Variables:
514 : * statbuf Word currently being processed.
515 : *
516 : * Outputs:
517 : * Returned Value: NONE
518 : *
519 : * Error Detection:
520 : * At least two words in the input stream are expected to be on
521 : * the same line as the user-Macro definer, same as when the
522 : * directives occurs in a segment that is not being Ignored.
523 : * The get_word_in_line() and get_rest_of_line() routines
524 : * will check for condition., we will issue the Error Message.
525 : *
526 : * Process Explanation:
527 : * We need to protect against the case of a macro-definition that
528 : * invokes a directive that alters Conditional processing...
529 : *
530 : **************************************************************************** */
531 : void skip_user_macro( tic_bool_param_t pfield )
532 20 : {
533 20 : bool failure = TRUE;
534 20 : char *func_cpy = strdup( statbuf);
535 20 : if ( get_word_in_line( NULL ) )
536 : {
537 20 : if ( get_rest_of_line() )
538 : {
539 20 : failure = FALSE;
540 : }
541 : }
542 :
543 20 : print_if_mac_err( failure, func_cpy);
544 :
545 20 : }
|