diff options
Diffstat (limited to 'lib/ic/src/ic_pp.erl')
-rw-r--r-- | lib/ic/src/ic_pp.erl | 2245 |
1 files changed, 0 insertions, 2245 deletions
diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl deleted file mode 100644 index 8c2e3a0ffe..0000000000 --- a/lib/ic/src/ic_pp.erl +++ /dev/null @@ -1,2245 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% -%% --module(ic_pp). - --export([run/2]). - --define(is_number(X), X >= $0, X =< $9). --define(is_upper(X), X >= $A, X =< $Z). --define(is_lower(X), X >= $a, X =< $z). --define(is_underline(X), X == $_). --define(is_tab(X), X == 9). --define(is_space(X), X == 32). --define(tab, 9). --define(space, 32). - - -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== -%% Preprocessor -%% -%% This preprocessor is equivalent to the gcc-preprocessor. It takes a file name and -%% a list of preprocessor flags as an input and returns a processed text file. -%% -%% The processing is done in two phases. -%% In the first phase the input file is tokenised into a list where all comments are -%% replaced by a space and all "backslash-newline" sequences are removed. -%% -%% In the second phase all macros are expanded. - -%% %% %% NOTE: #if, #else, and #elif are not yet implemented. -%% Only '#if 0' is implemented to be possible to keep old code as a comment for -%% future refence by putting '#if 0' before it and '#endif' after it. -%% -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== - - -%%====================================================================================== -%% Variables which are used throughout the program: -%% ------------------------------------------------ -%% -%% Command A preprocessor command -%% Current Temporary variable used when tokenising the file -%% Defs The currently valid macro definitions -%% Err The current list of errors = [{file, line number, error text}, ...] -%% File The tokenised file (or what remains of it when expanding the macros) -%% Flags The preprocessor flags -%% FN or FileName Tbe name of the current file -%% IfCou Used for ifdef/ifndef/endif values: check_all | {endif, Endif, IfLine} -%% Endif = number of matching endif's yet to be found -%% Ifline = the line number for the the first found ifdef/ifndef -%% IncDir Directories to be searched for included files -%% IncFile Stack of included files -%% IncLine The line numer of an include -%% L The current line number -%% Name Name of a macro -%% Nl Number of encountered newlines -%% No_of_para Numer of parameters of the currently expanded macro -%% Out The result of the second step -%% Parameters The parameters of the currently expanded macro -%% PrevFile The name of the "parent" file which includes the currently expanded file -%% Rem Remaining of the file currently being expanded -%% Removed The tokens removed, used when removing tokens to the end of a line -%% Result The current result of something -%% SelfRef List of variables which shoud not be expanded at the rescan to avoid -%% endless loops due to self referencing -%% Str Temporary string -%% Text A variable used for string handling, e.g at error handling -%% Tokens Temoprary list when tokenising -%% War The current list of warnings = [{file, line number, warning text}, ...] -%% X Temporary variable used when the value is not important -%% Y Temporary variable used when the value is not important -%% -%%====================================================================================== - -%% Multiple Include Optimization -%% -%% Algorithm described at: -%% http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html --record(mio, {valid = true, %% multiple include valid - cmacro, %% controlling macro of the current conditional directive - depth = 0, %% conditional directive depth - included = []}). - - - -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== -%% The main entry for the preprocessor -%% -%% -%% Output {ok, Out, War} | {error, Err} -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== -run(FileName, Flags) when is_atom(FileName) -> - run(atom_to_list(FileName), Flags); - -run(FileName, Flags) -> - IncDir = include_dir(Flags), - - case catch file:read_file(FileName) of - {ok, Bin} -> - FileList = binary_to_list(Bin), - run(FileList, FileName, IncDir, Flags); - {error, _} -> - Text = "No such file or directory", - {error, [FileName ++ ": " ++ Text]} - end. - - -run(FileList, FileName, IncDir, Flags) -> - %%---------------------------------------------------------- - %% Run the first phase, i.e tokenise the file - %%---------------------------------------------------------- - File = tokenise(FileList, FileName), - - %%---------------------------------------------------------- - %% Run the second phase, i.e expand macros - %%---------------------------------------------------------- - {Out, Err, War, _Defs, _Mio, IfCou} = expand(File, FileName, IncDir, Flags), - - %%---------------------------------------------------------- - %% Check if all #if #ifdef #ifndef have a matching #endif - %%---------------------------------------------------------- - IfError = case IfCou of - {endif, Endif, IfLine} when Endif > 0 -> - [{FileName, IfLine, "unterminated `#if' conditional"}]; - _ -> - [] - end, - - Err2 = Err++IfError, - - case Err2 of - [] -> - {ok, lists:flatten(lists:reverse(Out)), lists:reverse(War)}; - _ -> - {error, lists:reverse(Err2)} - end. - -%%====================================================================================== -%% The entry for all included files -%% -%% -%% Output {Out, Err, War, Defs, MultipleIncludeValid} -%%====================================================================================== -run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir, Mio) -> - - %%---------------------------------------------------------- - %% Run the first phase, i.e tokenise the file - %%---------------------------------------------------------- - [PrevFile | _T] = IncFile, - {File, FileInfoStart, FileInfoEnd} = - tokenise(FileList, FileName, IncLine, PrevFile), - - %%---------------------------------------------------------- - %% Run the second phase, i.e expand macros - %%---------------------------------------------------------- - {Out2, Err2, War2, Defs2, Mio2, IfCou2} = - expand([FileInfoStart|File]++FileInfoEnd, Defs, Err, War, - [FileName|IncFile], IncDir, - #mio{included=Mio#mio.included}), - - MergeIncluded = sets:to_list(sets:from_list(Mio#mio.included ++ Mio2#mio.included)), - - Mio3 = - case {Mio2#mio.valid, Mio2#mio.cmacro} of - {V, Macro} when V == false; - Macro == undefined -> - update_mio(Mio#mio{included=MergeIncluded}); - {true, _} -> - update_mio({include, FileName}, Mio#mio{included=MergeIncluded}) - end, - - %%---------------------------------------------------------- - %% Check if all #if #ifdef #ifndef have a matching #endif - %%---------------------------------------------------------- - IfError = case IfCou2 of - {endif, Endif, IfLine} when Endif > 0 -> - [{FileName, IfLine, "unterminated `#if' conditional"}]; - _ -> - [] - end, - - {Out2, Defs2, Err2++IfError, War2, Mio3}. - - - - -%%=================================================================================== -%%=================================================================================== -%%=================================================================================== -%% Tokenise the file -%% -%% -%% Output: File -%% -%% Description: -%% The input file is tokenised into a list where all comments are replaced -%% by a space and all "backslash-newline" sequences are removed. -%% -%% A file information is added at start and end of an included file to set the -%% current file name and line number. -%% -%% -%% A token consists of: -%% -------------------- -%% -%% {char, Char} special characters like ()[]{},!%& etc -%% {command,Command} a macro command -%% {expanded,Str} an expanded variable, used to prevent infinite loops -%% at self reference -%% {file_info,FI} start and end information of a file -%% FI is a string of the following format: -%% "# Line FileName Int" were Int is -%% 1 if start of an included file, -%% 2 when returning to "parent" file -%% {nl, L} newline -%% {number,Num) variable, a string starting with a number -%% {self_ref,Var} to allow reference to a variable again, used when expanding -%% self refering macros -%% space a space -%% space_exp a space, special notation to prevent not wanted concatination -%% {string, Str} a (tail of a) string constant -%% {string_part, Str} a head of a string constant defined on several consecutive lines -%% {sys_head, Str} (tail of) the file name of included system file -%% {sys_head_part , Str} the file name of included system file -%% {var,Var} variable, a string starting with minuscular or capital letter or -%% an underline -%% -%% Note, comments are not removed within a character or string constant -%% or inside an include-definition where the file name is delimited with < > -%%=================================================================================== -%%=================================================================================== -%%=================================================================================== - -tokenise(File, FileName) -> - {Result, _L} = token(File, 2, [], not_set, 0), - FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\"~n",[FileName]))), - FileInfoStart = {file_info, FI_start}, - [FileInfoStart | Result]. - -tokenise(File, FileName, IncLine, PrevFile) -> - {Result, _L} = token(File, 2, [], not_set, 0), - FI_start = lists:reverse(lists:flatten(io_lib:format("# 1 \"~ts\" 1~n",[FileName]))), - FileInfoStart = {file_info, FI_start}, - FI_end = lists:reverse(lists:flatten(io_lib:format("# ~p \"~ts\" 2~n~n",[IncLine-1,PrevFile]))), - FileInfoEnd = [{file_info, FI_end}], - {Result, FileInfoStart, FileInfoEnd}. -% [FileInfoStart | Result] ++ FileInfoEnd. - - -%%=================================================================================== -%% token(InputFile, L, Result, Gen) -%% Gen information of the first token on the line, default = not_set -%% -%% Output: File -%%=================================================================================== - -%%================================================================== -%% Normal line -%%================================================================== -%%--------------------------------------- -%% All file tokenised -%%--------------------------------------- -token([], L, [{nl,NL}|Result], _Gen, _BsNl) when L == NL+1-> - {lists:reverse([{nl,NL}|Result]), L}; -token([], L, Result, _Gen, _BsNl) -> - {lists:reverse([{nl,L-1}|Result]), L}; - -%%--------------------------------------- -%% String -%%--------------------------------------- -token(File, L, Result, string, BsNl) -> - case token_string(File, []) of - {Rem, Str, nl} -> - Result1 = [{nl, L}, {string,Str} | Result], - token(Rem, L+1, Result1, string, BsNl); - {Rem, Str} -> - token(Rem, L, [{string,Str}|Result], not_set, BsNl) - end; - -token([$"|File], L, Result, Gen, BsNl) -> - case token_string(File, []) of - {Rem, Str, nl} -> - Result1 = [{nl, L}, {string_part,Str} | Result], - token(Rem, L+1, Result1, string, BsNl); - {Rem, Str} -> - token(Rem, L, [{string,Str}|Result], Gen, BsNl) - end; - -%%--------------------------------------- -%% Include with < > -%%--------------------------------------- -token(File, L, Result, include, BsNl) -> - case token_include(File, []) of - {Rem, Str, nl} -> - Result1 = [{nl, L}, {sys_head,Str} | Result], - token(Rem, L+1, Result1, include, BsNl); - {Rem, Str} -> - token(Rem, L, [{sys_head,Str}|Result], not_set, BsNl) - end; - -token([$<|File], L, [space,{command,"include"}|Result], Gen, BsNl) -> - case token_include(File, []) of - {Rem, Str, nl} -> - Result1 = [{nl, L}, {sys_head_part,Str}, space, {command,"include"} |Result], - token(Rem, L+1,Result1, include, BsNl); - {Rem, Str} -> - Result1 = [{sys_head,Str}, space, {command,"include"} |Result], - token(Rem, L, Result1, Gen, BsNl) - end; -token([$<|File], L, [{command,"include"}|Result], Gen, BsNl) -> - case token_include(File, []) of - {Rem, Str, nl} -> - Result1 = [{nl, L}, {sys_head_part,Str}, space, {command,"include"} |Result], - token(Rem, L+1,Result1, include, BsNl); - {Rem, Str} -> - Result1 = [{sys_head,Str}, space, {command,"include"} |Result], - token(Rem, L, Result1, Gen, BsNl) - end; - - - - -%%--------------------------------------- -%% CR (just remove these) -%%--------------------------------------- -token([$\r|File], L, Result, Gen, BsNl) -> -% Bs = lists:duplicate(BsNl+1,{nl,L}), - token(File, L, Result, Gen, BsNl); %% Bs or BsNl? - -%%--------------------------------------- -%% Newline -%%--------------------------------------- -token([$\n|File], L, Result, _Gen, BsNl) -> - Bs = lists:duplicate(BsNl+1,{nl,L}), - token(File, L+1, Bs++Result, not_set, 0); - -token([$\\,$\n|File], L, Result, Gen, BsNl) -> - token(File, L, Result, Gen, BsNl+1); - -%%--------------------------------------- -%% Comments -%%--------------------------------------- -token([$/,$/|File], L, Result, not_set, BsNl) -> - Rem = skip_to_nl(File), - token(Rem, L+1,[{nl, L} | Result], not_set, BsNl); -token([$/,$/|File], L, Result, _Gen, BsNl) -> - Rem = skip_to_nl(File), - token(Rem, L+1,[{nl, L} | Result], not_set, BsNl); - -token([$/,$*|File], L, Result, not_set, BsNl) -> - case token_comment(File) of - {Rem, nl} -> - token(Rem, L+1, [{nl, L} | Result], not_set, BsNl); - Rem -> - token(Rem, L, Result, not_set, BsNl) - end; -token([$/,$*|File], L, Result, Gen, BsNl) -> - case token_comment(File) of - {Rem, nl} -> - token(Rem, L+1, [{nl, L}, space | Result], not_set, BsNl); - Rem -> - token(Rem, L, [space|Result], Gen, BsNl) - end; - -%%--------------------------------------- -%% Variable -%%--------------------------------------- -token([X|File], L, Result, Gen, BsNl) when ?is_upper(X) -> - GenNew = case Gen of not_set -> var; _ -> Gen end, - {Rem, Var} = tok_var(File, [X]), - token(Rem, L, [{var,Var}|Result], GenNew, BsNl); -token([X|File], L, Result, Gen, BsNl) when ?is_lower(X) -> - GenNew = case Gen of not_set -> var; _ -> Gen end, - {Rem, Var} = tok_var(File, [X]), - token(Rem, L, [{var,Var}|Result], GenNew, BsNl); -token([X|File], L, Result, Gen, BsNl) when ?is_underline(X) -> - GenNew = case Gen of not_set -> var; _ -> Gen end, - {Rem, Var} = tok_var(File, [X]), - token(Rem, L, [{var,Var}|Result], GenNew, BsNl); - -%%--------------------------------------- -%% Number -%%--------------------------------------- -token([X|File], L, Result, Gen, BsNl) when ?is_number(X) -> - GenNew = case Gen of not_set -> number; _ -> Gen end, - {Rem, Tokens} = tok_number(File, [X]), - token(Rem, L, [{number,Tokens}|Result], GenNew, BsNl); - -%%--------------------------------------- -%% Space -%%--------------------------------------- -token([X|File], L, [Y|Result], Gen, BsNl) when ?is_space(X) -> - case Y of - space -> - Rem = remove_leading_spaces(File), - token(Rem, L, [Y|Result], Gen, BsNl); - {nl,_,_} -> - Rem = remove_leading_spaces(File), - token(Rem, L, Result, Gen, BsNl); - _ -> - Rem = remove_leading_spaces(File), - token(Rem, L, [space, Y |Result], Gen, BsNl) - end; - -token([X|File], L, [Y|Result], Gen, BsNl) when ?is_tab(X) -> - case Y of - space -> - Rem = remove_leading_spaces(File), - token(Rem, L, [Y|Result], Gen, BsNl); - {nl,_,_} -> - Rem = remove_leading_spaces(File), - token(Rem, L, Result, Gen, BsNl); - _ -> - Rem = remove_leading_spaces(File), - token(Rem, L, [space, Y |Result], Gen, BsNl) - end; - -%%--------------------------------------- -%% Command -%%--------------------------------------- -token([$#|File], L, Result, not_set, BsNl) -> - {Rem, Command} = token_pp_com(File), - case catch list_to_integer(Command) of - {'EXIT', _} -> - token(Rem, L, [{command,Command}|Result], not_set, BsNl); - _Int -> - Result1 = [{number,Command}, {command,"line"}| Result], - token(Rem, L, Result1, not_set, BsNl) - end; - -%%--------------------------------------- -%% Char -%%--------------------------------------- -token([X|File], L, Result, Gen, BsNl) -> - GenNew = case Gen of not_set -> char; _ -> Gen end, - token(File, L, [{char,X}|Result], GenNew, BsNl). - - -%%================================================================== -%% Scan to the end of a token -%%================================================================== -%%--------------------------------------- -%% Number -%%--------------------------------------- -tok_number([], Str) -> - {[], lists:reverse(Str)}; -tok_number([X|File], Str) when ?is_upper(X) -> - tok_number(File, [X|Str]); -tok_number([X|File], Str) when ?is_lower(X) -> - tok_number(File, [X|Str]); -tok_number([X|File], Str) when ?is_underline(X) -> - tok_number(File, [X|Str]); -tok_number([X|File], Str) when ?is_number(X) -> - tok_number(File, [X|Str]); -tok_number(File, Str) -> - {File, lists:reverse(Str)}. - - -%%--------------------------------------- -%% Variable -%%--------------------------------------- -tok_var([], Str) -> - {[], lists:reverse(Str)}; -tok_var([X|File], Str) when ?is_upper(X) -> - tok_var(File, [X|Str]); -tok_var([X|File], Str) when ?is_lower(X) -> - tok_var(File, [X|Str]); -tok_var([X|File], Str) when ?is_underline(X) -> - tok_var(File, [X|Str]); -tok_var([X|File], Str) when ?is_number(X) -> - tok_var(File, [X|Str]); -tok_var(File, Str) -> - {File, lists:reverse(Str)}. - - -%%--------------------------------------- -%% Preprocessor command -%%--------------------------------------- -token_pp_com([X|File]) when ?is_upper(X) -> - tok_var(File, [X]); -token_pp_com([X|File]) when ?is_lower(X) -> - tok_var(File, [X]); -token_pp_com([X|File]) when ?is_underline(X) -> - tok_var(File, [X]); -token_pp_com([X|File]) when ?is_number(X) -> - tok_var(File, [X]); -token_pp_com(File) -> - Rem = remove_leading_spaces(File), - {Rem, "null"}. - - - -%%--------------------------------------- -%% Comment -%%--------------------------------------- -token_comment([]) -> - []; -token_comment([$*,$/|File]) -> - File; -token_comment([$\n|File]) -> - {[$/,$*|File], nl}; -token_comment([$\r,$\n|File]) -> - {[$/,$*|File], nl}; -token_comment([$\\,$\n|File]) -> - {[$/,$*|File], nl}; -%token_comment([$\\,$\n|File]) -> -% token_comment(File); -token_comment([_|File]) -> - token_comment(File). - - -%%--------------------------------------- -%% String -%%--------------------------------------- -token_string([], Str) -> - {[], lists:reverse(Str)}; -token_string([$"|File], Str) -> - {File, lists:reverse(Str)}; -token_string([$\n|File], Str) -> - {File, lists:reverse(Str), nl}; -token_string([$\r,$\n|File], Str) -> - {File, lists:reverse(Str), nl}; -token_string([$\\,$\n|File], Str) -> - token_string(File, Str); -token_string([X|File], Str) -> - token_string(File, [X|Str]). - - -%%--------------------------------------- -%% Include -%%--------------------------------------- -token_include([], Str) -> - {[], lists:reverse(Str)}; -token_include([$>|File], Str) -> - {File, lists:reverse(Str)}; -token_include([$\n|File], Str) -> - {File, lists:reverse(Str), nl}; -token_include([$\r,$\n|File], Str) -> - {File, lists:reverse(Str), nl}; -token_include([$\\,$\n|File], Str) -> - token_include(File, Str); -token_include([X|File], Str) -> - token_include(File, [X|Str]). - - - - -%%=================================================================================== -%% detokenise a list of tokens, until next newline -%% -%% Output: a string -%%=================================================================================== -detokenise(Tokens) -> - detokenise(Tokens, []). - -detokenise([], Result) -> - lists:flatten(Result); -detokenise([space], Result) -> - lists:flatten(Result); -detokenise([space_exp], Result) -> - lists:flatten(Result); -detokenise([space|Rem], Result) -> - detokenise(Rem, Result++[?space]); -detokenise([space_exp|Rem], Result) -> - detokenise(Rem, Result++[?space]); -detokenise([nl|Rem], Result) -> - detokenise(Rem, Result++[$\n]); -detokenise([{_, String}|Rem], Result) -> - detokenise(Rem, Result++[String]). - - -detokenise_pragma(Tokens) -> - detokenise_pragma(Tokens, []). - -detokenise_pragma([], Result) -> - lists:flatten(Result); -detokenise_pragma([space], Result) -> - lists:flatten(Result); -detokenise_pragma([space|Rem], Result) -> - detokenise_pragma(Rem, Result++[?space]); -detokenise_pragma([nl|Rem], Result) -> - detokenise_pragma(Rem, Result++[$\n]); -detokenise_pragma([{string, String}|Rem], Result) -> - detokenise_pragma(Rem, Result++[$"|String]++[$"]); -detokenise_pragma([{_, String}|Rem], Result) -> - detokenise_pragma(Rem, Result++[String]). - - - - - - - -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== -%% Expand macros. -%% -%% -%% Output: A text file -%% -%% Description: Expands all macros. All macro definitions are logged in a list 'Defs' -%% and all found errors and warnings are logged in a list 'Err' and 'War', -%% respectively. -%% -%% When a macro name is found in a source line it is expanded according -%% to the current 'Defs'-list. The macro must agree both to the name -%% and number of parameters, otherwise an error is reported. -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== - - -expand(List, FileName, IncDir, Flags) -> - %% Get all definitions from preprocessor commnads - %% and merge them on top of the file collected. - CLDefs = get_cmd_line_defs(Flags), - expand(List, [], [], CLDefs, [FileName], IncDir, #mio{}, check_all, [], [], 1, FileName). - -expand(List, Defs, Err, War, [FileName|IncFile], IncDir, Mio) -> - expand(List, [], [], Defs, [FileName|IncFile], IncDir, Mio, check_all, Err, War, 1, FileName). - -%%======================================================= -%% Main loop for the expansion -%%======================================================= -expand([], Out, _SelfRef, Defs, _IncFile, _IncDir, Mio, IfCou, Err, War, _L, _FN) -> -% io:format("~n ===============~n"), -% io:format(" definitions ~p~n",[lists:reverse(Defs)]), -% io:format(" found warnings ~p~n",[lists:reverse(War)]), -% io:format(" found errors ~p~n",[lists:reverse(Err)]), -% io:format(" ===============~n~n~n"), - {Out, Err, War, Defs, Mio, IfCou}; - -expand([{file_info, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, Str++Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN); - -%%--------------------------------------- -%% Searching for endif, -%% i.e skip all source lines until matching -%% end if is encountered -%%--------------------------------------- -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) - when Command == "ifdef" -> - {_Removed, Rem2, _Nl} = read_to_nl(Rem), - IfCou2 = {endif, Endif+1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN); - - -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) - when Command == "ifndef" -> - {_Removed, Rem2, _Nl} = read_to_nl(Rem), - IfCou2 = {endif, Endif+1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN); - - -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) - when Command == "if" -> - case pp_command(Command, Rem, Defs, IncDir, Mio, Err, War, L, FN) of - {{'if', true}, Rem2, Err2, War2, Nl} -> - IfCou2 = {endif, Endif+1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); -%% {{'if', false}, Rem2, Err2, War2, Nl} -> Not implemented yet - {{'if', error}, Rem2, Err2, War2, Nl} -> - IfCou2 = {endif, Endif, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN) - end; - -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) - when Command == "endif" -> - {_Removed, Rem2, Nl} = read_to_nl(Rem), - case Endif of - 1 -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L+Nl, FN); - _ -> - IfCou2 = {endif, Endif-1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L+Nl, FN) - end; - - -expand([{command,_Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> - {_Removed, Rem2, _Nl} = read_to_nl(Rem), - IfCou2 = {endif, Endif, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN); - -%% Solves a bug when spaces in front of hashmark ! -expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> - expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN); - -expand([{nl,_Nl} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> - expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN); - - -expand([_X | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> - {_Removed, Rem2, Nl} = read_to_nl(Rem), - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN); - - - - - -%%--------------------------------------- -%% Check all tokens -%%--------------------------------------- -expand([{nl, _N} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, [$\n | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L+1, FN); - -expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN); - -expand([space_exp | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN); - -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L, FN) -> - case pp_command(Command, Rem, Defs, IncDir, Mio, Err, War, L, FN) of - {define, Rem2, Defs2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN); - - {undef, Rem2, Defs2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN); - - {{include, ok}, FileName, FileCont, Rem2, Nl, Err2, War2} -> - {Out3, Defs3, Err3, War3, Mio2} = - run_include(FileName, FileCont, Out, Defs, Err2, War2, L+Nl, IncFile, IncDir, Mio), - Nls = [], - Out4 = Out3++Nls++Out, - expand(Rem2, Out4, SelfRef, Defs3, IncFile, IncDir, Mio2, check_all, Err3, War3, L+Nl, FN); - - {{include, error}, Rem2, Nl, Err2, War2} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN); - - {{include, skip}, Rem2} -> - Out2 = [$\n|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+1, FN); - - {{ifdef, true}, Rem2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - IfCou2 = {endif, 1, L}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); - {{ifdef, false}, Rem2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - Mio2 = update_mio(ifdef, Mio), - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); - - {{ifndef, true}, Rem2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - IfCou2 = {endif, 1, L}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); - {{ifndef, false}, Macro, Rem2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - Mio2 = update_mio({ifndef, Macro}, Mio), - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); - - {endif, Rem2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - Mio2 = update_mio(endif, Mio), - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); - - {{'if', true}, Rem2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - IfCou2 = {endif, 1, L}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); -%% {{'if', false}, Removed, Rem2, Nl} -> Not implemented at present - {{'if', error}, Rem2, Err2, War2, Nl} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - Mio2 = update_mio('if', Mio), - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); - - {'else', {_Removed, Rem2, Nl}} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - Err2 = {FN, L, "`else' command is not implemented at present"}, - Mio2 = update_mio('else', Mio), - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN); - - {'elif', {_Removed, Rem2, Nl}} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - Err2 = {FN, L, "`elif' command is not implemented at present"}, - Mio2 = update_mio('elif', Mio), - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN); - - {warning, {WarningText, Rem2, Nl}} -> - [FileName|_More] = IncFile, - War2 = {FileName, L, "warning: #warning "++detokenise(WarningText)}, - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, [War2|War], L+Nl, FN); - - {error, {ErrorText, Rem2, Nl}} -> - [FileName|_More] = IncFile, - Err2 = {FileName, L, detokenise(ErrorText)}, - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, [Err2|Err], War, L+Nl, FN); - - {{line, ok}, {_Removed, Rem2, Nl}, L2, FN2, LineText} -> - Out2 = lists:duplicate(Nl,$\n)++LineText++Out, - [_X|IF] = IncFile, - IncFile2 = [FN2|IF], - expand(Rem2, Out2, SelfRef, Defs, IncFile2, IncDir, update_mio(Mio), check_all, Err, War, L2, FN2); - {{line, error}, {_Removed, Rem2, Nl}, Err2} -> - Out2 = lists:duplicate(Nl,$\n) ++ Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, [Err2|Err], War, L+Nl, FN); - - hash_mark -> - expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L, FN); - - {pragma, Rem2, Nl, Text} -> - Out2 = lists:duplicate(Nl,$\n)++Text++Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+Nl, FN); - - {ident, Rem2, Nl, Text} -> - Out2 = lists:duplicate(Nl,$\n)++Text++Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+Nl, FN); - - {not_recognised, {Removed, Rem2, Nl}} -> - Text = lists:reverse([$#|Command]), - RemovedS = lists:reverse([?space|detokenise(Removed)]), - Out2 = [$\n|RemovedS]++Text++Out, - Mio2 = update_mio(Mio), - case Command of - [X|_T] when ?is_upper(X) -> - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN); - [X|_T] when ?is_lower(X) -> - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN); - [X|_T] when ?is_underline(X) -> - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN); - _ -> - Err2 = {FN, L, "invalid preprocessing directive name"}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN) - end; - - Else -> -% io:format(" %%%%Else%%%%%% ~p~n",[Else]), - exit(Else) - end; - - -expand([{var, "__LINE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - LL = io_lib:format("~p",[L]), - expand(Rem, [LL | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{var, "__FILE__"}|Rem], Out, SelfRef, Defs, IncFile, Mio, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [$",FN,$" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{var, "__DATE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - {{Y,M,D},{_H,_Mi,_S}} = calendar:universal_time(), - Date = io_lib:format("\"~s ~p ~p\"",[month(M),D,Y]), - expand(Rem, [Date | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{var, "__TIME__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - {{_Y,_M,_D},{H,Mi,S}} = calendar:universal_time(), - HS = if H < 10 -> "0"++integer_to_list(H); - true -> integer_to_list(H) - end, - MiS = if Mi < 10 -> "0"++integer_to_list(Mi); - true -> integer_to_list(Mi) - end, - SS = if S < 10 -> "0"++integer_to_list(S); - true -> integer_to_list(S) - end, - Time = io_lib:format("\"~s:~s:~s\"",[HS,MiS,SS]), - expand(Rem, [Time | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{var, "__INCLUDE_LEVEL__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - IL = io_lib:format("~p",[length(IncFile)-1]), - expand(Rem, [IL | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{var, "__BASE_FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - [BF|_T] = lists:reverse(IncFile), - expand(Rem, [$",BF,$" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{var, Var} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - {Out2, Err2, War2, Rem2, SelfRef2} = - source_line(Var, Rem, SelfRef, Defs, Err, War, L, FN), - expand(Rem2, [Out2 | Out], SelfRef2, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err2, War2, L, FN); - -expand([{char, Char} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, [Char | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{number, Number} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, [Number | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{expanded, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, [Str | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{self_ref, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - SelfRef2 = lists:delete(Str,SelfRef), - expand(Rem, Out, SelfRef2, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{string, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - expand(Rem, [$", Str, $" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); - -expand([{string_part, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> - {Str2, Rem2, Nl} = expand_string_part([$"|Str], Rem), - expand(Rem2, [Str2| Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L+Nl, FN). - - - - - - - - -%%======================================================================== -%% Expand a line starting as a partial string -%%======================================================================== -expand_string_part(Str, File) -> - expand_string_part(File, Str, 0). - -expand_string_part([{string, Str_part} | Rem], Str, Nl) -> - {Str++Str_part++[$"], Rem, Nl}; -expand_string_part([space | Rem], Str, Nl) -> - expand_string_part(Rem, Str, Nl); -expand_string_part([nl| Rem], Str, Nl) -> - expand_string_part(Rem, Str++[$\n], Nl); -expand_string_part([{string_part, Str_part} | Rem], Str, Nl) -> - expand_string_part(Rem, Str++Str_part, Nl). - - - - - -%%======================================================================== -%% Parse and integrate command line macro directives -%% At this momment, only -D and -U are supported (gcc like) -%%======================================================================== - - -%% Collect all command line macro definitions -get_cmd_line_defs(Flags) -> - Adjusted = parse_cmd_line(Flags,[]), - - {_Out, _Err, _War, Defs, _IfCou, _Mio} = - expand(tokenise(Adjusted,""), - [], - [], - [], - [], - [], - #mio{}, - check_all, - [], - [], - 1, - ""), - Defs. - -%% Parse command line macros -parse_cmd_line([],Found) -> - lists:flatten(lists:reverse(Found)); - -parse_cmd_line([45,68|Rest],Found) -> - {Collected,RestCmds} = collect_define(Rest,[]), - parse_cmd_line(RestCmds,[Collected|Found]); - -parse_cmd_line([45,85|Rest],Found) -> - {Collected,RestCmds} = collect_undefine(Rest,[]), - parse_cmd_line(RestCmds,[Collected|Found]); - -parse_cmd_line([_|Rest],Found) -> - parse_cmd_line(Rest,Found). - - -%% Collect defines and translate them -%% into a text format -collect_define([],Found) -> - { "#define "++lists:reverse(Found)++"\n", [] }; -collect_define([32|Rest],Found) -> - { "#define "++lists:reverse(Found)++"\n", Rest }; -collect_define([61|Rest],[]) -> - { "", Rest }; -collect_define([61|Rest],Found) -> - collect_define(Rest,[32|Found]); -collect_define([C|Rest],Found) -> - collect_define(Rest,[C|Found]). - - -%% Collect undefines and translate them -%% into a text format -collect_undefine([],Found) -> - { "#undef "++lists:reverse(Found)++"\n", [] }; -collect_undefine([32|Rest],Found) -> - { "#undef "++lists:reverse(Found)++"\n", Rest }; -collect_undefine([C|Rest],Found) -> - collect_undefine(Rest,[C|Found]). - - - - - - - - - - - - -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== -%% Read a preprocessor command -%% -%% -%% Output: Depending of the command, typically = {Command, Rem, Err, War, Nl} -%% -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== - -pp_command(Command, [space|File], Defs, IncDir, Mio, Err, War, L, FN) -> - pp_command(Command, File, Defs, IncDir, Mio, Err, War, L, FN); - -pp_command(Command, File, Defs, IncDir, Mio, Err, War, L, FN) -> - - case Command of - %%---------------------------------------- - %% #define - %%---------------------------------------- - "define" -> - case define(File, Err, War, L, FN) of - {error, Rem, Err2, War2, Nl} -> - {define, Rem, Defs, Err2, War2, Nl}; - {warning, Rem, Name, No_of_para, Parameters, Macro, Err2, War2, Nl} -> - case is_define_ok(Name, No_of_para, Parameters, Macro, Defs) of - {yes, Defs2} -> - {define, Rem, Defs2, Err2, War2, Nl}; - {no, Defs2} -> - Text = lists:flatten(io_lib:format("`~s' redefined",[Name])), - {define, Rem, Defs2, Err2, [{FN, L, Text}|War2], Nl}; - {error, Text, Defs2} -> - {define, Rem, Defs2, [{FN, L, Text}|Err2], War2, Nl} - end; - {ok, Rem, Name, No_of_para, Parameters, Macro, Err2, War2, Nl} -> - case is_define_ok(Name, No_of_para, Parameters, Macro, Defs) of - {yes, Defs2} -> - {define, Rem, Defs2, Err2, War2, Nl}; - {no, Defs2} -> - Text = lists:flatten(io_lib:format("`~s' redefined",[Name])), - {define, Rem, Defs2, Err2, [{FN, L, Text}|War2], Nl}; - {error, Text, Defs2} -> - {define, Rem, Defs2, [{FN, L, Text}|Err2], War2, Nl} - end - end; - - %%---------------------------------------- - %% #undef - %%---------------------------------------- - "undef" -> - case undef(File, Err, War, L, FN) of - {error, Rem, Err2, War2, Nl} -> - {undef, Rem, Defs, Err2, War2, Nl}; - {ok, Rem, Name, Err2, War2, Nl} -> - Defs2 = lists:keydelete(Name, 1, Defs), - {undef, Rem, Defs2, Err2, War2, Nl} - end; - - %%---------------------------------------- - %% #include - %%---------------------------------------- - "include" -> - case include(File, IncDir, Mio) of - {error, Rem, Nl, Err2} -> - {{include, error}, Rem, Nl, [{FN, L, Err2}|Err], War}; - {error, Rem, Nl, Err2, NameNl} -> - {{include, error}, Rem, Nl, [{FN, L+ NameNl, Err2}|Err], War}; - {ok, FileNamePath, FileCont, Rem, Nl} -> - {{include, ok}, FileNamePath, FileCont, Rem, Nl, Err, War}; - {skip, Rem} -> - {{include, skip}, Rem} - end; - - %%---------------------------------------- - %% #ifdef - %%---------------------------------------- - "ifdef" -> - case define(File, Err, War, L, FN) of - {error, Rem, Err2, War2, Nl} -> - {{ifdef, false}, Rem, Defs, Err2, War2, Nl}; - {warning, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> - case is_defined_before(Name, No_of_para, Defs) of - yes -> - {{ifdef, false}, Rem, Err2, War2, Nl}; - no -> - {{ifdef, true}, Rem, Err2, War2, Nl} - end; - {ok, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> - case is_defined_before(Name, No_of_para, Defs) of - yes -> - {{ifdef, false}, Rem, Err2, War2, Nl}; - no -> - {{ifdef, true}, Rem, Err2, War2, Nl} - end - end; - - - - %%---------------------------------------- - %% #ifndef - %%---------------------------------------- - "ifndef" -> - case define(File, Err, War, L, FN) of - {error, Rem, Err2, War2, Nl} -> - {{ifndef, false}, Rem, Defs, Err2, War2, Nl}; - {warning, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> - case is_defined_before(Name, No_of_para, Defs) of - yes -> - {{ifndef, true}, Rem, Err2, War2, Nl}; - no -> - {{ifndef, false}, Name, Rem, Err2, War2, Nl} - end; - {ok, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> - case is_defined_before(Name, No_of_para, Defs) of - yes -> - {{ifndef, true}, Rem, Err2, War2, Nl}; - no -> - {{ifndef, false}, Name, Rem, Err2, War2, Nl} - end - end; - - - %%---------------------------------------- - %% #endif - %%---------------------------------------- - "endif" -> - {Removed, Rem, Nl} = read_to_nl(File), - case Removed of - [] -> - {endif, Rem, Err, War, 1}; - _ -> - Text = "ignoring the tail of the line", - {ok, Rem, Err, [{FN, L, Text}|War], Nl} - end; - - - %%---------------------------------------- - %% #if - %%---------------------------------------- - "if" -> - case if_zero(File, Err, War, L, FN) of - {error, Rem2, _Removed, Nl} -> - Err2 = {FN, L, "only '#if 0' is implemented at present"}, - {{'if', error}, Rem2, [Err2 | Err], War, Nl}; - {ok, Rem2, 0, _Removed, Nl} -> - {{'if', true}, Rem2, Err, War, Nl}; - {ok, Rem2, _Num, _Removed, Nl} -> - Err2 = {FN, L, "only '#if 0' is implemented at present"}, - {{'if', error}, Rem2, [Err2 | Err], War, Nl} - end; - - %%---------------------------------------- - %% #else - %%---------------------------------------- - "else" -> - {'else', read_to_nl(File)}; - - %%---------------------------------------- - %% #elif - %%---------------------------------------- - "elif" -> - {'elif', read_to_nl(File)}; - - %%---------------------------------------- - %% #pragma - %%---------------------------------------- - "pragma" -> - {Removed, Rem, Nl} = read_to_nl(File), - {pragma, Rem, Nl, lists:reverse("#pragma " ++ detokenise_pragma(Removed))}; - - %%---------------------------------------- - %% #ident - %%---------------------------------------- - "ident" -> - {Removed, Rem, Nl} = read_to_nl(File), - {ident, Rem, Nl, lists:reverse("#ident " ++ detokenise_pragma(Removed))}; - - %%---------------------------------------- - %% #warning - %%---------------------------------------- - "warning" -> - {warning, read_to_nl(File)}; - - %%---------------------------------------- - %% #error - %%---------------------------------------- - "error" -> - {error, read_to_nl(File)}; - - %%---------------------------------------- - %% #line - %%---------------------------------------- - "line" -> - line(File, L, FN); - - %%---------------------------------------- - %% # - %%---------------------------------------- - "null" -> - hash_mark; - - %%---------------------------------------- - %% not recognised preprocessor commands - %%---------------------------------------- - _Else -> - {not_recognised, read_to_nl(File)} - end. - - - - -%%=============================================================== -%%=============================================================== -%%=============================================================== -%% if -%% -%% Only #if 0 is implemented at the time to be able to use if -%% to comment some code parts. -%%=============================================================== -%%=============================================================== -%%=============================================================== - -if_zero(File, _Err, _War, _L, _FN) -> - case if_zero(File) of - {ok, Remain, Num, Removed, Nl} -> - case catch list_to_integer(Num) of - {'EXIT', _} -> - {Removed2, Rem2, Nl2} = read_to_nl(File), - {error, Rem2, Removed2, Nl2}; - Int -> - {ok, Remain, Int, Removed, Nl} - end; - E -> - E - end. - -if_zero([{number,Num}]) -> - {ok, [], Num, [], 0}; -if_zero([{number,Num}, space]) -> - {ok, [], Num, [], 0}; -if_zero([{number,Num} | Rem]) -> - {Removed, Rem2, Nl} = read_to_nl(Rem), - {ok, Rem2, Num, Removed, Nl}; -%if_zero([{number,Num}, {nl,_X} | Rem]) -> -% {ok, Rem, Num, [], 1}; -if_zero(Rem) -> - {Removed, Rem2, Nl} = read_to_nl(Rem), - {error, Rem2, Removed, Nl}. - - - -%%=============================================================== -%%=============================================================== -%%=============================================================== -%% Define macro -%% -%% Check the syntax of the macro, extract the parameters if any. -%% If valid macro it is added to the Defs-list. -%% If a macro is redefined, a warning will be given, the latest -%% definition is always the valid one. -%%=============================================================== -%%=============================================================== -%%=============================================================== - -define(File, Err, War, L, FN) -> - case define_name(File) of - {ok, Rem, Name, No_of_para, Parameters, Macro, Nl} -> - {ok, Rem, Name, No_of_para, Parameters, Macro, Err, War, Nl}; - {{warning,no_space}, Rem, Name, No_of_para, Parameters, Macro, Nl} -> - Text = lists:flatten(io_lib:format("missing white space after `#define ~s'",[Name])), - {warning, Rem, Name, No_of_para, Parameters, Macro, Err, [{FN, L, Text}|War], Nl}; - {error, invalid_name, Nl} -> - Text = "invalid macro name", - {_Removed, Rem, Nl2} = read_to_nl(File), - {error, Rem, [{FN, L, Text}|Err], War, Nl+Nl2}; - {error, invalid_name, Name, Nl} -> - Text = lists:flatten(io_lib:format("invalid macro name `~s'",[Name])), - {_Removed, Rem, Nl2} = read_to_nl(File), - {error, Rem, [{FN, L, Text}|Err], War, Nl+Nl2}; - {error, illegal_arg} -> - {Removed, Rem, Nl} = read_to_nl(File), - RemovedS = detokenise(Removed), - Text = lists:flatten(io_lib:format("Invalid argument list ~s",[RemovedS])), - {error, Rem, [{FN, L, Text}|Err], War, Nl} - end. - - - -%%=========================================================== -%% Check if valid macro -%%=========================================================== -define_name([]) -> - {warning, no_macro}; -define_name([space]) -> - {warning, no_macro}; -%% Macro with parameters -define_name([{var,Name},{char,$(}|Rem]) -> - case read_para([{char,$(}|Rem]) of - {ok, Rem2, Para, NoOfPara} -> - {Removed, Rem3, _Nl} = read_to_nl(Rem2), - {ok, Rem3, Name, NoOfPara, Para, Removed, 1}; - Error -> - Error - end; -%% Macro without parameters -define_name([{var,Name}]) -> - {ok, [], Name, 0, [], [], 0}; -define_name([{var,Name}, space | Rem]) -> - {Removed, Rem2, Nl} = read_to_nl(Rem), - {ok, Rem2, Name, 0, [], Removed, Nl}; -define_name([{var,Name}, {nl,_X} | Rem]) -> - {ok, Rem, Name, 0, [], [], 1}; -define_name([{var,Name} | Rem]) -> - {Removed, Rem2, Nl} = read_to_nl(Rem), - {{warning,no_space}, Rem2, Name, 0, [], Removed, Nl}; -%% Invalid macro name -define_name([{number, Name} | _Rem]) -> - {error, invalid_name, Name, 0}; -define_name(_Rem) -> - {error, invalid_name, 0}. - - - - - - - -%%=============================================================== -%%=============================================================== -%%=============================================================== -%% Undefine macro -%% -%% If it is a valid undef command the macro name will be deleted -%% from the Defs-list -%%=============================================================== -%%=============================================================== -%%=============================================================== - -undef(File, Err, War, L, FN) -> - case undef(File) of - {ok, Rem, Name, Nl} -> - {ok, Rem, Name, Err, War, Nl}; - {warning, Rem, Name, Nl} -> - Text = "ignoring the tail of the line", - {ok, Rem, Name, Err, [{FN, L, Text}|War], Nl}; - {error, invalid_name} -> - Text = "invalid macro name", - {_Removed, Rem, Nl} = read_to_nl(File), - {error, Rem, [{FN, L, Text}|Err], War, Nl}; - {error, invalid_name, Name} -> - Text = lists:flatten(io_lib:format("invalid macro name `~s'",[Name])), - {_Removed, Rem, Nl} = read_to_nl(File), - {error, Rem, [{FN, L, Text}|Err], War, Nl} - end. - -%%------------------------------------------------- -%% Check if valid macro name -%%------------------------------------------------- -undef([]) -> - {error, invalid_name, []}; -%% Valid name -undef([{var,Name}]) -> - {ok, [], Name, 0}; -undef([{var,Name}, {nl,_X} | Rem]) -> - {ok, Rem, Name, 1}; -undef([{var,Name}, space, {nl,_X} | Rem]) -> - {ok, Rem, Name, 1}; -undef([{var,Name} | Rem]) -> - {_Removed, Rem2, Nl} = read_to_nl(Rem), - {warning, Rem2, Name, Nl}; -%% Invalid macro name -undef([{number, Name} | _Rem]) -> - {error, invalid_name, Name}; -undef(_Rem) -> - {error, invalid_name}. - - - - - - -%%=============================================================== -%%=============================================================== -%%=============================================================== -%% Include macro -%% -%% Read the included file -%%=============================================================== -%%=============================================================== -%%=============================================================== - -include(File, IncDir, Mio) -> - case include2(File) of - {ok, FileName, Rem, Nl, FileType} -> - Result = read_inc_file(FileName, IncDir, Mio), - case {Result, Nl, FileType} of - {{ok, FileNamePath, FileCont}, _, _} -> - {ok, FileNamePath, FileCont, Rem, Nl}; - {skip, _, _} -> - {skip, Rem}; - {{error, Text}, _, own_file} -> - NameNl = count_nl(FileName,0), - Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), - {error, Rem, Nl, Error, NameNl}; - {{error, Text}, 1, sys_file} -> - NameNl = count_nl(FileName,0), - Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), - {error, Rem, Nl, Error, NameNl}; - {{error, _Text}, _, sys_file} -> - {error, Rem, Nl, "`#include' expects \"FILENAME\" or <FILENAME>"} - end; - {error, {_Removed, Rem, Nl}} -> - {error, Rem, Nl, "`#include' expects \#FILENAME\" or <FILENAME>"} - end. - - - -count_nl([],Nl) -> - Nl; -count_nl([$\n|T],Nl) -> - count_nl(T,Nl+1); -count_nl([_H|T],Nl) -> - count_nl(T,Nl). - -%%================================================= -%% Extract the file name from the token list -%%================================================= -include2([space|Rem]) -> - include2(Rem); - -include2([{string, FileName}]) -> - {ok, FileName, [], 1, own_file}; -include2([{string, FileName}, space]) -> - {ok, FileName, [], 1, own_file}; -include2([{string, FileName}, {nl, _X} | Rem]) -> - {ok, FileName, Rem, 1, own_file}; -include2([{string, FileName}, space, {nl, _X} | Rem]) -> - {ok, FileName, Rem, 1, own_file}; -include2([{string, _FileName}, _No_nl | Rem]) -> - {error, read_to_nl(Rem)}; -include2([{string_part, File_part}, {nl, _X} | Rem]) -> - case include_read_string_file_name(File_part++[$\n], Rem, 1) of - {ok, FileName, Rem2, Nl} -> - {ok, FileName, Rem2, Nl, own_file}; - error -> - {error, read_to_nl([{string_part,File_part} | Rem])} - end; -include2([{sys_head, FileName}]) -> - {ok, FileName, [], 1, sys_file}; -include2([{sys_head, FileName}, space]) -> - {ok, FileName, [], 1, sys_file}; -include2([{sys_head, FileName}, {nl, _X} | Rem]) -> - {ok, FileName, Rem, 1, sys_file}; -include2([{sys_head, FileName}, space, {nl, _X} | Rem]) -> - {ok, FileName, Rem, 1, sys_file}; -include2([{sys_head, _FileName}, _No_nl | Rem]) -> - {error, read_to_nl(Rem)}; -include2([{sys_head_part ,File_part}, {nl, _X} | Rem]) -> - case include_read_sys_file_name(File_part++[$\n], Rem, 1) of - {ok, FileName, Rem2, Nl} -> - {ok, FileName, Rem2, Nl, sys_file}; - error -> - {error, read_to_nl([{sys_head_part, File_part} | Rem])} - end; -include2(Rem) -> - {error, read_to_nl(Rem)}. - - - -%%------------------------------------------------- -%% File name framed by " " -%%------------------------------------------------- -include_read_string_file_name(File, [{string, File_part}, {nl,_X} | Rem], Nl) -> - {ok, File++File_part, Rem, Nl+1}; -include_read_string_file_name(File, [{string_part, File_part}, {nl,_X} | Rem], Nl) -> - include_read_string_file_name(File++File_part++[$\n], Rem, Nl+1); -include_read_string_file_name(_File, _X, _Nl) -> - error. - -%%------------------------------------------------- -%% File name framed by < > -%%------------------------------------------------- -include_read_sys_file_name(File, [{sys_head, File_part}, {nl,_X} | Rem], Nl) -> - {ok, File++File_part, Rem, Nl+1}; -include_read_sys_file_name(File, [{sys_head_part, File_part}, {nl,_X} | Rem], Nl) -> - include_read_sys_file_name(File++File_part++[$\n], Rem, Nl+1); -include_read_sys_file_name(_File, _X, _Nl) -> - error. - - - - - - - -%%=============================================================== -%%=============================================================== -%%=============================================================== -%% Line macro -%% -%% The line macro may redefine both the current line number and -%% the current file name: #line ' new_line_nr' 'new_file_name' -%%=============================================================== -%%=============================================================== -%%=============================================================== - -line(File, L, FN) -> - line(File, L, FN, not_defined, not_defined). - - - -line([], L, FN, _Line, _File) -> - {{line, error}, {[],[],0}, {FN,L,"invalid format `#line' directive"}}; - -line([space|Rem], L, FN, Line, File) -> - line(Rem, L, FN, Line, File); - -%%------------------------------ -%% Line number expected -%%------------------------------ -line([{number,Number}|Rem], L, FN, not_defined, File) -> - case catch list_to_integer(Number) of - {'EXIT', _} -> - {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}}; - Int -> - line(Rem, L, FN, Int, File) - end; -line(Rem, L, FN, not_defined, _File) -> - {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}}; - -%%------------------------------ -%% File name or newline expected -%%------------------------------ -line([{nl, _NL}|Rem], _L, FN, Line, not_defined) -> - {{line, ok}, {[],Rem,1}, Line, FN, io_lib:format("~n~p ~p #",[FN, Line-1])}; -line([{string,NewFN}|Rem], _L, _FN, Line, not_defined) -> - {{line, ok}, read_to_nl(Rem), Line, NewFN, io_lib:format("~n~p ~p #",[NewFN, Line-1])}; -line(Rem, L, FN, _Line, _File) -> - {{line, error}, read_to_nl(Rem), {FN,L,"invalid format `#line' directive"}}. - - - - -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== -%% Source line -%% -%% -%% Output: {Str, Err, War, Rem, SelfRef} -%% -%% Description: The input source line is searched for macros. If a macro is found it -%% is expanded. The result of an expansion is rescanned for more macros. -%% To prevent infinite loops if the macro is self referring -%% an extra token is put into the Rem list. The variable SelfRef -%% contains all the macros which are inhibited to be expanded. -%% A special specae token is also inserted to prevent not wanted -%% concatinations if one of the variables to be concatinated is expanded. -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== - -source_line(Str, Rem, SelfRef, Defs, Err, War, L, FN) -> - {Rem2, Para, No_of_para} = case read_para(Rem) of - {ok, RemT, ParaT, No_of_paraT} -> - {RemT, ParaT, No_of_paraT}; - {error, illegal_arg} -> - {[], [], 0} - end, - - - %%------------------------------------------------- - %% Check if a valid macro - %%------------------------------------------------- - case lists:keysearch(Str, 1, Defs) of - %% a macro without parameters - {value, {Str, 0, _MacroPara, Macro}} -> - case lists:member(Str, SelfRef) of - true -> - {[Str], Err, War, Rem, SelfRef}; - false -> - ExpandedRes2 = sl_mark_expanded(Macro, Str), - {[], Err, War, ExpandedRes2 ++ [{self_ref,Str}|Rem], [Str|SelfRef]} - end; - - %% a macro with parameters - {value, {Str, N, _MacroPara, Macro}} when N == No_of_para -> - case lists:member(Str, SelfRef) of - true -> - {[Str], Err, War, Rem, SelfRef}; - false -> - ExpandedRes = sl_macro_expand(Macro, Para, Defs), - ExpandedRes2 = sl_mark_expanded(ExpandedRes, Str), - {[], Err, War, ExpandedRes2 ++ [{self_ref,Str}|Rem2], [Str|SelfRef]} - end; - - %% a variable, because it doesn't have any parameters - {value, {Str, _N, _MacroPara, _Macro}} when No_of_para == 0 -> - {Str, Err, War, Rem, SelfRef}; - - %% illegal no of parameters - {value, {Str, N, _MacroPara, _Macro}} when No_of_para < N -> - Text = io_lib:format(" macro `~s' used with just ~p arg",[Str,No_of_para]), - Err2 = {FN, L, lists:flatten(Text)}, - {Str, [Err2|Err], War, Rem, SelfRef}; - {value, {Str, _N, _MacroPara, _Macro}} -> - Text = io_lib:format(" macro `~s' used with too many (~p) args",[Str,No_of_para]), - Err2 = {FN, L, lists:flatten(Text)}, - {Str, [Err2|Err], War, Rem, SelfRef}; - - %% no macro - false -> - {Str, Err, War, Rem, SelfRef} - end. - - - - - -%%================================================= -%% Expand a macro -%%================================================= -sl_macro_expand(Macro, Para, Defs) -> - sl_macro_expand(Macro, Para, Defs, []). - - -%%................... -%% End -%%................... -sl_macro_expand([], _Para, _Defs, Res) -> - lists:reverse(Res); - -%%................... -%% Concatination -%%................... -%% para ## para -sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> - Exp = sl_para_para({para, N},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); -%% para## para -sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> - Exp = sl_para_para({para, N},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); -%% para ##para -sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> - Exp = sl_para_para({para, N},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); -%% para##para -sl_macro_expand([{para, N}, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> - Exp = sl_para_para({para, N},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); - -%% para ## var -sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, {var, Var}|T], Para, Defs, Res) -> - Exp = sl_para_var({para, N}, {var, Var}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); -%% para## var -sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, {var, Var} | T], Para, Defs, Res) -> - [{var, VarN}] = lists:nth(N,Para), - sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]); -%% para ##var -sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, {var, Var} | T], Para, Defs, Res) -> - [{var, VarN}] = lists:nth(N,Para), - sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]); -%% para##var -sl_macro_expand([{para, N}, {char,$#}, {char,$#}, {var, Var} | T], Para, Defs, Res) -> - [{var, VarN}] = lists:nth(N,Para), - sl_macro_expand(T, Para, Defs, [{expanded,Var},{expanded,VarN}, space |Res]); - -%% var ## para -sl_macro_expand([{var, Var}, space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> - Exp = sl_var_para({var, Var},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); -%% var## para -sl_macro_expand([{var, Var}, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, Res) -> - Exp = sl_var_para({var, Var},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); -%% var ##para -sl_macro_expand([{var, Var}, space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> - Exp = sl_var_para({var, Var},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); -%% var##para -sl_macro_expand([{var, Var}, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, Res) -> - Exp = sl_var_para({var, Var},{para, M}, Para), - sl_macro_expand(Exp++T, Para, Defs, [space |Res]); - -%% expanded ## para -sl_macro_expand([space, {char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> - [{var, VarM}] = lists:nth(M,Para), - sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); -%% expanded## para -sl_macro_expand([{char,$#}, {char,$#}, space, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> - [{var, VarM}] = lists:nth(M,Para), - sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); -%% expanded ##para -sl_macro_expand([space, {char,$#}, {char,$#}, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> - [{var, VarM}] = lists:nth(M,Para), - sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); -%% expanded##para -sl_macro_expand([{char,$#}, {char,$#}, {para,M} | T], Para, Defs, [{expanded, Var}|Res]) -> - [{var, VarM}] = lists:nth(M,Para), - sl_macro_expand(T, Para, Defs, [{expanded,VarM},{expanded,Var} |Res]); - -%% para ## ? -sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, space, X | T], Para, Defs, Res) -> - Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), - sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); -%% para## ? -sl_macro_expand([{para, N}, {char,$#}, {char,$#}, space, X | T], Para, Defs, Res) -> - Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), - sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); -%% para ##? -sl_macro_expand([{para, N}, space, {char,$#}, {char,$#}, X | T], Para, Defs, Res) -> - Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), - sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); -%% para##? -sl_macro_expand([{para, N}, {char,$#}, {char,$#}, X | T], Para, Defs, Res) -> - Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), - sl_macro_expand([X, space|T], Para, Defs, lists:flatten([Reexp, space|Res])); - -sl_macro_expand([{char,$#}, {char,$#}, space |T], Para, Defs, [space|Res]) -> - sl_macro_expand(T, Para, Defs, Res); -sl_macro_expand([{char,$#}, {char,$#} |T], Para, Defs, [space|Res]) -> - sl_macro_expand(T, Para, Defs, Res); -sl_macro_expand([{char,$#}, {char,$#}, space |T], Para, Defs, Res) -> - sl_macro_expand(T, Para, Defs, Res); -sl_macro_expand([{char,$#}, {char,$#} |T], Para, Defs, Res) -> - sl_macro_expand(T, Para, Defs, Res); - -%%................... -%% Stringification -%%................... -sl_macro_expand([{char,$#}, {para, N}|T], Para, Defs, Res) -> - Nth = lists:nth(N,Para), - Tokens = detokenise(Nth), - sl_macro_expand(T, Para, Defs, [{string,Tokens}|Res]); -sl_macro_expand([{char,$#}, space, {para, N}|T], Para, Defs, Res) -> - Nth = lists:nth(N,Para), - Tokens = detokenise(Nth), - sl_macro_expand(T, Para, Defs, [{string,Tokens}|Res]); - -%%................... -%% A parameter -%%................... -sl_macro_expand([{para, N}|T], Para, Defs, Res) -> - Reexp = sl_macro_reexpand(lists:nth(N,Para), Defs, []), - sl_macro_expand(T, Para, Defs, lists:flatten([Reexp|Res])); - -%%................... -%% No parameter -%%................... -sl_macro_expand([H|T], Para, Defs, Res) -> - sl_macro_expand(T, Para, Defs, [H|Res]). - - - -%%------------------------------------------------- -%% Expand parameters -%%------------------------------------------------- -sl_para_para({para, N}, {para, M}, Para) -> - case sl_para_1st(lists:nth(N,Para)) of - {ok, Para1st} -> - Para1st ++ sl_para_2nd(lists:nth(M,Para)); - {exp, Para1st} -> - Para1st ++ sl_para_2nd(lists:nth(M,Para)) ++ [space_exp]; - {space, Para1st} -> - Para1st ++ [space_exp | sl_para_2nd(lists:nth(M,Para))] - end. - - -sl_var_para(Var, {para, M}, Para) -> - [Var|sl_para_2nd(lists:nth(M,Para))]. - - -sl_para_var({para, N}, Var, Para) -> - case sl_para_1st(lists:nth(N,Para)) of - {ok, Para1st} -> - Para1st ++ [Var]; - {exp, Para1st} -> - Para1st ++ [Var | space_exp]; - {space, Para1st} -> - Para1st ++ [space_exp | Var] - end. - - -sl_para_1st([{var, Var}]) -> - {ok,[{expanded,Var}]}; -sl_para_1st([{var, Var}, space]) -> - {ok,[{expanded,Var}]}; -sl_para_1st([{var, Var}, space_exp]) -> - {exp, [{expanded,Var}]}; -sl_para_1st(L) -> - {space, L}. - -sl_para_2nd([{var, Var}]) -> - [{expanded,Var}]; -sl_para_2nd([{var, Var}, space_exp]) -> - [{expanded,Var}]; -sl_para_2nd([space, {var, Var}]) -> - [{expanded,Var}]; -sl_para_2nd([space_exp, {var, Var}]) -> - [{expanded,Var}]; -sl_para_2nd(L) -> - L++[space]. - - - -%%------------------------------------------------- -%% Check if the expansion is a valid macro, -%% do not reexpand if concatination -%%------------------------------------------------- -sl_macro_reexpand([], _Defs, Result) -> - Result; -sl_macro_reexpand([{var,Var}|Rem], Defs, Result) -> - case lists:keysearch(Var, 1, Defs) of - {value, {Var, 0, _MacroPara, Macro}} -> - Rem2 = case Rem of - [space | RemT] -> - [space_exp | RemT]; - _ -> - [space_exp | Rem] - end, - sl_macro_reexpand(Macro++Rem2, Defs, Result); - _ -> - sl_macro_reexpand(Rem, Defs, [{var,Var}|Result]) - end; -sl_macro_reexpand([H|Rem], Defs, Result) -> - sl_macro_reexpand(Rem, Defs, [H|Result]). - - - -%%------------------------------------------------- -%% Self referring macros are marked not be reexpanded -%%------------------------------------------------- -sl_mark_expanded(QQ, Str) -> - sl_mark_expanded(QQ, Str, []). - -sl_mark_expanded([], _Str, Res) -> - lists:reverse(Res); -sl_mark_expanded([H|T], Str, Res) -> - case H of - {_,Str} -> - sl_mark_expanded(T, Str, [{expanded, Str}|Res]); - _ -> - sl_mark_expanded(T, Str, [H|Res]) - end. - - - - - - - - - -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== -%% Misceleaneous functions -%%====================================================================================== -%%====================================================================================== -%%====================================================================================== - - -%%=============================================================== -%% Check the Flags for include directories -%%=============================================================== -include_dir(Flags) when is_list(Flags)-> - include_dir(Flags,[]); -include_dir(_Flags) -> - []. - -include_dir(Flags,IncDirs) -> - case string:str(Flags,"-I") of - 0 -> - lists:reverse(IncDirs); - X -> - {NewDir, RemainingFlags} = - gobble_inc_dir(string:sub_string(Flags, X+2),nq,[]), - include_dir(RemainingFlags, [NewDir|IncDirs]) - end. - -% nq = not-quoted, q = quoted. -% Possible strange scenarios: -% /usr/test\ ing/ -% "/usr/test ing/" -% /usr/test\"ing/ -% "/usr/test\"ing/" -gobble_inc_dir([],nq,Acc) -> - % Only accept nq here, if we end up here in q mode the user has missed a " - {lists:reverse(Acc),[]}; -gobble_inc_dir([$\\,$"|R],Q,Acc) -> - gobble_inc_dir(R,Q,[$"|Acc]); -gobble_inc_dir([$"|R],nq,Acc) -> - gobble_inc_dir(R,q,Acc); -gobble_inc_dir([$"|R],q,Acc) -> - gobble_inc_dir(R,nq,Acc); -gobble_inc_dir([$\\,$ |R],nq,Acc) -> - gobble_inc_dir(R,nq,[$ |Acc]); -gobble_inc_dir([$ |R],nq,Acc) -> - {lists:reverse(Acc),R}; -gobble_inc_dir([C|R],Q,Acc) -> - gobble_inc_dir(R,Q,[C|Acc]). - - -%%=============================================================== -%% Read a included file. Try current dir first then the IncDir list -%%=============================================================== - -read_inc_file(FileName, IncDir, Mio) -> - case find_inc_file(FileName, IncDir) of - {ok, AbsFile} -> - %% is included before? - case lists:member(FileName, Mio#mio.included) of - false -> - case catch file:read_file(AbsFile) of - {ok, Bin} -> - FileList = binary_to_list(Bin), - {ok, AbsFile, FileList}; - {error, Text} -> - {error, Text} - end; - true -> - skip - end; - {error, Text} -> - {error, Text} - end. - -find_inc_file(FileName, IncDir) -> - case catch file:read_file_info(FileName) of - {ok, _} -> - {ok, FileName}; - {error, _} -> - find_inc_file2(FileName, IncDir) - end. - -find_inc_file2(_FileName, []) -> - {error, "No such file or directory"}; -find_inc_file2(FileName, [D|Rem]) -> - Dir = case lists:last(D) of - $/ -> - D; - _ -> - D++"/" - end, - case catch file:read_file_info(Dir++FileName) of - {ok, _} -> - {ok, Dir++FileName}; - {error, _} -> - find_inc_file2(FileName, Rem) - end. - - -%%=============================================================== -%% Read parameters of a macro or a variable in a source line -%%=============================================================== -read_para([{char,$(} | Rem]) -> - read_para(Rem, 1, [], [], 1); -read_para([space,{char,$(} | Rem]) -> - read_para(Rem, 1, [], [], 1); -read_para(_Rem) -> - {ok, [], [], 0}. - - -%% Abrupt end of the list -read_para([], _NoOfParen, _Current, _Para, _NoOfPara) -> - {error, illegal_arg}; -%% All parameters checked -read_para([{char,$)}|Rem], 1, [], Para, NoOfPara) -> - {ok, Rem, lists:reverse(Para), NoOfPara}; -read_para([{char,$)}|Rem], 1, Current, Para, NoOfPara) -> - {ok, Rem, lists:reverse([Current|Para]), NoOfPara}; - -%% Continue reading -read_para([{char,$)}|Rem], NoOfParen, Current, Para, NoOfPara) -> - read_para(Rem, NoOfParen-1, Current++[{char,$)}], Para, NoOfPara); -read_para([{char,$(}|Rem], NoOfParen, Current, Para, NoOfPara) -> - read_para(Rem, NoOfParen+1, Current++[{char,$(}], Para, NoOfPara); -read_para([{char,$,}|Rem], NoOfParen, Current, Para, NoOfPara) when NoOfParen == 1 -> - read_para(Rem, NoOfParen, [], [Current|Para], NoOfPara+1); -read_para([space|Rem], NoOfParen, [], Para, NoOfPara) -> - read_para(Rem, NoOfParen, [], Para, NoOfPara); -read_para([X|Rem], NoOfParen, Current, Para, NoOfPara) -> - read_para(Rem, NoOfParen, Current++[X], Para, NoOfPara). - - - - - - -%%=================================================================================== -%% check if a macro is already defined -%%=================================================================================== -is_define_ok(Name, No_of_para, Parameters, Macro, Defs) -> - - case lists:keysearch(Name, 1, Defs) of - {value, {Name, No_of_para, _MacroPara, Macro}} -> - {yes, Defs}; - {value, _} -> - Defs2 = lists:keydelete(Name, 1, Defs), - NewMacro = is_define_ok_check_para(Parameters, Macro, []), - case is_stringify_ok(NewMacro) of - yes -> - {no, [{Name, No_of_para, Parameters, NewMacro}|Defs2]}; - no -> - ErrorText = "`#' operator is not followed by a macro argument name", - {error, ErrorText, [{Name, No_of_para, Parameters, NewMacro}|Defs2]} - end; - false -> - NewMacro = is_define_ok_check_para(Parameters, Macro, []), - case is_stringify_ok(NewMacro) of - yes -> - {yes, [{Name, No_of_para, Parameters, NewMacro}|Defs]}; - no -> - ErrorText = "`#' operator is not followed by a macro argument name", - {error, ErrorText, [{Name, No_of_para, Parameters, NewMacro}|Defs]} - end - end. - -is_define_ok_check_para(_Para, [], Result) -> - lists:reverse(Result); - -is_define_ok_check_para(Para, [H|T], Result) -> - case define_arg_para_number(1, Para, H) of - no_para -> - is_define_ok_check_para(Para, T, [H|Result]); - N -> - is_define_ok_check_para(Para, T, [{para,N}|Result]) - end. - -define_arg_para_number(_N, [], _Current) -> - no_para; -define_arg_para_number(N, [H|_Para], Current) when H == [Current] -> - N; -define_arg_para_number(N, [_H|Para], Current) -> - define_arg_para_number(N+1, Para, Current). - - -is_stringify_ok([]) -> - yes; -is_stringify_ok([{char,$#},{char,$#}|T]) -> - is_stringify_ok(T); -is_stringify_ok([{char,$#},space,{para,_X}|T]) -> - is_stringify_ok(T); -is_stringify_ok([{char,$#},{para,_X}|T]) -> - is_stringify_ok(T); -is_stringify_ok([{char,$#},space,{var,_X}|T]) -> - is_stringify_ok(T); -is_stringify_ok([{char,$#},{var,_X}|T]) -> - is_stringify_ok(T); -is_stringify_ok([{char,$#},space,{nl,_X}|_T]) -> - no; -is_stringify_ok([{char,$#},{nl,_X}|_T]) -> - no; -is_stringify_ok([{char,$#}|_T]) -> - no; -is_stringify_ok([_H|T]) -> - is_stringify_ok(T). - -%%=================================================================================== -%% check if a macro is already defined -%%=================================================================================== -is_defined_before(Name, No_of_para, Defs) -> - case lists:keysearch(Name, 1, Defs) of - {value, {Name, No_of_para, _MacroPara, _Macro}} -> - yes; - {value, _} -> - no; - false -> - no - end. - - - - -%%=================================================================================== -%% read_to_nl(File) -%%=================================================================================== -read_to_nl([space|Rem]) -> - read_to_nl(Rem, [], 1); -read_to_nl(Rem) -> - read_to_nl(Rem, [], 1). - -read_to_nl([], Result, Nl) -> - {lists:reverse(Result), [], Nl}; -read_to_nl([{nl, _N}|Rem], [{string_part,String} | Result], Nl) -> - read_to_nl(Rem, [nl, {string_part,String}|Result], Nl+1); -read_to_nl([{nl, _N}|Rem], [{sys_head_part,String} | Result], Nl) -> - read_to_nl(Rem, [nl, {sys_head_part,String}|Result], Nl+1); -read_to_nl([{nl, _N}|Rem], Result, Nl) -> - {lists:reverse(Result), Rem, Nl}; -read_to_nl([space|Rem], Result, Nl) -> - read_to_nl(Rem, [space|Result], Nl); -read_to_nl([{X,String}|Rem], Result, Nl) -> - read_to_nl(Rem, [{X,String}|Result], Nl). - - - - -%%=========================================================== -%% Read characters until next newline -%%=========================================================== -%read_to_nl2(Str) -> read_to_nl2([],Str). - -%read_to_nl2(Line, []) -> {Line,[]}; -%read_to_nl2(Line, [$\n|Str]) -> {Line, Str}; -%read_to_nl2(Line, [X|Str]) -> read_to_nl2([X|Line], Str). - - - - -%%=========================================================== -%% Remove leading spaces from a list -%%=========================================================== -remove_leading_spaces([?space|List]) -> - remove_leading_spaces(List); -remove_leading_spaces([?tab|List]) -> - remove_leading_spaces(List); -remove_leading_spaces(List) -> - List. - - - - -%%=========================================================== -%% Skip characters until next newline -%%=========================================================== -skip_to_nl([]) -> []; -skip_to_nl([$\n | Str]) -> Str; -skip_to_nl([$\\,$\n | Str]) -> [$/,$/|Str]; -skip_to_nl([_|Str]) -> skip_to_nl(Str). - - - - -month(1) -> "Jan"; -month(2) -> "Feb"; -month(3) -> "Mar"; -month(4) -> "Apr"; -month(5) -> "May"; -month(6) -> "Jun"; -month(7) -> "Jul"; -month(8) -> "Aug"; -month(9) -> "Sep"; -month(10) -> "Oct"; -month(11) -> "Nov"; -month(12) -> "Dec". - - -%% Multiple Include Optimization -%% -%% Algorithm described at: -%% http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html -update_mio({include, FileName}, #mio{included=Inc}=Mio) -> - Mio#mio{valid=false, included=[FileName|Inc]}; - -%% valid=false & cmacro=undefined indicates it is already decided this file is -%% not subject to MIO -update_mio(_, #mio{valid=false, depth=0, cmacro=undefined}=Mio) -> - Mio; - -%% if valid=true, there is no non-whitespace tokens before this ifndef -update_mio({'ifndef', Macro}, #mio{valid=true, depth=0, cmacro=undefined}=Mio) -> - Mio#mio{valid=false, cmacro=Macro, depth=1}; - -%% detect any tokens before top level #ifndef -update_mio(_, #mio{valid=true, depth=0, cmacro=undefined}=Mio) -> - Mio#mio{valid=false}; - -%% If cmacro is alreay set, this is after the top level #endif -update_mio({'ifndef', _}, #mio{valid=true, depth=0}=Mio) -> - Mio#mio{valid=false, cmacro=undefined}; - -%% non-top level conditional, just update depth -update_mio({'ifndef', _}, #mio{depth=D}=Mio) when D > 0 -> - Mio#mio{depth=D+1}; -update_mio('ifdef', #mio{depth=D}=Mio) -> - Mio#mio{depth=D+1}; -update_mio('if', #mio{depth=D}=Mio) -> - Mio#mio{depth=D+1}; - -%% top level #else #elif invalidates multiple include optimization -update_mio('else', #mio{depth=1}=Mio) -> - Mio#mio{valid=false, cmacro=undefined}; -update_mio('else', Mio) -> - Mio; -update_mio('elif', #mio{depth=1}=Mio) -> - Mio#mio{valid=false, cmacro=undefined}; -update_mio('elif', Mio) -> - Mio; - -%% AT exit to top level, if the controlling macro is not set, this could be the -%% end of a non-ifndef conditional block, or there were tokens before entering -%% the #ifndef block. In either way, this invalidates the MIO -%% -%% It doesn't matter if `valid` is true at the time of exiting, it is set to -%% true. This will be used to detect if more tokens are following the top -%% level #endif. -update_mio('endif', #mio{depth=1, cmacro=undefined}=Mio) -> - Mio#mio{valid=false, depth=0}; -update_mio('endif', #mio{depth=1}=Mio) -> - Mio#mio{valid=true, depth=0}; -update_mio('endif', #mio{depth=D}=Mio) when D > 1 -> - Mio#mio{valid=true, depth=D-1}; - -%%if more tokens are following the top level #endif. -update_mio('endif', #mio{depth=1, cmacro=undefined}=Mio) -> - Mio#mio{valid=false, depth=0}; -update_mio('endif', #mio{depth=D}=Mio) when D > 0 -> - Mio#mio{valid=true, depth=D-1}; -update_mio(_, Mio) -> - Mio#mio{valid=false}. - -%% clear `valid`, this doesn't matter since #endif will restore it if -%% appropriate -update_mio(Mio) -> - Mio#mio{valid=false}. - - |