1 /****************************************************************************
2 *
3 *  This code is Public Domain.
4 *
5 *  ========================================================================
6 *
7 * Description:  preprocessor
8 *
9 ****************************************************************************/
10 
11 #include <ctype.h>
12 #include <stdarg.h>
13 
14 #include "globals.h"
15 #include "memalloc.h"
16 #include "parser.h"
17 #include "condasm.h"
18 #include "tokenize.h"
19 #include "equate.h"
20 #include "macro.h"
21 #include "input.h"
22 #include "fastpass.h"
23 #include "listing.h"
24 
25 #define REMOVECOMENT 0 /* 1=remove comments from source       */
26 
27 extern ret_code (* const directive_tab[])( int, struct asm_tok[] );
28 
29 #ifdef DEBUG_OUT
30 int_32 cntppl0;    /* count preprocessed lines 1 */
31 int_32 cntppl1;    /* count preprocessed lines 2 */
32 int_32 cntppl2;    /* count lines NOT handled by preprocessor */
33 #endif
34 
35 /* preprocessor directive or macro procedure is preceded
36  * by a code label.
37  */
WriteCodeLabel(char * line,struct asm_tok tokenarray[])38 ret_code WriteCodeLabel( char *line, struct asm_tok tokenarray[] )
39 /****************************************************************/
40 {
41     int oldcnt;
42     int oldtoken;
43     char oldchar;
44 
45     if ( tokenarray[0].token != T_ID ) {
46         return( EmitErr( SYNTAX_ERROR_EX, tokenarray[0].string_ptr ) );
47     }
48     /* ensure the listing is written with the FULL source line */
49     if ( CurrFile[LST] ) LstWrite( LSTTYPE_LABEL, 0, NULL );
50     /* v2.04: call ParseLine() to parse the "label" part of the line */
51     oldcnt = Token_Count;
52     oldtoken = tokenarray[2].token;
53     oldchar = *tokenarray[2].tokpos;
54     Token_Count = 2;
55     tokenarray[2].token = T_FINAL;
56     *tokenarray[2].tokpos = NULLC;
57     ParseLine( tokenarray );
58     if ( Options.preprocessor_stdout == TRUE )
59         WritePreprocessedLine( line );
60     Token_Count = oldcnt;
61     tokenarray[2].token = oldtoken;
62     *tokenarray[2].tokpos = oldchar;
63     return( NOT_ERROR );
64 }
65 
66 /* PreprocessLine() is the "preprocessor".
67  * 1. the line is tokenized with Tokenize(), Token_Count set
68  * 2. (text) macros are expanded by ExpandLine()
69  * 3. "preprocessor" directives are executed
70  */
PreprocessLine(char * line,struct asm_tok tokenarray[])71 int PreprocessLine( char *line, struct asm_tok tokenarray[] )
72 /***********************************************************/
73 {
74     int i;
75 
76     /* v2.11: GetTextLine() removed - this is now done in ProcessFile() */
77 
78     /* v2.08: moved here from GetTextLine() */
79     ModuleInfo.CurrComment = NULL;
80     /* v2.06: moved here from Tokenize() */
81     ModuleInfo.line_flags = 0;
82     /* Token_Count is the number of tokens scanned */
83     Token_Count = Tokenize( line, 0, tokenarray, TOK_DEFAULT );
84 
85 #ifdef DEBUG_OUT
86     cntppl0++;
87     if ( ModuleInfo.GeneratedCode )
88         DebugMsg1(("PreprocessLine: >%s<\n", line ));
89     else
90         DebugMsg1(("PreprocessLine(%s): >%s< cmt=%s\n", GetTopSrcName(), line, ModuleInfo.CurrComment ? ModuleInfo.CurrComment : "" ));
91 #endif
92 
93 #if REMOVECOMENT == 0
94     if ( Token_Count == 0 && ( CurrIfState == BLOCK_ACTIVE || ModuleInfo.listif ) )
95         LstWriteSrcLine();
96 #endif
97 
98     if ( Token_Count == 0 )
99         return( 0 );
100 
101 #ifdef DEBUG_OUT
102     /* option -np, skip preprocessor? */
103     if ( Options.skip_preprocessor )
104         return( Token_Count );
105 #endif
106 
107     /* CurrIfState != BLOCK_ACTIVE && Token_Count == 1 | 3 may happen
108      * if a conditional assembly directive has been detected by Tokenize().
109      * However, it's important NOT to expand then */
110     if ( CurrIfState == BLOCK_ACTIVE ) {
111         if ( ( tokenarray[Token_Count].bytval & TF3_EXPANSION ? ExpandText( line, tokenarray, TRUE ) : ExpandLine( line, tokenarray ) ) < NOT_ERROR )
112             return( 0 );
113     }
114 
115     DebugCmd( cntppl1++ );
116 
117     i = 0;
118     if ( Token_Count > 2 && ( tokenarray[1].token == T_COLON || tokenarray[1].token == T_DBL_COLON ) )
119         i = 2;
120 
121     /* handle "preprocessor" directives:
122      * IF, ELSE, ENDIF, ...
123      * FOR, REPEAT, WHILE, ...
124      * PURGE
125      * INCLUDE
126      * since v2.05, error directives are no longer handled here!
127      */
128     if ( tokenarray[i].token == T_DIRECTIVE &&
129         tokenarray[i].dirtype <= DRT_INCLUDE ) {
130 
131         /* if i != 0, then a code label is located before the directive */
132         if ( i > 1 ) {
133             if ( ERROR == WriteCodeLabel( line, tokenarray ) )
134                 return( 0 );
135         }
136         directive_tab[tokenarray[i].dirtype]( i, tokenarray );
137         return( 0 );
138     }
139 
140     /* handle preprocessor directives which need a label */
141 
142     if ( tokenarray[0].token == T_ID && tokenarray[1].token == T_DIRECTIVE ) {
143         struct asym *sym;
144         switch ( tokenarray[1].dirtype ) {
145         case DRT_EQU:
146             /*
147              * EQU is a special case:
148              * If an EQU directive defines a text equate
149              * it MUST be handled HERE and 0 must be returned to the caller.
150              * This will prevent further processing, nothing will be stored
151              * if FASTPASS is on.
152              * Since one cannot decide whether EQU defines a text equate or
153              * a number before it has scanned its argument, we'll have to
154              * handle it in ANY case and if it defines a number, the line
155              * must be stored and, if -EP is set, written to stdout.
156              */
157             if ( sym = CreateConstant( tokenarray ) ) {
158                 if ( sym->state != SYM_TMACRO ) {
159 #if FASTPASS
160                     if ( StoreState ) FStoreLine( 0 );
161 #endif
162                     if ( Options.preprocessor_stdout == TRUE )
163                         WritePreprocessedLine( line );
164                 }
165                 /* v2.03: LstWrite() must be called AFTER StoreLine()! */
166                 if ( ModuleInfo.list == TRUE ) {
167                     LstWrite( sym->state == SYM_INTERNAL ? LSTTYPE_EQUATE : LSTTYPE_TMACRO, 0, sym );
168                 }
169             }
170             return( 0 );
171         case DRT_MACRO:
172         case DRT_CATSTR: /* CATSTR + TEXTEQU directives */
173         case DRT_SUBSTR:
174             directive_tab[tokenarray[1].dirtype]( 1, tokenarray );
175             return( 0 );
176         }
177     }
178 
179     DebugCmd( cntppl2++ );
180     return( Token_Count );
181 }
182 
183