5 src/macro.pir - does macro substitution
9 Copyright: 2004-2005 Bernhard Schmalhofer. All Rights Reserved.
11 History: Ported from GNU m4 1.4
12 References: http://www.gnu.org/software/m4/m4.html
16 =head2 void expand_input( Hash state )
18 Loop through some input files.
19 TODO: read files in next_token()
26 .local string token_data, token_type # current token data and type
28 # go through the input, token for token
30 ( token_type, token_data ) = next_token( state )
31 expand_token( state, token_type, token_data )
32 if token_type != 'TOKEN_EOF' goto NEXT_TOKEN
38 =head2 void expand_token( Hash state, string token_type, string token_data )
40 Expand one token, according to its type. Potential macro names
41 (TOKEN_WORD) are looked up in the symbol table, to see if they have a
42 macro definition. If they have, they are expanded as macros, otherwise
43 the text are just copied to the output.
49 .param string token_type
50 .param string token_data
53 symtab = state['symtab']
55 # TOKEN_EOF and TOKEN_MACDEF are not expanded
56 if token_type == 'TOKEN_EOF' goto FINISH_EXPAND_TOKEN
57 if token_type == 'TOKEN_MACDEF' goto FINISH_EXPAND_TOKEN
59 # 'TOKEN_STRING' does the same as 'TOKEN_SIMPLE',
60 if token_type == 'TOKEN_STRING' goto SHIPOUT_TEXT
61 if token_type == 'TOKEN_SIMPLE' goto SHIPOUT_TEXT
63 if token_type != 'TOKEN_WORD' goto NO_TOKEN_WORD
64 .local int symbol_exists
65 symbol_exists = exists symtab[token_data]
66 unless symbol_exists goto SHIPOUT_TEXT
68 symbol = symtab[token_data]
69 .local string symbol_type
70 symbol_type = symbol['type']
71 if symbol_type != 'TOKEN_FUNC' goto EXPAND_MACRO
72 # there are two types of macro invocations:
75 # Most macros needs a parameter list, but e.g. __file__ has not args
76 .local int blind_no_args
77 blind_no_args = symbol['blind_no_args']
78 unless blind_no_args goto EXPAND_MACRO
79 .local string input_string
80 input_string = state['stack';'input';0;'string']
81 .local int first_char, open_parenthesis
82 first_char = ord input_string
83 open_parenthesis = ord '('
84 if first_char != open_parenthesis goto SHIPOUT_TEXT
88 expand_macro( state, symbol )
89 goto FINISH_EXPAND_TOKEN
92 printerr "unknown token type: "
97 shipout_text( state, token_data )
98 goto FINISH_EXPAND_TOKEN
104 =head2 string processed_token expand_macro( Hash state, Hash symbol )
106 The macro expansion is handled by expand_macro().
107 It parses the arguments in collect_arguments() and
108 builds a ResizablePMCArray containing the arguments.
109 The arguments themselves are stored on a local obstack.
110 expand_macro() uses call_macro() to do the call of the macro.
112 expand_macro() is potentially recursive, since it calls expand_argument(),
113 which might call expand_token (), which might call expand_macro().
121 .local int expansion_level
122 expansion_level = state['expansion_level']
123 .local int nesting_limit
124 nesting_limit = state['nesting_limit']
126 if expansion_level <= nesting_limit goto NESTING_LIMIT_NOT_REACHED_YET
127 printerr "ERROR: Recursion limit of "
128 printerr nesting_limit
129 printerr "exceeded, use -L<N> to change it"
131 NESTING_LIMIT_NOT_REACHED_YET:
132 state['expansion_level'] = expansion_level
135 arguments = new .ResizablePMCArray
136 collect_arguments( state, arguments )
139 ( text ) = call_macro( state, symbol, arguments )
140 .local string input_string
141 input_string = state['stack';'input';0;'string']
142 input_string = text . input_string
143 state['stack';'input';0;'string'] = input_string
145 expansion_level = state['expansion_level']
147 state['expansion_level'] = expansion_level
152 =head2 string processed_token call_macro( ResizablePMCArray macro, string token )
154 Apply macro to a token.
156 TODO: distinguish between TOKEN_FUNC and TOKEN_WORD
165 .local string symbol_type, symbol_name
166 symbol_type = symbol['type']
167 symbol_name = symbol['name']
170 if symbol_type != 'TOKEN_TEXT' goto NO_TOKEN_TEXT
171 text = symbol['text']
172 goto FINISH_CALL_MACRO
175 if symbol_type == 'TOKEN_FUNC' goto TOKEN_FUNC
176 printerr "INTERNAL ERROR: Bad symbol type in call_macro"
180 func = symbol['func']
181 # indirect call of subs, seems to need elaborate PIR syntax
195 =head2 void collect_arguments
197 Collect all the arguments to a call of a macro.
201 .sub collect_arguments
205 # The macro name has already been read in, thus we need to match
206 # something like "('furcht', 'Hallo Welt')"
207 # and capture the name 'furcht' and the substitution 'Hallo Welt'.
208 # Thus we need to remenber the start and the length of these two captures
210 .local string input_string
211 input_string = state['stack';'input';0;'string']
213 # We need a '(' at beginning of string
214 .local int index_opening
215 index_opening = index input_string, '('
216 if index_opening != 0 goto NOT_A_ARGUMENT_LIST
219 .local int start_index
223 .local int index_comma, index_closing, index_start_string, index_end_string, len_string
226 # find a string before ')'
227 index_closing = index input_string, ')', start_index
229 if index_closing == -1 goto NO_MORE_ARGS
230 if num_args == 0 goto SKIP_SKIP_COMMA
231 index_comma = index input_string, ',', start_index
232 if index_comma == -1 goto NO_MORE_ARGS
233 if index_closing < index_comma goto NO_MORE_ARGS
234 start_index = index_comma
236 index_start_string = index input_string, '`', start_index
237 if index_start_string == -1 goto NO_MORE_ARGS
238 inc index_start_string
239 if index_closing < index_start_string goto NO_MORE_ARGS
240 index_end_string = index input_string, "'", index_start_string
241 if index_end_string == -1 goto NO_MORE_ARGS
242 len_string = index_end_string - index_start_string
243 substr arg, input_string, index_start_string, len_string
244 start_index = index_end_string
251 substr input_string, 0, index_closing, ''
254 state['stack';'input';0;'string'] = input_string
261 # vim: expandtab shiftwidth=4 ft=pir: