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