1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21-module(ic_pp).
22
23-export([run/2]).
24
25-define(is_number(X), X >= $0, X =< $9).
26-define(is_upper(X), X >= $A, X =< $Z).
27-define(is_lower(X), X >= $a, X =< $z).
28-define(is_underline(X), X == $_).
29-define(is_tab(X), X == 9).
30-define(is_space(X), X == 32).
31-define(tab, 9).
32-define(space, 32).
33
34
35%%======================================================================================
36%%======================================================================================
37%%======================================================================================
38%% Preprocessor
39%%
40%% This preprocessor is equivalent to the gcc-preprocessor. It takes a file name and
41%% a list of preprocessor flags as an input and returns a processed text file.
42%%
43%% The processing is done in two phases.
44%% In the first phase the input file is tokenised into a list where all comments are
45%% replaced by a space and all "backslash-newline" sequences are removed.
46%%
47%% In the second phase all macros are expanded.
48
49%% %% %% NOTE: #if, #else, and #elif are not yet implemented.
50%% Only '#if 0' is implemented to be possible to keep old code as a comment for
51%% future refence by putting '#if 0' before it and '#endif' after it.
52%%
53%%======================================================================================
54%%======================================================================================
55%%======================================================================================
56
57
58%%======================================================================================
59%% Variables which are used throughout the program:
60%% ------------------------------------------------
61%%
62%% Command         A preprocessor command
63%% Current         Temporary variable used when tokenising the file
64%% Defs            The currently valid macro definitions
65%% Err             The current list of errors = [{file, line number, error text}, ...]
66%% File            The tokenised file (or what remains of it when expanding the macros)
67%% Flags           The preprocessor flags
68%% FN or FileName  Tbe name of the current file
69%% IfCou           Used for ifdef/ifndef/endif  values: check_all | {endif, Endif, IfLine}
70%%                   Endif = number of matching endif's yet to be found
71%%                   Ifline = the line number for the the first found ifdef/ifndef
72%% IncDir          Directories to be searched for included files
73%% IncFile         Stack of included files
74%% IncLine         The line numer of an include
75%% L               The current line number
76%% Name            Name of a macro
77%% Nl              Number of encountered newlines
78%% No_of_para      Numer of parameters of the currently expanded macro
79%% Out             The result of the second step
80%% Parameters      The parameters of the currently expanded macro
81%% PrevFile        The name of the "parent" file which includes the currently expanded file
82%% Rem             Remaining of the file currently being expanded
83%% Removed         The tokens removed, used when removing tokens to the end of a line
84%% Result          The current result of something
85%% SelfRef         List of variables which shoud not be expanded at the rescan to avoid
86%%                   endless loops due to self referencing
87%% Str             Temporary string
88%% Text            A variable used for string handling, e.g at error handling
89%% Tokens          Temoprary list when tokenising
90%% War             The current list of warnings = [{file, line number, warning text}, ...]
91%% X               Temporary variable used when the value is not important
92%% Y               Temporary variable used when the value is not important
93%%
94%%======================================================================================
95
96%% Multiple Include Optimization
97%%
98%% Algorithm described at:
99%% http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
100-record(mio, {valid = true,   %% multiple include valid
101              cmacro,         %% controlling macro of the current conditional directive
102              depth = 0,      %% conditional directive depth
103              included = []}).
104
105
106
107%%======================================================================================
108%%======================================================================================
109%%======================================================================================
110%% The main entry for the preprocessor
111%%
112%%
113%% Output   {ok, Out, War} | {error, Err}
114%%======================================================================================
115%%======================================================================================
116%%======================================================================================
117run(FileName, Flags) when is_atom(FileName) ->
118    run(atom_to_list(FileName), Flags);
119
120run(FileName, Flags) ->
121    IncDir = include_dir(Flags),
122
123    case catch file:read_file(FileName) of
124	{ok, Bin} ->
125	    FileList = binary_to_list(Bin),
126	    run(FileList, FileName, IncDir, Flags);
127	{error, _} ->
128	    Text = "No such file or directory",
129	    {error, [FileName ++ ": " ++ Text]}
130    end.
131
132
133run(FileList, FileName, IncDir, Flags) ->
134    %%----------------------------------------------------------
135    %% Run the first phase, i.e tokenise the file
136    %%----------------------------------------------------------
137    File = tokenise(FileList, FileName),
138
139    %%----------------------------------------------------------
140    %% Run the second phase, i.e expand macros
141    %%----------------------------------------------------------
142    {Out, Err, War, _Defs, _Mio, IfCou} = expand(File, FileName, IncDir, Flags),
143
144    %%----------------------------------------------------------
145    %% Check if all #if #ifdef #ifndef have a matching #endif
146    %%----------------------------------------------------------
147    IfError = case IfCou of
148		     {endif, Endif, IfLine} when Endif > 0 ->
149			 [{FileName, IfLine, "unterminated `#if' conditional"}];
150		     _ ->
151			 []
152		 end,
153
154    Err2 = Err++IfError,
155
156    case Err2 of
157	[] ->
158	    {ok, lists:flatten(lists:reverse(Out)), lists:reverse(War)};
159	_ ->
160	    {error, lists:reverse(Err2)}
161    end.
162
163%%======================================================================================
164%% The entry for all included files
165%%
166%%
167%% Output   {Out, Err, War, Defs, MultipleIncludeValid}
168%%======================================================================================
169run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir, Mio) ->
170
171    %%----------------------------------------------------------
172    %% Run the first phase, i.e tokenise the file
173    %%----------------------------------------------------------
174    [PrevFile | _T] = IncFile,
175    {File,  FileInfoStart, FileInfoEnd} =
176	tokenise(FileList, FileName, IncLine, PrevFile),
177
178    %%----------------------------------------------------------
179    %% Run the second phase, i.e expand macros
180    %%----------------------------------------------------------
181    {Out2, Err2, War2, Defs2, Mio2, IfCou2} =
182        expand([FileInfoStart|File]++FileInfoEnd, Defs, Err, War,
183               [FileName|IncFile], IncDir,
184               #mio{included=Mio#mio.included}),
185
186    MergeIncluded = sets:to_list(sets:from_list(Mio#mio.included ++ Mio2#mio.included)),
187
188    Mio3 =
189        case {Mio2#mio.valid, Mio2#mio.cmacro} of
190            {V, Macro} when V == false;
191                            Macro == undefined ->
192                update_mio(Mio#mio{included=MergeIncluded});
193            {true, _} ->
194                update_mio({include, FileName}, Mio#mio{included=MergeIncluded})
195        end,
196
197    %%----------------------------------------------------------
198    %% Check if all #if #ifdef #ifndef have a matching #endif
199    %%----------------------------------------------------------
200    IfError = case IfCou2 of
201		       {endif, Endif, IfLine} when Endif > 0 ->
202			   [{FileName, IfLine, "unterminated `#if' conditional"}];
203		       _ ->
204			   []
205		   end,
206
207    {Out2, Defs2, Err2++IfError, War2, Mio3}.
208
209
210
211
212%%===================================================================================
213%%===================================================================================
214%%===================================================================================
215%% Tokenise the file
216%%
217%%
218%% Output: File
219%%
220%% Description:
221%% The input file is tokenised into a list where all comments are replaced
222%% by a space and all "backslash-newline" sequences are removed.
223%%
224%% A file information is added at start and end of an included file to set the
225%% current file name and line number.
226%%
227%%
228%% A token consists of:
229%% --------------------
230%%
231%%  {char, Char}           special characters like ()[]{},!%&  etc
232%%  {command,Command}      a macro command
233%%  {expanded,Str}         an expanded variable, used to prevent infinite loops
234%%                           at self reference
235%%  {file_info,FI}         start and end information of a file
236%%                           FI is a string of the following format:
237%%                           "# Line FileName Int" were Int is
238%%                           1 if start of an included file,
239%%                           2 when returning to "parent" file
240%%  {nl, L}                newline
241%%  {number,Num)           variable, a string starting with a number
242%%  {self_ref,Var}         to allow reference to a variable again, used when expanding
243%%                           self refering macros
244%%  space                  a space
245%%  space_exp              a space, special notation to prevent not wanted concatination
246%%  {string, Str}          a (tail of a) string constant
247%%  {string_part, Str}     a head of a string constant defined on several consecutive lines
248%%  {sys_head, Str}        (tail of) the file name of included system file
249%%  {sys_head_part , Str}  the file name of included system file
250%%  {var,Var}              variable, a string starting with minuscular or capital letter or
251%%                           an underline
252%%
253%% Note, comments are not removed within a character or string constant
254%% or inside an include-definition where the file name is delimited with < >
255%%===================================================================================
256%%===================================================================================
257%%===================================================================================
258
259tokenise(File, FileName) ->
260    {Result, _L} = token(File, 2, [], not_set, 0),
261    FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\"~n",[FileName]))),
262    FileInfoStart = {file_info, FI_start},
263    [FileInfoStart | Result].
264
265tokenise(File, FileName, IncLine, PrevFile) ->
266    {Result, _L} = token(File, 2, [], not_set, 0),
267    FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\" 1~n",[FileName]))),
268    FileInfoStart = {file_info, FI_start},
269    FI_end = lists:reverse(lists:flatten(io_lib:format("# ~p \"~ts\" 2~n~n",[IncLine-1,PrevFile]))),
270    FileInfoEnd = [{file_info, FI_end}],
271    {Result,  FileInfoStart, FileInfoEnd}.
272%    [FileInfoStart | Result] ++ FileInfoEnd.
273
274
275%%===================================================================================
276%% token(InputFile, L, Result, Gen)
277%%    Gen     information of the first token on the line, default = not_set
278%%
279%% Output: File
280%%===================================================================================
281
282%%==================================================================
283%% Normal line
284%%==================================================================
285%%---------------------------------------
286%% All file tokenised
287%%---------------------------------------
288token([], L, [{nl,NL}|Result], _Gen, _BsNl) when L == NL+1->
289    {lists:reverse([{nl,NL}|Result]), L};
290token([], L, Result, _Gen, _BsNl) ->
291    {lists:reverse([{nl,L-1}|Result]), L};
292
293%%---------------------------------------
294%% String
295%%---------------------------------------
296token(File, L, Result, string, BsNl) ->
297    case token_string(File, []) of
298	{Rem, Str, nl} ->
299	    Result1 = [{nl, L}, {string,Str} | Result],
300	    token(Rem, L+1, Result1, string, BsNl);
301	{Rem, Str} ->
302	    token(Rem, L, [{string,Str}|Result], not_set, BsNl)
303    end;
304
305token([$"|File], L, Result, Gen, BsNl) ->
306    case token_string(File, []) of
307	{Rem, Str, nl} ->
308	    Result1 = [{nl, L}, {string_part,Str} | Result],
309	    token(Rem, L+1, Result1, string, BsNl);
310	{Rem, Str} ->
311	    token(Rem, L, [{string,Str}|Result], Gen, BsNl)
312    end;
313
314%%---------------------------------------
315%% Include with < >
316%%---------------------------------------
317token(File, L, Result, include, BsNl) ->
318    case token_include(File, []) of
319	{Rem, Str, nl} ->
320	    Result1 = [{nl, L}, {sys_head,Str} | Result],
321	    token(Rem, L+1, Result1, include, BsNl);
322	{Rem, Str} ->
323	    token(Rem, L, [{sys_head,Str}|Result], not_set, BsNl)
324    end;
325
326token([$<|File], L, [space,{command,"include"}|Result], Gen, BsNl) ->
327    case token_include(File, []) of
328	{Rem, Str, nl} ->
329	    Result1 = [{nl, L}, {sys_head_part,Str}, space, {command,"include"} |Result],
330	    token(Rem, L+1,Result1, include, BsNl);
331	{Rem, Str} ->
332	    Result1 = [{sys_head,Str}, space, {command,"include"} |Result],
333	    token(Rem, L, Result1, Gen, BsNl)
334    end;
335token([$<|File], L, [{command,"include"}|Result], Gen, BsNl) ->
336    case token_include(File, []) of
337	{Rem, Str, nl} ->
338	    Result1 = [{nl, L}, {sys_head_part,Str}, space, {command,"include"} |Result],
339	    token(Rem, L+1,Result1, include, BsNl);
340	{Rem, Str} ->
341	    Result1 = [{sys_head,Str}, space, {command,"include"} |Result],
342	    token(Rem, L, Result1, Gen, BsNl)
343    end;
344
345
346
347
348%%---------------------------------------
349%% CR (just remove these)
350%%---------------------------------------
351token([$\r|File], L, Result, Gen, BsNl) ->
352%    Bs = lists:duplicate(BsNl+1,{nl,L}),
353    token(File, L, Result, Gen, BsNl); %% Bs or BsNl?
354
355%%---------------------------------------
356%% Newline
357%%---------------------------------------
358token([$\n|File], L, Result, _Gen, BsNl) ->
359    Bs = lists:duplicate(BsNl+1,{nl,L}),
360    token(File, L+1, Bs++Result, not_set, 0);
361
362token([$\\,$\n|File], L, Result, Gen, BsNl) ->
363    token(File, L, Result, Gen, BsNl+1);
364
365%%---------------------------------------
366%% Comments
367%%---------------------------------------
368token([$/,$/|File], L, Result, not_set, BsNl) ->
369    Rem = skip_to_nl(File),
370    token(Rem, L+1,[{nl, L} | Result], not_set, BsNl);
371token([$/,$/|File], L, Result, _Gen, BsNl) ->
372    Rem = skip_to_nl(File),
373    token(Rem, L+1,[{nl, L} | Result], not_set, BsNl);
374
375token([$/,$*|File], L, Result, not_set, BsNl) ->
376    case token_comment(File) of
377	{Rem, nl} ->
378	    token(Rem, L+1, [{nl, L} | Result], not_set, BsNl);
379	Rem ->
380	    token(Rem, L, Result, not_set, BsNl)
381    end;
382token([$/,$*|File], L, Result, Gen, BsNl) ->
383    case token_comment(File) of
384	{Rem, nl} ->
385	    token(Rem, L+1, [{nl, L}, space | Result], not_set, BsNl);
386	Rem ->
387	    token(Rem, L, [space|Result], Gen, BsNl)
388    end;
389
390%%---------------------------------------
391%% Variable
392%%---------------------------------------
393token([X|File], L, Result, Gen, BsNl) when ?is_upper(X) ->
394    GenNew = case Gen of not_set -> var; _ -> Gen end,
395    {Rem, Var} = tok_var(File, [X]),
396    token(Rem, L, [{var,Var}|Result], GenNew, BsNl);
397token([X|File], L, Result, Gen, BsNl) when ?is_lower(X) ->
398    GenNew = case Gen of not_set -> var; _ -> Gen end,
399    {Rem, Var} = tok_var(File, [X]),
400    token(Rem, L, [{var,Var}|Result], GenNew, BsNl);
401token([X|File], L, Result, Gen, BsNl) when ?is_underline(X) ->
402    GenNew = case Gen of not_set -> var; _ -> Gen end,
403    {Rem, Var} = tok_var(File, [X]),
404    token(Rem, L, [{var,Var}|Result], GenNew, BsNl);
405
406%%---------------------------------------
407%% Number
408%%---------------------------------------
409token([X|File], L, Result, Gen, BsNl) when ?is_number(X) ->
410    GenNew = case Gen of not_set -> number; _ -> Gen end,
411    {Rem, Tokens} = tok_number(File, [X]),
412    token(Rem, L, [{number,Tokens}|Result], GenNew, BsNl);
413
414%%---------------------------------------
415%% Space
416%%---------------------------------------
417token([X|File], L, [Y|Result], Gen, BsNl) when ?is_space(X) ->
418    case Y of
419	space ->
420	    Rem = remove_leading_spaces(File),
421	    token(Rem, L, [Y|Result], Gen, BsNl);
422	{nl,_,_} ->
423	    Rem = remove_leading_spaces(File),
424	    token(Rem, L, Result, Gen, BsNl);
425	_ ->
426	    Rem = remove_leading_spaces(File),
427	    token(Rem, L, [space, Y |Result], Gen, BsNl)
428    end;
429
430token([X|File], L, [Y|Result], Gen, BsNl) when ?is_tab(X) ->
431    case Y of
432	space ->
433	    Rem = remove_leading_spaces(File),
434	    token(Rem, L, [Y|Result], Gen, BsNl);
435	{nl,_,_} ->
436	    Rem = remove_leading_spaces(File),
437	    token(Rem, L, Result, Gen, BsNl);
438	_ ->
439	    Rem = remove_leading_spaces(File),
440	    token(Rem, L, [space, Y |Result], Gen, BsNl)
441    end;
442
443%%---------------------------------------
444%% Command
445%%---------------------------------------
446token([$#|File], L, Result, not_set, BsNl) ->
447    {Rem, Command} = token_pp_com(File),
448    case catch list_to_integer(Command) of
449	{'EXIT', _} ->
450	    token(Rem, L, [{command,Command}|Result], not_set, BsNl);
451	_Int ->
452	    Result1 = [{number,Command}, {command,"line"}| Result],
453	    token(Rem, L, Result1, not_set, BsNl)
454    end;
455
456%%---------------------------------------
457%% Char
458%%---------------------------------------
459token([X|File], L, Result, Gen, BsNl) ->
460    GenNew = case Gen of not_set -> char; _ -> Gen end,
461    token(File, L, [{char,X}|Result], GenNew, BsNl).
462
463
464%%==================================================================
465%% Scan to the end of a token
466%%==================================================================
467%%---------------------------------------
468%% Number
469%%---------------------------------------
470tok_number([], Str) ->
471    {[], lists:reverse(Str)};
472tok_number([X|File], Str) when ?is_upper(X) ->
473    tok_number(File, [X|Str]);
474tok_number([X|File], Str) when ?is_lower(X) ->
475    tok_number(File, [X|Str]);
476tok_number([X|File], Str) when ?is_underline(X) ->
477    tok_number(File, [X|Str]);
478tok_number([X|File], Str) when ?is_number(X) ->
479    tok_number(File, [X|Str]);
480tok_number(File, Str) ->
481    {File, lists:reverse(Str)}.
482
483
484%%---------------------------------------
485%% Variable
486%%---------------------------------------
487tok_var([], Str) ->
488    {[], lists:reverse(Str)};
489tok_var([X|File], Str) when ?is_upper(X) ->
490    tok_var(File, [X|Str]);
491tok_var([X|File], Str) when ?is_lower(X) ->
492    tok_var(File, [X|Str]);
493tok_var([X|File], Str) when ?is_underline(X) ->
494    tok_var(File, [X|Str]);
495tok_var([X|File], Str) when ?is_number(X) ->
496    tok_var(File, [X|Str]);
497tok_var(File, Str) ->
498    {File, lists:reverse(Str)}.
499
500
501%%---------------------------------------
502%% Preprocessor command
503%%---------------------------------------
504token_pp_com([X|File]) when ?is_upper(X) ->
505    tok_var(File, [X]);
506token_pp_com([X|File]) when ?is_lower(X) ->
507    tok_var(File, [X]);
508token_pp_com([X|File]) when ?is_underline(X) ->
509    tok_var(File, [X]);
510token_pp_com([X|File]) when ?is_number(X) ->
511    tok_var(File, [X]);
512token_pp_com(File) ->
513    Rem = remove_leading_spaces(File),
514    {Rem, "null"}.
515
516
517
518%%---------------------------------------
519%% Comment
520%%---------------------------------------
521token_comment([]) ->
522    [];
523token_comment([$*,$/|File]) ->
524    File;
525token_comment([$\n|File]) ->
526    {[$/,$*|File], nl};
527token_comment([$\r,$\n|File]) ->
528    {[$/,$*|File], nl};
529token_comment([$\\,$\n|File]) ->
530    {[$/,$*|File], nl};
531%token_comment([$\\,$\n|File]) ->
532%    token_comment(File);
533token_comment([_|File]) ->
534    token_comment(File).
535
536
537%%---------------------------------------
538%% String
539%%---------------------------------------
540token_string([], Str) ->
541    {[], lists:reverse(Str)};
542token_string([$"|File], Str) ->
543    {File, lists:reverse(Str)};
544token_string([$\n|File], Str) ->
545    {File, lists:reverse(Str), nl};
546token_string([$\r,$\n|File], Str) ->
547    {File, lists:reverse(Str), nl};
548token_string([$\\,$\n|File], Str) ->
549    token_string(File, Str);
550token_string([X|File], Str) ->
551    token_string(File, [X|Str]).
552
553
554%%---------------------------------------
555%% Include
556%%---------------------------------------
557token_include([], Str) ->
558    {[], lists:reverse(Str)};
559token_include([$>|File], Str) ->
560    {File, lists:reverse(Str)};
561token_include([$\n|File], Str) ->
562    {File, lists:reverse(Str), nl};
563token_include([$\r,$\n|File], Str) ->
564    {File, lists:reverse(Str), nl};
565token_include([$\\,$\n|File], Str) ->
566    token_include(File, Str);
567token_include([X|File], Str) ->
568    token_include(File, [X|Str]).
569
570
571
572
573%%===================================================================================
574%% detokenise a list of tokens, until next newline
575%%
576%% Output: a string
577%%===================================================================================
578detokenise(Tokens) ->
579    detokenise(Tokens, []).
580
581detokenise([], Result) ->
582    lists:flatten(Result);
583detokenise([space], Result) ->
584    lists:flatten(Result);
585detokenise([space_exp], Result) ->
586    lists:flatten(Result);
587detokenise([space|Rem], Result) ->
588    detokenise(Rem, Result++[?space]);
589detokenise([space_exp|Rem], Result) ->
590    detokenise(Rem, Result++[?space]);
591detokenise([nl|Rem], Result) ->
592    detokenise(Rem, Result++[$\n]);
593detokenise([{_, String}|Rem], Result) ->
594    detokenise(Rem, Result++[String]).
595
596
597detokenise_pragma(Tokens) ->
598    detokenise_pragma(Tokens, []).
599
600detokenise_pragma([], Result) ->
601    lists:flatten(Result);
602detokenise_pragma([space], Result) ->
603    lists:flatten(Result);
604detokenise_pragma([space|Rem], Result) ->
605    detokenise_pragma(Rem, Result++[?space]);
606detokenise_pragma([nl|Rem], Result) ->
607    detokenise_pragma(Rem, Result++[$\n]);
608detokenise_pragma([{string, String}|Rem], Result) ->
609    detokenise_pragma(Rem, Result++[$"|String]++[$"]);
610detokenise_pragma([{_, String}|Rem], Result) ->
611    detokenise_pragma(Rem, Result++[String]).
612
613
614
615
616
617
618
619%%======================================================================================
620%%======================================================================================
621%%======================================================================================
622%% Expand macros.
623%%
624%%
625%% Output:  A text file
626%%
627%% Description: Expands all macros. All macro definitions are logged in a list 'Defs'
628%%              and all found errors and warnings are logged in a list 'Err' and 'War',
629%%              respectively.
630%%
631%%              When a macro name is found in a source line it is expanded according
632%%              to the current 'Defs'-list. The macro must agree both to the name
633%%              and number of parameters, otherwise an error is reported.
634%%======================================================================================
635%%======================================================================================
636%%======================================================================================
637
638
639expand(List, FileName, IncDir, Flags) ->
640    %% Get all definitions from preprocessor commnads
641    %% and merge them on top of the file collected.
642    CLDefs = get_cmd_line_defs(Flags),
643    expand(List, [], [], CLDefs, [FileName], IncDir, #mio{}, check_all, [], [], 1, FileName).
644
645expand(List, Defs, Err, War, [FileName|IncFile], IncDir, Mio) ->
646    expand(List, [], [], Defs, [FileName|IncFile], IncDir, Mio, check_all, Err, War, 1, FileName).
647
648%%=======================================================
649%% Main loop for the expansion
650%%=======================================================
651expand([], Out, _SelfRef, Defs, _IncFile, _IncDir, Mio, IfCou, Err, War, _L, _FN) ->
652%    io:format("~n   ===============~n"),
653%    io:format("   definitions    ~p~n",[lists:reverse(Defs)]),
654%    io:format("   found warnings ~p~n",[lists:reverse(War)]),
655%    io:format("   found errors   ~p~n",[lists:reverse(Err)]),
656%    io:format("   ===============~n~n~n"),
657    {Out, Err, War, Defs, Mio, IfCou};
658
659expand([{file_info, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
660    expand(Rem, Str++Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN);
661
662%%---------------------------------------
663%% Searching for endif,
664%% i.e skip all source lines until matching
665%% end if is encountered
666%%---------------------------------------
667expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN)
668  when Command == "ifdef" ->
669    {_Removed, Rem2, _Nl} = read_to_nl(Rem),
670    IfCou2 = {endif, Endif+1, IfLine},
671    expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN);
672
673
674expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN)
675  when Command == "ifndef" ->
676    {_Removed, Rem2, _Nl} = read_to_nl(Rem),
677    IfCou2 = {endif, Endif+1, IfLine},
678    expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN);
679
680
681expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN)
682  when Command == "if" ->
683    case pp_command(Command, Rem, Defs, IncDir, Mio, Err, War, L, FN) of
684        {{'if', true}, Rem2, Err2, War2, Nl} ->
685            IfCou2 = {endif, Endif+1, IfLine},
686            expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN);
687%%        {{'if', false}, Rem2, Err2, War2, Nl} -> Not implemented yet
688        {{'if', error}, Rem2, Err2, War2, Nl} ->
689            IfCou2 = {endif, Endif, IfLine},
690            expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN)
691    end;
692
693expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN)
694  when Command == "endif" ->
695    {_Removed, Rem2, Nl} = read_to_nl(Rem),
696    case Endif of
697	1 ->
698	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
699            expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L+Nl, FN);
700	_ ->
701            IfCou2 = {endif, Endif-1, IfLine},
702            expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L+Nl, FN)
703    end;
704
705
706expand([{command,_Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) ->
707    {_Removed, Rem2, _Nl} = read_to_nl(Rem),
708    IfCou2 = {endif, Endif, IfLine},
709    expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN);
710
711%% Solves a bug when spaces in front of hashmark !
712expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) ->
713    expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN);
714
715expand([{nl,_Nl} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) ->
716    expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN);
717
718
719expand([_X | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) ->
720    {_Removed, Rem2, Nl} = read_to_nl(Rem),
721    Out2 = lists:duplicate(Nl,$\n) ++ Out,
722    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN);
723
724
725
726
727
728%%---------------------------------------
729%% Check all tokens
730%%---------------------------------------
731expand([{nl, _N} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
732    expand(Rem, [$\n | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L+1, FN);
733
734expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
735    expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN);
736
737expand([space_exp | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
738    expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN);
739
740expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L, FN) ->
741    case pp_command(Command, Rem, Defs, IncDir, Mio, Err, War, L, FN) of
742	{define, Rem2, Defs2, Err2, War2, Nl} ->
743	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
744	    expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN);
745
746	{undef, Rem2, Defs2, Err2, War2, Nl} ->
747	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
748	    expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN);
749
750	{{include, ok}, FileName, FileCont, Rem2, Nl, Err2, War2} ->
751	    {Out3, Defs3, Err3, War3, Mio2} =
752		run_include(FileName, FileCont, Out, Defs, Err2, War2, L+Nl, IncFile, IncDir, Mio),
753	    Nls = [],
754	    Out4 = Out3++Nls++Out,
755	    expand(Rem2, Out4, SelfRef, Defs3, IncFile, IncDir, Mio2, check_all, Err3, War3, L+Nl, FN);
756
757	{{include, error}, Rem2, Nl, Err2, War2} ->
758	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
759	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN);
760
761	{{include, skip}, Rem2} ->
762	    Out2 = [$\n|Out],
763	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+1, FN);
764
765	{{ifdef, true}, Rem2, Err2, War2, Nl} ->
766	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
767	    IfCou2 = {endif, 1, L},
768	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN);
769	{{ifdef, false}, Rem2, Err2, War2, Nl} ->
770	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
771	    Mio2 = update_mio(ifdef, Mio),
772	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN);
773
774	{{ifndef, true}, Rem2, Err2, War2, Nl} ->
775	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
776	    IfCou2 = {endif, 1, L},
777	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN);
778	{{ifndef, false}, Macro, Rem2, Err2, War2, Nl} ->
779	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
780	    Mio2 = update_mio({ifndef, Macro}, Mio),
781	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN);
782
783	{endif, Rem2, Err2, War2, Nl} ->
784	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
785	    Mio2 = update_mio(endif, Mio),
786	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN);
787
788	{{'if', true}, Rem2, Err2, War2, Nl} ->
789	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
790	    IfCou2 = {endif, 1, L},
791	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN);
792%%	{{'if', false}, Removed, Rem2, Nl} ->   Not implemented at present
793	{{'if', error}, Rem2, Err2, War2, Nl} ->
794	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
795	    Mio2 = update_mio('if', Mio),
796	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN);
797
798	{'else', {_Removed, Rem2, Nl}} ->
799	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
800	    Err2 = {FN, L, "`else' command is not implemented at present"},
801	    Mio2 = update_mio('else', Mio),
802	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN);
803
804	{'elif', {_Removed, Rem2, Nl}} ->
805	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
806	    Err2 = {FN, L, "`elif' command is not implemented at present"},
807	    Mio2 = update_mio('elif', Mio),
808	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN);
809
810	{warning, {WarningText, Rem2, Nl}} ->
811	    [FileName|_More] = IncFile,
812	    War2 = {FileName, L, "warning: #warning "++detokenise(WarningText)},
813	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
814	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, [War2|War], L+Nl, FN);
815
816	{error, {ErrorText, Rem2, Nl}} ->
817	    [FileName|_More] = IncFile,
818	    Err2 = {FileName, L, detokenise(ErrorText)},
819	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
820	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, [Err2|Err], War, L+Nl, FN);
821
822	{{line, ok}, {_Removed, Rem2, Nl}, L2, FN2, LineText} ->
823	    Out2 = lists:duplicate(Nl,$\n)++LineText++Out,
824	    [_X|IF] = IncFile,
825	    IncFile2 = [FN2|IF],
826	    expand(Rem2, Out2, SelfRef, Defs, IncFile2, IncDir, update_mio(Mio), check_all, Err, War, L2, FN2);
827	{{line, error}, {_Removed, Rem2, Nl}, Err2} ->
828	    Out2 = lists:duplicate(Nl,$\n) ++ Out,
829	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, [Err2|Err], War, L+Nl, FN);
830
831	hash_mark ->
832	    expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L, FN);
833
834	{pragma, Rem2, Nl, Text} ->
835	    Out2 = lists:duplicate(Nl,$\n)++Text++Out,
836	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+Nl, FN);
837
838	{ident, Rem2, Nl, Text} ->
839	    Out2 = lists:duplicate(Nl,$\n)++Text++Out,
840	    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+Nl, FN);
841
842	{not_recognised, {Removed, Rem2, Nl}} ->
843	    Text = lists:reverse([$#|Command]),
844	    RemovedS = lists:reverse([?space|detokenise(Removed)]),
845	    Out2 = [$\n|RemovedS]++Text++Out,
846	    Mio2 = update_mio(Mio),
847	    case Command of
848		[X|_T] when ?is_upper(X) ->
849		    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN);
850		[X|_T] when ?is_lower(X) ->
851		    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN);
852		[X|_T] when ?is_underline(X) ->
853		    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN);
854		_ ->
855		    Err2 = {FN, L, "invalid preprocessing directive name"},
856		    expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN)
857	    end;
858
859	Else ->
860%	    io:format(" %%%%Else%%%%%% ~p~n",[Else]),
861	    exit(Else)
862    end;
863
864
865expand([{var, "__LINE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
866    LL = io_lib:format("~p",[L]),
867    expand(Rem, [LL | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
868
869expand([{var, "__FILE__"}|Rem], Out, SelfRef, Defs, IncFile, Mio, IncDir, IfCou, Err, War, L, FN) ->
870    expand(Rem, [$",FN,$" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
871
872expand([{var, "__DATE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
873    {{Y,M,D},{_H,_Mi,_S}} = calendar:universal_time(),
874    Date = io_lib:format("\"~s ~p ~p\"",[month(M),D,Y]),
875    expand(Rem, [Date | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
876
877expand([{var, "__TIME__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
878    {{_Y,_M,_D},{H,Mi,S}} = calendar:universal_time(),
879    HS = if H < 10 -> "0"++integer_to_list(H);
880	    true -> integer_to_list(H)
881	 end,
882    MiS = if Mi < 10 -> "0"++integer_to_list(Mi);
883       true -> integer_to_list(Mi)
884    end,
885    SS = if S < 10 -> "0"++integer_to_list(S);
886       true -> integer_to_list(S)
887    end,
888    Time = io_lib:format("\"~s:~s:~s\"",[HS,MiS,SS]),
889    expand(Rem, [Time | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
890
891expand([{var, "__INCLUDE_LEVEL__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
892    IL = io_lib:format("~p",[length(IncFile)-1]),
893    expand(Rem, [IL | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
894
895expand([{var, "__BASE_FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
896    [BF|_T] = lists:reverse(IncFile),
897    expand(Rem, [$",BF,$" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
898
899expand([{var, Var} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
900    {Out2, Err2, War2, Rem2, SelfRef2} =
901	source_line(Var, Rem, SelfRef, Defs, Err, War, L, FN),
902    expand(Rem2, [Out2 | Out], SelfRef2, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err2, War2, L, FN);
903
904expand([{char, Char} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
905    expand(Rem, [Char | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
906
907expand([{number, Number} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
908    expand(Rem, [Number | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
909
910expand([{expanded, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
911    expand(Rem, [Str | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
912
913expand([{self_ref, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
914    SelfRef2 = lists:delete(Str,SelfRef),
915    expand(Rem, Out, SelfRef2, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
916
917expand([{string, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
918    expand(Rem, [$", Str, $" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN);
919
920expand([{string_part, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) ->
921    {Str2, Rem2, Nl} = expand_string_part([$"|Str], Rem),
922    expand(Rem2, [Str2| Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L+Nl, FN).
923
924
925
926
927
928
929
930
931%%========================================================================
932%% Expand a line starting as a partial string
933%%========================================================================
934expand_string_part(Str, File) ->
935    expand_string_part(File, Str, 0).
936
937expand_string_part([{string, Str_part} | Rem], Str, Nl) ->
938    {Str++Str_part++[$"], Rem, Nl};
939expand_string_part([space | Rem], Str, Nl) ->
940    expand_string_part(Rem, Str, Nl);
941expand_string_part([nl| Rem], Str, Nl) ->
942    expand_string_part(Rem, Str++[$\n], Nl);
943expand_string_part([{string_part, Str_part} | Rem], Str, Nl) ->
944    expand_string_part(Rem, Str++Str_part, Nl).
945
946
947
948
949
950%%========================================================================
951%% Parse and integrate command line macro directives
952%% At this momment, only -D and -U are supported (gcc like)
953%%========================================================================
954
955
956%% Collect all command line macro definitions
957get_cmd_line_defs(Flags) ->
958    Adjusted = parse_cmd_line(Flags,[]),
959
960    {_Out, _Err, _War, Defs, _IfCou, _Mio} =
961	expand(tokenise(Adjusted,""),
962	       [],
963	       [],
964	       [],
965	       [],
966	       [],
967	       #mio{},
968	       check_all,
969	       [],
970	       [],
971	       1,
972	       ""),
973    Defs.
974
975%% Parse command line macros
976parse_cmd_line([],Found) ->
977    lists:flatten(lists:reverse(Found));
978
979parse_cmd_line([45,68|Rest],Found) ->
980    {Collected,RestCmds} = collect_define(Rest,[]),
981    parse_cmd_line(RestCmds,[Collected|Found]);
982
983parse_cmd_line([45,85|Rest],Found) ->
984    {Collected,RestCmds} = collect_undefine(Rest,[]),
985    parse_cmd_line(RestCmds,[Collected|Found]);
986
987parse_cmd_line([_|Rest],Found) ->
988    parse_cmd_line(Rest,Found).
989
990
991%% Collect defines and translate them
992%% into a text format
993collect_define([],Found) ->
994    { "#define "++lists:reverse(Found)++"\n", [] };
995collect_define([32|Rest],Found) ->
996    { "#define "++lists:reverse(Found)++"\n", Rest };
997collect_define([61|Rest],[]) ->
998    { "", Rest };
999collect_define([61|Rest],Found) ->
1000    collect_define(Rest,[32|Found]);
1001collect_define([C|Rest],Found) ->
1002    collect_define(Rest,[C|Found]).
1003
1004
1005%% Collect undefines and translate them
1006%% into a text format
1007collect_undefine([],Found) ->
1008    { "#undef "++lists:reverse(Found)++"\n", [] };
1009collect_undefine([32|Rest],Found) ->
1010    { "#undef "++lists:reverse(Found)++"\n", Rest };
1011collect_undefine([C|Rest],Found) ->
1012    collect_undefine(Rest,[C|Found]).
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025%%======================================================================================
1026%%======================================================================================
1027%%======================================================================================
1028%% Read a preprocessor command
1029%%
1030%%
1031%% Output:  Depending of the command, typically = {Command, Rem, Err, War, Nl}
1032%%
1033%%======================================================================================
1034%%======================================================================================
1035%%======================================================================================
1036
1037pp_command(Command, [space|File], Defs, IncDir, Mio, Err, War, L, FN) ->
1038    pp_command(Command, File, Defs, IncDir, Mio, Err, War, L, FN);
1039
1040pp_command(Command, File, Defs, IncDir, Mio, Err, War, L, FN) ->
1041
1042    case Command of
1043	%%----------------------------------------
1044	%% #define
1045	%%----------------------------------------
1046	"define" ->
1047	    case define(File, Err, War, L, FN) of
1048		{error, Rem, Err2, War2, Nl} ->
1049		    {define, Rem, Defs, Err2, War2, Nl};
1050		{warning, Rem, Name, No_of_para, Parameters, Macro, Err2, War2, Nl} ->
1051		    case is_define_ok(Name, No_of_para, Parameters, Macro, Defs) of
1052			{yes, Defs2} ->
1053			    {define, Rem, Defs2, Err2, War2, Nl};
1054			{no, Defs2} ->
1055			    Text = lists:flatten(io_lib:format("`~s' redefined",[Name])),
1056			    {define, Rem, Defs2, Err2, [{FN, L, Text}|War2], Nl};
1057			{error, Text, Defs2} ->
1058			    {define, Rem, Defs2, [{FN, L, Text}|Err2], War2, Nl}
1059		    end;
1060		{ok, Rem, Name, No_of_para, Parameters, Macro, Err2, War2, Nl} ->
1061		    case is_define_ok(Name, No_of_para, Parameters, Macro, Defs) of
1062			{yes, Defs2} ->
1063			    {define, Rem, Defs2, Err2, War2, Nl};
1064			{no, Defs2} ->
1065			    Text = lists:flatten(io_lib:format("`~s' redefined",[Name])),
1066			    {define, Rem, Defs2, Err2, [{FN, L, Text}|War2], Nl};
1067			{error, Text, Defs2} ->
1068			    {define, Rem, Defs2, [{FN, L, Text}|Err2], War2, Nl}
1069		    end
1070	    end;
1071
1072	%%----------------------------------------
1073	%% #undef
1074	%%----------------------------------------
1075	"undef" ->
1076	    case undef(File, Err, War, L, FN) of
1077		{error, Rem, Err2, War2, Nl} ->
1078		    {undef, Rem, Defs, Err2, War2, Nl};
1079		{ok, Rem, Name, Err2, War2, Nl} ->
1080		    Defs2 = lists:keydelete(Name, 1, Defs),
1081		    {undef, Rem, Defs2, Err2, War2, Nl}
1082	    end;
1083
1084	%%----------------------------------------
1085	%% #include
1086	%%----------------------------------------
1087	"include" ->
1088             case include(File, IncDir, Mio) of
1089                 {error, Rem, Nl, Err2} ->
1090                     {{include, error}, Rem, Nl, [{FN, L, Err2}|Err], War};
1091                 {error, Rem, Nl, Err2, NameNl} ->
1092                     {{include, error}, Rem, Nl, [{FN, L+ NameNl, Err2}|Err], War};
1093                 {ok, FileNamePath, FileCont, Rem, Nl} ->
1094                     {{include, ok}, FileNamePath, FileCont, Rem, Nl, Err, War};
1095                 {skip, Rem} ->
1096                     {{include, skip}, Rem}
1097             end;
1098
1099	%%----------------------------------------
1100	%% #ifdef
1101	%%----------------------------------------
1102	"ifdef" ->
1103	    case define(File, Err, War, L, FN) of
1104		{error, Rem, Err2, War2, Nl} ->
1105		    {{ifdef, false}, Rem, Defs, Err2, War2, Nl};
1106		{warning, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} ->
1107		    case is_defined_before(Name, No_of_para, Defs) of
1108			yes ->
1109			    {{ifdef, false}, Rem, Err2, War2, Nl};
1110			no ->
1111			    {{ifdef, true}, Rem, Err2, War2, Nl}
1112		    end;
1113		{ok, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} ->
1114		    case is_defined_before(Name, No_of_para, Defs) of
1115			yes ->
1116			    {{ifdef, false}, Rem, Err2, War2, Nl};
1117			no ->
1118			    {{ifdef, true}, Rem, Err2, War2, Nl}
1119		    end
1120	    end;
1121
1122
1123
1124	%%----------------------------------------
1125	%% #ifndef
1126	%%----------------------------------------
1127	"ifndef" ->
1128	    case define(File, Err, War, L, FN) of
1129		{error, Rem, Err2, War2, Nl} ->
1130		    {{ifndef, false}, Rem, Defs, Err2, War2, Nl};
1131		{warning, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} ->
1132		    case is_defined_before(Name, No_of_para, Defs) of
1133			yes ->
1134			    {{ifndef, true}, Rem, Err2, War2, Nl};
1135			no ->
1136			    {{ifndef, false}, Name, Rem, Err2, War2, Nl}
1137		    end;
1138		{ok, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} ->
1139		    case is_defined_before(Name, No_of_para, Defs) of
1140			yes ->
1141			    {{ifndef, true}, Rem, Err2, War2, Nl};
1142			no ->
1143			    {{ifndef, false}, Name, Rem, Err2, War2, Nl}
1144		    end
1145	    end;
1146
1147
1148	%%----------------------------------------
1149	%% #endif
1150	%%----------------------------------------
1151	"endif" ->
1152	    {Removed, Rem, Nl} = read_to_nl(File),
1153	    case Removed of
1154		[] ->
1155		    {endif, Rem, Err, War, 1};
1156		_ ->
1157		    Text = "ignoring the tail of the line",
1158		    {ok, Rem, Err, [{FN, L, Text}|War], Nl}
1159	    end;
1160
1161
1162	%%----------------------------------------
1163	%% #if
1164	%%----------------------------------------
1165	"if" ->
1166	    case if_zero(File, Err, War, L, FN) of
1167		{error, Rem2, _Removed, Nl} ->
1168		    Err2 = {FN, L, "only '#if 0' is implemented at present"},
1169		    {{'if', error}, Rem2, [Err2 | Err], War, Nl};
1170		{ok, Rem2, 0, _Removed, Nl} ->
1171		    {{'if', true}, Rem2, Err, War, Nl};
1172		{ok, Rem2, _Num, _Removed, Nl} ->
1173		    Err2 = {FN, L, "only '#if 0' is implemented at present"},
1174		    {{'if', error}, Rem2, [Err2 | Err], War, Nl}
1175	    end;
1176
1177	%%----------------------------------------
1178	%% #else
1179	%%----------------------------------------
1180	"else" ->
1181	    {'else', read_to_nl(File)};
1182
1183	%%----------------------------------------
1184	%% #elif
1185	%%----------------------------------------
1186	"elif" ->
1187	    {'elif', read_to_nl(File)};
1188
1189	%%----------------------------------------
1190	%% #pragma
1191	%%----------------------------------------
1192	"pragma" ->
1193	    {Removed, Rem, Nl} = read_to_nl(File),
1194	    {pragma, Rem, Nl, lists:reverse("#pragma " ++ detokenise_pragma(Removed))};
1195
1196	%%----------------------------------------
1197	%% #ident
1198	%%----------------------------------------
1199	"ident" ->
1200	    {Removed, Rem, Nl} = read_to_nl(File),
1201	    {ident, Rem, Nl, lists:reverse("#ident " ++ detokenise_pragma(Removed))};
1202
1203	%%----------------------------------------
1204	%% #warning
1205	%%----------------------------------------
1206	"warning" ->
1207	    {warning, read_to_nl(File)};
1208
1209	%%----------------------------------------
1210	%% #error
1211	%%----------------------------------------
1212	"error" ->
1213	    {error, read_to_nl(File)};
1214
1215	%%----------------------------------------
1216	%% #line
1217	%%----------------------------------------
1218	"line" ->
1219	    line(File, L, FN);
1220
1221	%%----------------------------------------
1222	%% #
1223	%%----------------------------------------
1224	"null" ->
1225	    hash_mark;
1226
1227	%%----------------------------------------
1228	%% not recognised preprocessor commands
1229	%%----------------------------------------
1230	_Else ->
1231	    {not_recognised, read_to_nl(File)}
1232    end.
1233
1234
1235
1236
1237%%===============================================================
1238%%===============================================================
1239%%===============================================================
1240%% if
1241%%
1242%% Only #if 0 is implemented at the time to be able to use if
1243%% to comment some code parts.
1244%%===============================================================
1245%%===============================================================
1246%%===============================================================
1247
1248if_zero(File, _Err, _War, _L, _FN) ->
1249    case if_zero(File) of
1250	{ok, Remain, Num, Removed, Nl} ->
1251	    case catch list_to_integer(Num) of
1252		{'EXIT', _} ->
1253		    {Removed2, Rem2, Nl2} = read_to_nl(File),
1254		    {error, Rem2, Removed2, Nl2};
1255		Int ->
1256		    {ok, Remain, Int, Removed, Nl}
1257	    end;
1258	E ->
1259	    E
1260    end.
1261
1262if_zero([{number,Num}]) ->
1263    {ok, [], Num, [], 0};
1264if_zero([{number,Num}, space]) ->
1265    {ok, [], Num, [], 0};
1266if_zero([{number,Num} | Rem]) ->
1267    {Removed, Rem2, Nl} = read_to_nl(Rem),
1268    {ok, Rem2, Num, Removed, Nl};
1269%if_zero([{number,Num}, {nl,_X} | Rem]) ->
1270%    {ok, Rem, Num, [], 1};
1271if_zero(Rem) ->
1272    {Removed, Rem2, Nl} = read_to_nl(Rem),
1273    {error, Rem2, Removed, Nl}.
1274
1275
1276
1277%%===============================================================
1278%%===============================================================
1279%%===============================================================
1280%% Define macro
1281%%
1282%% Check the syntax of the macro, extract the parameters if any.
1283%% If valid macro it is added to the Defs-list.
1284%% If a macro is redefined, a warning will be given, the latest
1285%% definition is always the valid one.
1286%%===============================================================
1287%%===============================================================
1288%%===============================================================
1289
1290define(File, Err, War, L, FN) ->
1291    case define_name(File) of
1292	{ok, Rem, Name, No_of_para, Parameters, Macro, Nl} ->
1293	    {ok, Rem, Name, No_of_para, Parameters, Macro, Err, War, Nl};
1294	{{warning,no_space}, Rem, Name, No_of_para, Parameters, Macro, Nl} ->
1295	    Text = lists:flatten(io_lib:format("missing white space after `#define ~s'",[Name])),
1296	    {warning, Rem, Name, No_of_para, Parameters, Macro, Err, [{FN, L, Text}|War], Nl};
1297	{error, invalid_name, Nl} ->
1298	    Text = "invalid macro name",
1299	    {_Removed, Rem, Nl2} = read_to_nl(File),
1300	    {error, Rem, [{FN, L, Text}|Err], War, Nl+Nl2};
1301	{error, invalid_name, Name, Nl} ->
1302	    Text = lists:flatten(io_lib:format("invalid macro name `~s'",[Name])),
1303	    {_Removed, Rem, Nl2} = read_to_nl(File),
1304	    {error, Rem, [{FN, L, Text}|Err], War, Nl+Nl2};
1305	{error, illegal_arg} ->
1306	    {Removed, Rem, Nl} = read_to_nl(File),
1307	    RemovedS = detokenise(Removed),
1308	    Text = lists:flatten(io_lib:format("Invalid argument list ~s",[RemovedS])),
1309	    {error, Rem, [{FN, L, Text}|Err], War, Nl}
1310    end.
1311
1312
1313
1314%%===========================================================
1315%% Check if valid macro
1316%%===========================================================
1317define_name([]) ->
1318    {warning, no_macro};
1319define_name([space]) ->
1320    {warning, no_macro};
1321%% Macro with parameters
1322define_name([{var,Name},{char,$(}|Rem]) ->
1323    case read_para([{char,$(}|Rem]) of
1324	{ok, Rem2, Para, NoOfPara} ->
1325	    {Removed, Rem3, _Nl} = read_to_nl(Rem2),
1326	    {ok, Rem3, Name, NoOfPara, Para, Removed, 1};
1327	Error ->
1328	    Error
1329    end;
1330%% Macro without parameters
1331define_name([{var,Name}]) ->
1332    {ok, [], Name, 0, [], [], 0};
1333define_name([{var,Name}, space | Rem]) ->
1334    {Removed, Rem2, Nl} = read_to_nl(Rem),
1335    {ok, Rem2, Name, 0, [], Removed, Nl};
1336define_name([{var,Name}, {nl,_X} | Rem]) ->
1337    {ok, Rem, Name, 0, [], [], 1};
1338define_name([{var,Name} | Rem]) ->
1339    {Removed, Rem2, Nl} = read_to_nl(Rem),
1340    {{warning,no_space}, Rem2, Name, 0, [], Removed, Nl};
1341%% Invalid macro name
1342define_name([{number, Name} | _Rem]) ->
1343    {error, invalid_name, Name, 0};
1344define_name(_Rem) ->
1345    {error, invalid_name, 0}.
1346
1347
1348
1349
1350
1351
1352
1353%%===============================================================
1354%%===============================================================
1355%%===============================================================
1356%% Undefine macro
1357%%
1358%% If it is a valid undef command the macro name will be deleted
1359%% from the Defs-list
1360%%===============================================================
1361%%===============================================================
1362%%===============================================================
1363
1364undef(File, Err, War, L, FN) ->
1365    case undef(File) of
1366	{ok, Rem, Name, Nl} ->
1367	    {ok, Rem, Name, Err, War, Nl};
1368	{warning, Rem, Name, Nl} ->
1369	    Text = "ignoring the tail of the line",
1370	    {ok, Rem, Name, Err, [{FN, L, Text}|War], Nl};
1371	{error, invalid_name} ->
1372	    Text = "invalid macro name",
1373	    {_Removed, Rem, Nl} = read_to_nl(File),
1374	    {error, Rem, [{FN, L, Text}|Err], War, Nl};
1375	{error, invalid_name, Name} ->
1376	    Text = lists:flatten(io_lib:format("invalid macro name `~s'",[Name])),
1377	    {_Removed, Rem, Nl} = read_to_nl(File),
1378	    {error, Rem, [{FN, L, Text}|Err], War, Nl}
1379    end.
1380
1381%%-------------------------------------------------
1382%% Check if valid macro name
1383%%-------------------------------------------------
1384undef([]) ->
1385    {error, invalid_name, []};
1386%% Valid name
1387undef([{var,Name}]) ->
1388    {ok, [], Name, 0};
1389undef([{var,Name}, {nl,_X} | Rem]) ->
1390    {ok, Rem, Name, 1};
1391undef([{var,Name}, space, {nl,_X} | Rem]) ->
1392    {ok, Rem, Name, 1};
1393undef([{var,Name} | Rem]) ->
1394    {_Removed, Rem2, Nl} = read_to_nl(Rem),
1395    {warning, Rem2, Name, Nl};
1396%% Invalid macro name
1397undef([{number, Name} | _Rem]) ->
1398    {error, invalid_name, Name};
1399undef(_Rem) ->
1400    {error, invalid_name}.
1401
1402
1403
1404
1405
1406
1407%%===============================================================
1408%%===============================================================
1409%%===============================================================
1410%% Include macro
1411%%
1412%% Read the included file
1413%%===============================================================
1414%%===============================================================
1415%%===============================================================
1416
1417include(File, IncDir, Mio) ->
1418    case include2(File) of
1419        {ok, FileName, Rem, Nl, FileType} ->
1420            Result = read_inc_file(FileName, IncDir, Mio),
1421            case {Result, Nl, FileType} of
1422                {{ok, FileNamePath, FileCont}, _, _} ->
1423                    {ok, FileNamePath, FileCont, Rem, Nl};
1424                {skip, _, _} ->
1425                    {skip, Rem};
1426                {{error, Text}, _, own_file} ->
1427                    NameNl = count_nl(FileName,0),
1428                    Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])),
1429                    {error, Rem, Nl, Error,  NameNl};
1430                {{error, Text}, 1, sys_file} ->
1431                    NameNl = count_nl(FileName,0),
1432                    Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])),
1433                    {error, Rem, Nl, Error,  NameNl};
1434                {{error, _Text}, _, sys_file} ->
1435                    {error, Rem, Nl, "`#include' expects \"FILENAME\" or <FILENAME>"}
1436            end;
1437        {error, {_Removed, Rem, Nl}} ->
1438            {error, Rem, Nl, "`#include' expects \#FILENAME\" or <FILENAME>"}
1439    end.
1440
1441
1442
1443count_nl([],Nl) ->
1444    Nl;
1445count_nl([$\n|T],Nl) ->
1446    count_nl(T,Nl+1);
1447count_nl([_H|T],Nl) ->
1448    count_nl(T,Nl).
1449
1450%%=================================================
1451%% Extract the file name from the token list
1452%%=================================================
1453include2([space|Rem]) ->
1454    include2(Rem);
1455
1456include2([{string, FileName}]) ->
1457    {ok, FileName, [], 1, own_file};
1458include2([{string, FileName}, space]) ->
1459    {ok, FileName, [], 1, own_file};
1460include2([{string, FileName}, {nl, _X} | Rem]) ->
1461    {ok, FileName, Rem, 1, own_file};
1462include2([{string, FileName}, space, {nl, _X} | Rem]) ->
1463    {ok, FileName, Rem, 1, own_file};
1464include2([{string, _FileName}, _No_nl | Rem]) ->
1465    {error, read_to_nl(Rem)};
1466include2([{string_part, File_part}, {nl, _X} | Rem]) ->
1467     case include_read_string_file_name(File_part++[$\n], Rem, 1) of
1468	 {ok, FileName, Rem2, Nl} ->
1469	     {ok, FileName, Rem2, Nl, own_file};
1470	 error ->
1471	     {error, read_to_nl([{string_part,File_part} | Rem])}
1472     end;
1473include2([{sys_head, FileName}]) ->
1474    {ok, FileName, [], 1, sys_file};
1475include2([{sys_head, FileName}, space]) ->
1476    {ok, FileName, [], 1, sys_file};
1477include2([{sys_head, FileName}, {nl, _X}  | Rem]) ->
1478    {ok, FileName, Rem, 1, sys_file};
1479include2([{sys_head, FileName}, space, {nl, _X}  | Rem]) ->
1480    {ok, FileName, Rem, 1, sys_file};
1481include2([{sys_head, _FileName}, _No_nl | Rem]) ->
1482    {error, read_to_nl(Rem)};
1483include2([{sys_head_part ,File_part}, {nl, _X} | Rem]) ->
1484     case include_read_sys_file_name(File_part++[$\n], Rem, 1) of
1485	 {ok, FileName, Rem2, Nl} ->
1486	     {ok, FileName, Rem2, Nl, sys_file};
1487	 error ->
1488	     {error, read_to_nl([{sys_head_part, File_part} | Rem])}
1489     end;
1490include2(Rem) ->
1491    {error, read_to_nl(Rem)}.
1492
1493
1494
1495%%-------------------------------------------------
1496%% File name framed by " "
1497%%-------------------------------------------------
1498include_read_string_file_name(File, [{string, File_part}, {nl,_X} | Rem], Nl) ->
1499    {ok, File++File_part, Rem, Nl+1};
1500include_read_string_file_name(File, [{string_part, File_part}, {nl,_X} | Rem], Nl) ->
1501    include_read_string_file_name(File++File_part++[$\n], Rem, Nl+1);
1502include_read_string_file_name(_File, _X, _Nl) ->
1503    error.
1504
1505%%-------------------------------------------------
1506%% File name framed by < >
1507%%-------------------------------------------------
1508include_read_sys_file_name(File, [{sys_head, File_part}, {nl,_X} | Rem], Nl) ->
1509    {ok, File++File_part, Rem, Nl+1};
1510include_read_sys_file_name(File, [{sys_head_part, File_part}, {nl,_X} | Rem], Nl) ->
1511    include_read_sys_file_name(File++File_part++[$\n], Rem, Nl+1);
1512include_read_sys_file_name(_File, _X, _Nl) ->
1513    error.
1514
1515
1516
1517
1518
1519
1520
1521%%===============================================================
1522%%===============================================================
1523%%===============================================================
1524%% Line macro
1525%%
1526%% The line macro may redefine both the current line number and
1527%% the current file name: #line ' new_line_nr'  'new_file_name'
1528%%===============================================================
1529%%===============================================================
1530%%===============================================================
1531
1532line(File, L, FN) ->
1533    line(File, L, FN, not_defined, not_defined).
1534
1535
1536
1537line([], L, FN, _Line, _File) ->
1538    {{line, error}, {[],[],0}, {FN,L,"invalid format `#line' directive"}};
1539
1540line([space|Rem], L, FN, Line, File) ->
1541    line(Rem, L, FN, Line, File);
1542
1543%%------------------------------
1544%% Line number expected
1545%%------------------------------
1546line([{number,Number}|Rem], L, FN, not_defined, File) ->
1547    case catch list_to_integer(Number) of
1548	{'EXIT', _} ->
1549	    {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}};
1550	Int ->
1551	    line(Rem, L, FN, Int, File)
1552    end;
1553line(Rem, L, FN, not_defined, _File) ->
1554    {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}};
1555
1556%%------------------------------
1557%% File name or newline expected
1558%%------------------------------
1559line([{nl, _NL}|Rem], _L, FN, Line, not_defined) ->
1560    {{line, ok}, {[],Rem,1}, Line, FN, io_lib:format("~n~p ~p #",[FN, Line-1])};
1561line([{string,NewFN}|Rem], _L, _FN, Line, not_defined) ->
1562    {{line, ok}, read_to_nl(Rem), Line, NewFN, io_lib:format("~n~p ~p #",[NewFN, Line-1])};
1563line(Rem, L, FN, _Line, _File) ->
1564    {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}}.
1565
1566
1567
1568
1569%%======================================================================================
1570%%======================================================================================
1571%%======================================================================================
1572%% Source line
1573%%
1574%%
1575%% Output:  {Str, Err, War, Rem, SelfRef}
1576%%
1577%% Description: The input source line is searched for macros. If a macro is found it
1578%%              is expanded. The result of an expansion is rescanned for more macros.
1579%%              To prevent infinite loops if the macro is self referring
1580%%              an extra token is put into the Rem list. The variable SelfRef
1581%%              contains all the macros which are inhibited to be expanded.
1582%%              A special specae token is also inserted to prevent not wanted
1583%%              concatinations if one of the variables to be concatinated is expanded.
1584%%======================================================================================
1585%%======================================================================================
1586%%======================================================================================
1587
1588source_line(Str, Rem, SelfRef, Defs, Err, War, L, FN) ->
1589    {Rem2, Para, No_of_para} = case read_para(Rem) of
1590				   {ok, RemT, ParaT, No_of_paraT} ->
1591				       {RemT, ParaT, No_of_paraT};
1592				   {error, illegal_arg} ->
1593				       {[], [], 0}
1594			       end,
1595
1596
1597    %%-------------------------------------------------
1598    %% Check if a valid macro
1599    %%-------------------------------------------------
1600    case lists:keysearch(Str, 1, Defs) of
1601	%% a macro without parameters
1602	{value, {Str, 0, _MacroPara, Macro}} ->
1603	    case lists:member(Str, SelfRef) of
1604		true ->
1605		    {[Str], Err, War, Rem, SelfRef};
1606		false ->
1607		    ExpandedRes2 = sl_mark_expanded(Macro, Str),
1608		    {[], Err, War, ExpandedRes2 ++ [{self_ref,Str}|Rem], [Str|SelfRef]}
1609	    end;
1610
1611	%% a macro with parameters
1612	{value, {Str, N, _MacroPara, Macro}} when N == No_of_para ->
1613	    case lists:member(Str, SelfRef) of
1614		true ->
1615		    {[Str], Err, War, Rem, SelfRef};
1616		false ->
1617		    ExpandedRes = sl_macro_expand(Macro, Para, Defs),
1618		    ExpandedRes2 = sl_mark_expanded(ExpandedRes, Str),
1619		    {[], Err, War, ExpandedRes2 ++ [{self_ref,Str}|Rem2], [Str|SelfRef]}
1620	    end;
1621
1622	%% a variable, because it doesn't have any parameters
1623	{value, {Str, _N, _MacroPara, _Macro}} when No_of_para == 0 ->
1624	    {Str, Err, War, Rem, SelfRef};
1625
1626	%% illegal no of parameters
1627	{value, {Str, N, _MacroPara, _Macro}} when No_of_para < N ->
1628	    Text = io_lib:format(" macro `~s' used with just ~p arg",[Str,No_of_para]),
1629	    Err2 = {FN, L, lists:flatten(Text)},
1630	    {Str, [Err2|Err], War, Rem, SelfRef};
1631	{value, {Str, _N, _MacroPara, _Macro}} ->
1632	    Text = io_lib:format(" macro `~s' used with too many (~p) args",[Str,No_of_para]),
1633	    Err2 = {FN, L, lists:flatten(Text)},
1634	    {Str, [Err2|Err], War, Rem, SelfRef};
1635
1636	%% no macro
1637	false ->
1638	    {Str, Err, War, Rem, SelfRef}
1639    end.
1640
1641
1642
1643
1644
1645%%=================================================
1646%% Expand a macro
1647%%=================================================
1648sl_macro_expand(Macro, Para, Defs) ->
1649    sl_macro_expand(Macro, Para, Defs, []).
1650
1651
1652%%...................
1653%% End
1654%%...................
1655sl_macro_expand([], _Para, _Defs, Res) ->
1656    lists:reverse(Res);
1657
1658%%...................
1659%% Concatination
1660%%...................
1661%% para ## para
1662sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) ->
1663    Exp = sl_para_para({para, N},{para, M}, Para),
1664    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1665%% para## para
1666sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) ->
1667    Exp = sl_para_para({para, N},{para, M}, Para),
1668    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1669%% para ##para
1670sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) ->
1671    Exp = sl_para_para({para, N},{para, M}, Para),
1672    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1673%% para##para
1674sl_macro_expand([{para, N}, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) ->
1675    Exp = sl_para_para({para, N},{para, M}, Para),
1676    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1677
1678%% para ## var
1679sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, {var, Var}|T], Para, Defs, Res) ->
1680    Exp = sl_para_var({para, N}, {var, Var}, Para),
1681    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1682%% para## var
1683sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, {var, Var} | T], Para, Defs, Res) ->
1684    [{var, VarN}] = lists:nth(N,Para),
1685    sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]);
1686%% para ##var
1687sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, {var, Var} | T], Para, Defs, Res) ->
1688    [{var, VarN}] = lists:nth(N,Para),
1689    sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]);
1690%% para##var
1691sl_macro_expand([{para, N}, {char,$#}, {char,$#}, {var, Var} | T], Para, Defs, Res) ->
1692    [{var, VarN}] = lists:nth(N,Para),
1693    sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]);
1694
1695%% var ## para
1696sl_macro_expand([{var, Var}, space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) ->
1697    Exp = sl_var_para({var, Var},{para, M}, Para),
1698    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1699%% var## para
1700sl_macro_expand([{var, Var}, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) ->
1701    Exp = sl_var_para({var, Var},{para, M}, Para),
1702    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1703%% var ##para
1704sl_macro_expand([{var, Var}, space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) ->
1705    Exp = sl_var_para({var, Var},{para, M}, Para),
1706    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1707%% var##para
1708sl_macro_expand([{var, Var}, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) ->
1709    Exp = sl_var_para({var, Var},{para, M}, Para),
1710    sl_macro_expand(Exp++T, Para, Defs, [space |Res]);
1711
1712%% expanded ## para
1713sl_macro_expand([space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) ->
1714    [{var, VarM}] = lists:nth(M,Para),
1715    sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]);
1716%% expanded## para
1717sl_macro_expand([{char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) ->
1718    [{var, VarM}] = lists:nth(M,Para),
1719    sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]);
1720%% expanded ##para
1721sl_macro_expand([space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) ->
1722    [{var, VarM}] = lists:nth(M,Para),
1723    sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]);
1724%% expanded##para
1725sl_macro_expand([{char,$#}, {char,$#}, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) ->
1726    [{var, VarM}] = lists:nth(M,Para),
1727    sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]);
1728
1729%% para ## ?
1730sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, X | T], Para, Defs, Res) ->
1731    Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []),
1732    sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res]));
1733%% para## ?
1734sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, X | T], Para, Defs, Res) ->
1735    Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []),
1736    sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res]));
1737%% para ##?
1738sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, X | T], Para, Defs, Res) ->
1739    Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []),
1740    sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res]));
1741%% para##?
1742sl_macro_expand([{para, N}, {char,$#}, {char,$#}, X | T], Para, Defs, Res) ->
1743    Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []),
1744    sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res]));
1745
1746sl_macro_expand([{char,$#}, {char,$#}, space |T], Para, Defs, [space|Res]) ->
1747    sl_macro_expand(T, Para, Defs, Res);
1748sl_macro_expand([{char,$#}, {char,$#} |T], Para, Defs, [space|Res]) ->
1749    sl_macro_expand(T, Para, Defs, Res);
1750sl_macro_expand([{char,$#}, {char,$#}, space |T], Para, Defs, Res) ->
1751    sl_macro_expand(T, Para, Defs, Res);
1752sl_macro_expand([{char,$#}, {char,$#} |T], Para, Defs, Res) ->
1753    sl_macro_expand(T, Para, Defs, Res);
1754
1755%%...................
1756%% Stringification
1757%%...................
1758sl_macro_expand([{char,$#}, {para, N}|T], Para, Defs, Res) ->
1759    Nth = lists:nth(N,Para),
1760    Tokens = detokenise(Nth),
1761    sl_macro_expand(T, Para, Defs, [{string,Tokens}|Res]);
1762sl_macro_expand([{char,$#}, space, {para, N}|T], Para, Defs, Res) ->
1763    Nth = lists:nth(N,Para),
1764    Tokens = detokenise(Nth),
1765    sl_macro_expand(T, Para, Defs, [{string,Tokens}|Res]);
1766
1767%%...................
1768%% A parameter
1769%%...................
1770sl_macro_expand([{para, N}|T], Para, Defs, Res) ->
1771    Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []),
1772    sl_macro_expand(T, Para, Defs, lists:flatten([Reexp|Res]));
1773
1774%%...................
1775%% No parameter
1776%%...................
1777sl_macro_expand([H|T], Para, Defs, Res) ->
1778    sl_macro_expand(T, Para, Defs, [H|Res]).
1779
1780
1781
1782%%-------------------------------------------------
1783%% Expand parameters
1784%%-------------------------------------------------
1785sl_para_para({para, N}, {para, M}, Para) ->
1786    case sl_para_1st(lists:nth(N,Para)) of
1787	{ok, Para1st} ->
1788	    Para1st ++ sl_para_2nd(lists:nth(M,Para));
1789	{exp, Para1st} ->
1790	    Para1st ++ sl_para_2nd(lists:nth(M,Para)) ++ [space_exp];
1791	{space, Para1st} ->
1792	    Para1st ++ [space_exp | sl_para_2nd(lists:nth(M,Para))]
1793    end.
1794
1795
1796sl_var_para(Var, {para, M}, Para) ->
1797    [Var|sl_para_2nd(lists:nth(M,Para))].
1798
1799
1800sl_para_var({para, N}, Var, Para) ->
1801    case sl_para_1st(lists:nth(N,Para)) of
1802	{ok, Para1st} ->
1803	    Para1st ++ [Var];
1804	{exp, Para1st} ->
1805	    Para1st ++ [Var | space_exp];
1806	{space, Para1st} ->
1807	    Para1st ++ [space_exp | Var]
1808    end.
1809
1810
1811sl_para_1st([{var, Var}]) ->
1812    {ok,[{expanded,Var}]};
1813sl_para_1st([{var, Var}, space]) ->
1814    {ok,[{expanded,Var}]};
1815sl_para_1st([{var, Var}, space_exp]) ->
1816    {exp, [{expanded,Var}]};
1817sl_para_1st(L) ->
1818    {space, L}.
1819
1820sl_para_2nd([{var, Var}]) ->
1821    [{expanded,Var}];
1822sl_para_2nd([{var, Var}, space_exp]) ->
1823    [{expanded,Var}];
1824sl_para_2nd([space, {var, Var}]) ->
1825    [{expanded,Var}];
1826sl_para_2nd([space_exp, {var, Var}]) ->
1827    [{expanded,Var}];
1828sl_para_2nd(L) ->
1829    L++[space].
1830
1831
1832
1833%%-------------------------------------------------
1834%% Check if the expansion is a valid macro,
1835%% do not reexpand if concatination
1836%%-------------------------------------------------
1837sl_macro_reexpand([], _Defs, Result) ->
1838    Result;
1839sl_macro_reexpand([{var,Var}|Rem], Defs, Result) ->
1840    case lists:keysearch(Var, 1, Defs) of
1841	{value, {Var, 0, _MacroPara, Macro}} ->
1842	    Rem2 = case Rem of
1843		     [space | RemT] ->
1844			 [space_exp | RemT];
1845		     _ ->
1846			 [space_exp | Rem]
1847		 end,
1848	    sl_macro_reexpand(Macro++Rem2, Defs, Result);
1849	_ ->
1850	    sl_macro_reexpand(Rem, Defs, [{var,Var}|Result])
1851    end;
1852sl_macro_reexpand([H|Rem], Defs, Result) ->
1853    sl_macro_reexpand(Rem, Defs, [H|Result]).
1854
1855
1856
1857%%-------------------------------------------------
1858%% Self referring macros are marked not be reexpanded
1859%%-------------------------------------------------
1860sl_mark_expanded(QQ, Str) ->
1861    sl_mark_expanded(QQ, Str, []).
1862
1863sl_mark_expanded([], _Str, Res) ->
1864    lists:reverse(Res);
1865sl_mark_expanded([H|T], Str, Res) ->
1866    case H of
1867	{_,Str} ->
1868	    sl_mark_expanded(T, Str, [{expanded, Str}|Res]);
1869	_ ->
1870	    sl_mark_expanded(T, Str, [H|Res])
1871    end.
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881%%======================================================================================
1882%%======================================================================================
1883%%======================================================================================
1884%% Misceleaneous functions
1885%%======================================================================================
1886%%======================================================================================
1887%%======================================================================================
1888
1889
1890%%===============================================================
1891%%  Check the Flags for include directories
1892%%===============================================================
1893include_dir(Flags) when is_list(Flags)->
1894    include_dir(Flags,[]);
1895include_dir(_Flags) ->
1896    [].
1897
1898include_dir(Flags,IncDirs) ->
1899    case string:str(Flags,"-I") of
1900	0 ->
1901	    lists:reverse(IncDirs);
1902	X ->
1903	    {NewDir, RemainingFlags} =
1904		gobble_inc_dir(string:sub_string(Flags, X+2),nq,[]),
1905	    include_dir(RemainingFlags, [NewDir|IncDirs])
1906    end.
1907
1908% nq = not-quoted, q = quoted.
1909% Possible strange scenarios:
1910%  /usr/test\ ing/
1911%  "/usr/test ing/"
1912%  /usr/test\"ing/
1913%  "/usr/test\"ing/"
1914gobble_inc_dir([],nq,Acc) ->
1915    % Only accept nq here, if we end up here in q mode the user has missed a "
1916    {lists:reverse(Acc),[]};
1917gobble_inc_dir([$\\,$"|R],Q,Acc) ->
1918    gobble_inc_dir(R,Q,[$"|Acc]);
1919gobble_inc_dir([$"|R],nq,Acc) ->
1920    gobble_inc_dir(R,q,Acc);
1921gobble_inc_dir([$"|R],q,Acc) ->
1922    gobble_inc_dir(R,nq,Acc);
1923gobble_inc_dir([$\\,$ |R],nq,Acc) ->
1924    gobble_inc_dir(R,nq,[$ |Acc]);
1925gobble_inc_dir([$ |R],nq,Acc) ->
1926    {lists:reverse(Acc),R};
1927gobble_inc_dir([C|R],Q,Acc) ->
1928    gobble_inc_dir(R,Q,[C|Acc]).
1929
1930
1931%%===============================================================
1932%%  Read a included file. Try current dir first then the IncDir list
1933%%===============================================================
1934
1935read_inc_file(FileName, IncDir, Mio) ->
1936    case find_inc_file(FileName, IncDir) of
1937        {ok, AbsFile} ->
1938            %% is included before?
1939            case lists:member(FileName, Mio#mio.included) of
1940                false ->
1941                    case catch file:read_file(AbsFile) of
1942                        {ok, Bin} ->
1943                            FileList = binary_to_list(Bin),
1944                            {ok, AbsFile, FileList};
1945                        {error, Text} ->
1946			    {error, Text}
1947                    end;
1948                true ->
1949                    skip
1950            end;
1951        {error, Text} ->
1952            {error, Text}
1953    end.
1954
1955find_inc_file(FileName, IncDir) ->
1956    case catch file:read_file_info(FileName) of
1957	{ok, _} ->
1958	    {ok, FileName};
1959	{error, _} ->
1960	    find_inc_file2(FileName, IncDir)
1961    end.
1962
1963find_inc_file2(_FileName, []) ->
1964    {error, "No such file or directory"};
1965find_inc_file2(FileName, [D|Rem]) ->
1966    Dir = case lists:last(D) of
1967	      $/ ->
1968		  D;
1969	      _ ->
1970		  D++"/"
1971	  end,
1972    case catch file:read_file_info(Dir++FileName) of
1973	{ok, _} ->
1974	    {ok, Dir++FileName};
1975	{error, _} ->
1976	    find_inc_file2(FileName, Rem)
1977    end.
1978
1979
1980%%===============================================================
1981%%  Read parameters of a macro or a variable in a source line
1982%%===============================================================
1983read_para([{char,$(} | Rem]) ->
1984    read_para(Rem, 1, [], [], 1);
1985read_para([space,{char,$(} | Rem]) ->
1986    read_para(Rem, 1, [], [], 1);
1987read_para(_Rem) ->
1988    {ok, [], [], 0}.
1989
1990
1991%% Abrupt end of the list
1992read_para([], _NoOfParen, _Current, _Para, _NoOfPara) ->
1993    {error, illegal_arg};
1994%% All parameters checked
1995read_para([{char,$)}|Rem], 1, [], Para, NoOfPara) ->
1996    {ok, Rem, lists:reverse(Para), NoOfPara};
1997read_para([{char,$)}|Rem], 1, Current, Para, NoOfPara) ->
1998    {ok, Rem, lists:reverse([Current|Para]), NoOfPara};
1999
2000%% Continue reading
2001read_para([{char,$)}|Rem], NoOfParen, Current, Para, NoOfPara) ->
2002    read_para(Rem, NoOfParen-1, Current++[{char,$)}], Para, NoOfPara);
2003read_para([{char,$(}|Rem], NoOfParen, Current, Para, NoOfPara) ->
2004    read_para(Rem, NoOfParen+1, Current++[{char,$(}], Para, NoOfPara);
2005read_para([{char,$,}|Rem], NoOfParen, Current, Para, NoOfPara) when NoOfParen == 1 ->
2006    read_para(Rem, NoOfParen, [], [Current|Para], NoOfPara+1);
2007read_para([space|Rem], NoOfParen, [], Para, NoOfPara) ->
2008    read_para(Rem, NoOfParen, [], Para, NoOfPara);
2009read_para([X|Rem], NoOfParen, Current, Para, NoOfPara) ->
2010    read_para(Rem, NoOfParen, Current++[X], Para, NoOfPara).
2011
2012
2013
2014
2015
2016
2017%%===================================================================================
2018%% check if a macro is already defined
2019%%===================================================================================
2020is_define_ok(Name, No_of_para, Parameters, Macro, Defs) ->
2021
2022    case lists:keysearch(Name, 1, Defs) of
2023	{value, {Name, No_of_para, _MacroPara, Macro}} ->
2024	    {yes, Defs};
2025	{value, _} ->
2026	    Defs2 = lists:keydelete(Name, 1, Defs),
2027	    NewMacro = is_define_ok_check_para(Parameters, Macro, []),
2028	    case is_stringify_ok(NewMacro) of
2029		yes ->
2030		    {no, [{Name, No_of_para, Parameters, NewMacro}|Defs2]};
2031		no ->
2032		    ErrorText = "`#' operator is not followed by a macro argument name",
2033		    {error, ErrorText, [{Name, No_of_para, Parameters, NewMacro}|Defs2]}
2034	    end;
2035	false ->
2036	    NewMacro = is_define_ok_check_para(Parameters, Macro, []),
2037	    case is_stringify_ok(NewMacro) of
2038		yes ->
2039		    {yes, [{Name, No_of_para, Parameters, NewMacro}|Defs]};
2040		no ->
2041		    ErrorText = "`#' operator is not followed by a macro argument name",
2042		    {error, ErrorText, [{Name, No_of_para, Parameters, NewMacro}|Defs]}
2043	    end
2044    end.
2045
2046is_define_ok_check_para(_Para, [], Result) ->
2047    lists:reverse(Result);
2048
2049is_define_ok_check_para(Para, [H|T], Result) ->
2050    case define_arg_para_number(1, Para, H) of
2051	no_para ->
2052	    is_define_ok_check_para(Para, T, [H|Result]);
2053	N ->
2054	    is_define_ok_check_para(Para, T, [{para,N}|Result])
2055    end.
2056
2057define_arg_para_number(_N, [], _Current) ->
2058    no_para;
2059define_arg_para_number(N, [H|_Para], Current) when H == [Current] ->
2060    N;
2061define_arg_para_number(N, [_H|Para], Current) ->
2062    define_arg_para_number(N+1, Para, Current).
2063
2064
2065is_stringify_ok([]) ->
2066    yes;
2067is_stringify_ok([{char,$#},{char,$#}|T]) ->
2068    is_stringify_ok(T);
2069is_stringify_ok([{char,$#},space,{para,_X}|T]) ->
2070    is_stringify_ok(T);
2071is_stringify_ok([{char,$#},{para,_X}|T]) ->
2072    is_stringify_ok(T);
2073is_stringify_ok([{char,$#},space,{var,_X}|T]) ->
2074    is_stringify_ok(T);
2075is_stringify_ok([{char,$#},{var,_X}|T]) ->
2076    is_stringify_ok(T);
2077is_stringify_ok([{char,$#},space,{nl,_X}|_T]) ->
2078    no;
2079is_stringify_ok([{char,$#},{nl,_X}|_T]) ->
2080    no;
2081is_stringify_ok([{char,$#}|_T]) ->
2082    no;
2083is_stringify_ok([_H|T]) ->
2084    is_stringify_ok(T).
2085
2086%%===================================================================================
2087%% check if a macro is already defined
2088%%===================================================================================
2089is_defined_before(Name, No_of_para, Defs) ->
2090    case lists:keysearch(Name, 1, Defs) of
2091	{value, {Name, No_of_para, _MacroPara, _Macro}} ->
2092	    yes;
2093	{value, _} ->
2094	    no;
2095	false ->
2096	    no
2097    end.
2098
2099
2100
2101
2102%%===================================================================================
2103%% read_to_nl(File)
2104%%===================================================================================
2105read_to_nl([space|Rem]) ->
2106    read_to_nl(Rem, [], 1);
2107read_to_nl(Rem) ->
2108    read_to_nl(Rem, [], 1).
2109
2110read_to_nl([], Result, Nl) ->
2111    {lists:reverse(Result), [], Nl};
2112read_to_nl([{nl, _N}|Rem], [{string_part,String} | Result], Nl) ->
2113    read_to_nl(Rem, [nl, {string_part,String}|Result], Nl+1);
2114read_to_nl([{nl, _N}|Rem], [{sys_head_part,String} | Result], Nl) ->
2115    read_to_nl(Rem, [nl, {sys_head_part,String}|Result], Nl+1);
2116read_to_nl([{nl, _N}|Rem], Result, Nl) ->
2117    {lists:reverse(Result), Rem, Nl};
2118read_to_nl([space|Rem], Result, Nl) ->
2119    read_to_nl(Rem, [space|Result], Nl);
2120read_to_nl([{X,String}|Rem], Result, Nl) ->
2121    read_to_nl(Rem, [{X,String}|Result], Nl).
2122
2123
2124
2125
2126%%===========================================================
2127%% Read characters until next newline
2128%%===========================================================
2129%read_to_nl2(Str) -> read_to_nl2([],Str).
2130
2131%read_to_nl2(Line, [])        -> {Line,[]};
2132%read_to_nl2(Line, [$\n|Str]) -> {Line, Str};
2133%read_to_nl2(Line, [X|Str])   -> read_to_nl2([X|Line], Str).
2134
2135
2136
2137
2138%%===========================================================
2139%% Remove leading spaces from a list
2140%%===========================================================
2141remove_leading_spaces([?space|List]) ->
2142    remove_leading_spaces(List);
2143remove_leading_spaces([?tab|List]) ->
2144    remove_leading_spaces(List);
2145remove_leading_spaces(List) ->
2146    List.
2147
2148
2149
2150
2151%%===========================================================
2152%% Skip characters until next newline
2153%%===========================================================
2154skip_to_nl([]) -> [];
2155skip_to_nl([$\n | Str]) -> Str;
2156skip_to_nl([$\\,$\n | Str]) -> [$/,$/|Str];
2157skip_to_nl([_|Str]) -> skip_to_nl(Str).
2158
2159
2160
2161
2162month(1) -> "Jan";
2163month(2) -> "Feb";
2164month(3) -> "Mar";
2165month(4) -> "Apr";
2166month(5) -> "May";
2167month(6) -> "Jun";
2168month(7) -> "Jul";
2169month(8) -> "Aug";
2170month(9) -> "Sep";
2171month(10) -> "Oct";
2172month(11) -> "Nov";
2173month(12) -> "Dec".
2174
2175
2176%% Multiple Include Optimization
2177%%
2178%% Algorithm described at:
2179%% http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
2180update_mio({include, FileName}, #mio{included=Inc}=Mio) ->
2181    Mio#mio{valid=false, included=[FileName|Inc]};
2182
2183%% valid=false & cmacro=undefined indicates it is already decided this file is
2184%% not subject to MIO
2185update_mio(_, #mio{valid=false, depth=0, cmacro=undefined}=Mio) ->
2186    Mio;
2187
2188%% if valid=true, there is no non-whitespace tokens before this ifndef
2189update_mio({'ifndef', Macro}, #mio{valid=true, depth=0, cmacro=undefined}=Mio) ->
2190    Mio#mio{valid=false, cmacro=Macro, depth=1};
2191
2192%% detect any tokens before top level #ifndef
2193update_mio(_, #mio{valid=true, depth=0, cmacro=undefined}=Mio) ->
2194    Mio#mio{valid=false};
2195
2196%% If cmacro is alreay set, this is after the top level #endif
2197update_mio({'ifndef', _}, #mio{valid=true, depth=0}=Mio) ->
2198    Mio#mio{valid=false, cmacro=undefined};
2199
2200%% non-top level conditional, just update depth
2201update_mio({'ifndef', _}, #mio{depth=D}=Mio) when D > 0 ->
2202    Mio#mio{depth=D+1};
2203update_mio('ifdef', #mio{depth=D}=Mio) ->
2204    Mio#mio{depth=D+1};
2205update_mio('if', #mio{depth=D}=Mio) ->
2206    Mio#mio{depth=D+1};
2207
2208%% top level #else #elif invalidates multiple include optimization
2209update_mio('else', #mio{depth=1}=Mio) ->
2210    Mio#mio{valid=false, cmacro=undefined};
2211update_mio('else', Mio) ->
2212    Mio;
2213update_mio('elif', #mio{depth=1}=Mio) ->
2214    Mio#mio{valid=false, cmacro=undefined};
2215update_mio('elif', Mio) ->
2216    Mio;
2217
2218%% AT exit to top level, if the controlling macro is not set, this could be the
2219%% end of a non-ifndef conditional block, or there were tokens before entering
2220%% the #ifndef block. In either way, this invalidates the MIO
2221%%
2222%% It doesn't matter if `valid` is true at the time of exiting, it is set to
2223%% true.  This will be used to detect if more tokens are following the top
2224%% level #endif.
2225update_mio('endif', #mio{depth=1, cmacro=undefined}=Mio) ->
2226    Mio#mio{valid=false, depth=0};
2227update_mio('endif', #mio{depth=1}=Mio) ->
2228    Mio#mio{valid=true, depth=0};
2229update_mio('endif', #mio{depth=D}=Mio) when D > 1 ->
2230    Mio#mio{valid=true, depth=D-1};
2231
2232%%if more tokens are following the top level #endif.
2233update_mio('endif', #mio{depth=1, cmacro=undefined}=Mio) ->
2234    Mio#mio{valid=false, depth=0};
2235update_mio('endif', #mio{depth=D}=Mio) when D > 0 ->
2236    Mio#mio{valid=true, depth=D-1};
2237update_mio(_, Mio) ->
2238    Mio#mio{valid=false}.
2239
2240%% clear `valid`, this doesn't matter since #endif will restore it if
2241%% appropriate
2242update_mio(Mio) ->
2243    Mio#mio{valid=false}.
2244
2245
2246