aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ic/src/ic_pp.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ic/src/ic_pp.erl')
-rw-r--r--lib/ic/src/ic_pp.erl2139
1 files changed, 2139 insertions, 0 deletions
diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl
new file mode 100644
index 0000000000..db06118d32
--- /dev/null
+++ b/lib/ic/src/ic_pp.erl
@@ -0,0 +1,2139 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights 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
+%%
+%%======================================================================================
+
+
+
+
+%%======================================================================================
+%%======================================================================================
+%%======================================================================================
+%% 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, 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, Defs, Err, War}
+%%======================================================================================
+run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir) ->
+
+ %%----------------------------------------------------------
+ %% 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
+ %%----------------------------------------------------------
+
+ %% Try first pass without file info start/end
+ {OutT, ErrT, WarT, DefsT, IfCouT} =
+ expand(File, Defs, Err, War, [FileName|IncFile], IncDir),
+
+ {Out2, Err2, War2, Defs2, IfCou2} =
+ case only_nls(OutT) of
+ true -> %% The file is defined before
+ {["\n"], ErrT, WarT, DefsT, IfCouT};
+ false -> %% The file is not defined before, try second pass
+ expand([FileInfoStart|File]++FileInfoEnd, Defs, Err, War, [FileName|IncFile], IncDir)
+ 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}.
+
+
+
+%% Return true if there is no data
+%% other than new lines
+only_nls([]) ->
+ true;
+only_nls(["\n"|Rem]) ->
+ only_nls(Rem);
+only_nls(["\r","\n"|Rem]) ->
+ only_nls(Rem);
+only_nls([_|_Rem]) ->
+ false.
+
+
+
+
+
+
+
+
+
+
+%%===================================================================================
+%%===================================================================================
+%%===================================================================================
+%% 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 ~p~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 ~p 1~n",[FileName]))),
+ FileInfoStart = {file_info, FI_start},
+ FI_end = lists:reverse(lists:flatten(io_lib:format("# ~p ~p 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, check_all, [], [], 1, FileName).
+
+expand(List, Defs, Err, War, [FileName|IncFile], IncDir) ->
+ expand(List, [], [], Defs, [FileName|IncFile], IncDir, check_all, Err, War, 1, FileName).
+
+
+%%=======================================================
+%% Main loop for the expansion
+%%=======================================================
+expand([], Out, _SelfRef, Defs, _IncFile, _IncDir, 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, IfCou};
+
+expand([{file_info, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, Str++Out, SelfRef, Defs, IncFile, IncDir, 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, {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, IfCou2, Err, War, L, FN);
+
+
+expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {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, IfCou2, Err, War, L, FN);
+
+
+expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN)
+ when Command == "if" ->
+ case pp_command(Command, Rem, Defs, IncDir, Err, War, L, FN) of
+ {{'if', true}, Rem2, Err2, War2, Nl} ->
+ IfCou2 = {endif, Endif+1, IfLine},
+ expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, 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, IfCou2, Err2, War2, L+Nl, FN)
+ end;
+
+expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {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, check_all, Err, War, L+Nl, FN);
+ _ ->
+ IfCou2 = {endif, Endif-1, IfLine},
+ expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L+Nl, FN)
+ end;
+
+
+expand([{command,_Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {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, IfCou2, Err, War, L, FN);
+
+%% Solves a bug when spaces in front of hashmark !
+expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) ->
+ expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN);
+
+expand([{nl,_Nl} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) ->
+ expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN);
+
+
+expand([_X | Rem], Out, SelfRef, Defs, IncFile, IncDir, {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, {endif, Endif, IfLine}, Err, War, L, FN);
+
+
+
+
+
+%%---------------------------------------
+%% Check all tokens
+%%---------------------------------------
+expand([{nl, _N} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [$\n | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L+1, FN);
+
+expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([space_exp | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L, FN) ->
+ case pp_command(Command, Rem, Defs, IncDir, Err, War, L, FN) of
+ {define, Rem2, Defs2, Err2, War2, Nl} ->
+ Out2 = [lists:duplicate(Nl,$\n)|Out],
+ expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, 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, check_all, Err2, War2, L+Nl, FN);
+
+ {{include, ok}, FileName, FileCont, Rem2, Nl, Err2, War2} ->
+ {Out3, Defs3, Err3, War3} =
+ run_include(FileName, FileCont, Out, Defs, Err2, War2, L+Nl, IncFile, IncDir),
+ Nls = [],
+ Out4 = Out3++Nls++Out,
+ expand(Rem2, Out4, SelfRef, Defs3, IncFile, IncDir, 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, check_all, Err2, War2, L+Nl, FN);
+
+ {{ifdef, true}, Rem2, Err2, War2, Nl} ->
+ Out2 = [lists:duplicate(Nl,$\n)|Out],
+ IfCou2 = {endif, 1, L},
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN);
+ {{ifdef, false}, Rem2, Err2, War2, Nl} ->
+ Out2 = [lists:duplicate(Nl,$\n)|Out],
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, 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, IfCou2, Err2, War2, L+Nl, FN);
+ {{ifndef, false}, Rem2, Err2, War2, Nl} ->
+ Out2 = [lists:duplicate(Nl,$\n)|Out],
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN);
+
+ {endif, Rem2, Err2, War2, Nl} ->
+ Out2 = [lists:duplicate(Nl,$\n)|Out],
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, 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, 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],
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, 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"},
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, 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"},
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, 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, 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, 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, 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, check_all, [Err2|Err], War, L+Nl, FN);
+
+ hash_mark ->
+ expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L, FN);
+
+ {pragma, Rem2, Nl, Text} ->
+ Out2 = lists:duplicate(Nl,$\n)++Text++Out,
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, 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, 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,
+ case Command of
+ [X|_T] when ?is_upper(X) ->
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN);
+ [X|_T] when ?is_lower(X) ->
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN);
+ [X|_T] when ?is_underline(X) ->
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN);
+ _ ->
+ Err2 = {FN, L, "invalid preprocessing directive name"},
+ expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, 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, IfCou, Err, War, L, FN) ->
+ LL = io_lib:format("~p",[L]),
+ expand(Rem, [LL | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{var, "__FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [$",FN,$" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{var, "__DATE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, 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, IfCou, Err, War, L, FN);
+
+expand([{var, "__TIME__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, 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, IfCou, Err, War, L, FN);
+
+expand([{var, "__INCLUDE_LEVEL__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ IL = io_lib:format("~p",[length(IncFile)-1]),
+ expand(Rem, [IL | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{var, "__BASE_FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ [BF|_T] = lists:reverse(IncFile),
+ expand(Rem, [$",BF,$" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{var, Var} | Rem], Out, SelfRef, Defs, IncFile, IncDir, 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, IfCou, Err2, War2, L, FN);
+
+expand([{char, Char} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [Char | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{number, Number} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [Number | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{expanded, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [Str | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{self_ref, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ SelfRef2 = lists:delete(Str,SelfRef),
+ expand(Rem, Out, SelfRef2, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{string, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ expand(Rem, [$", Str, $" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN);
+
+expand([{string_part, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) ->
+ {Str2, Rem2, Nl} = expand_string_part([$"|Str], Rem),
+ expand(Rem2, [Str2| Out], SelfRef, Defs, IncFile, IncDir, 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} =
+ expand(tokenise(Adjusted,""),
+ [],
+ [],
+ [],
+ [],
+ [],
+ 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, Err, War, L, FN) ->
+ pp_command(Command, File, Defs, IncDir, Err, War, L, FN);
+
+pp_command(Command, File, Defs, IncDir, 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) 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, FileName, FileCont, Rem, Nl} ->
+ {{include, ok}, FileName, FileCont, Rem, Nl, Err, War}
+ 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}, 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}, 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) ->
+ case include2(File) of
+ {ok, FileName, Rem, Nl, FileType} ->
+ %% The error handling is lite strange just to make it compatible to gcc
+ case {read_inc_file(FileName, IncDir), Nl, FileType} of
+ {{ok, FileList, FileNamePath}, _, _} ->
+ {ok, FileNamePath, FileList, Rem, Nl};
+ {{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,IncDir) ->
+ case string:str(Flags,"-I") of
+ 0 ->
+ lists:reverse(IncDir);
+ X ->
+ Rem2 = string:sub_string(Flags, X+2),
+ Rem = string:strip(Rem2, left),
+ Y = string:str(Rem," "),
+ case string:str(Rem," ") of
+ 0 ->
+ lists:reverse([string:sub_string(Rem, Y+1)|IncDir]);
+ Y ->
+ include_dir(string:sub_string(Rem, Y+1),
+ [string:sub_string(Rem,1,Y-1)|IncDir])
+ end
+ end.
+
+
+
+%%===============================================================
+%% Read a included file. Try current dir first then the IncDir list
+%%===============================================================
+
+read_inc_file(FileName, IncDir) ->
+ case catch file:read_file(FileName) of
+ {ok, Bin} ->
+ FileList = binary_to_list(Bin),
+ {ok, FileList, FileName};
+ {error, _} ->
+ read_inc_file2(FileName, IncDir)
+ end.
+
+read_inc_file2(_FileName, []) ->
+ {error, "No such file or directory"};
+read_inc_file2(FileName, [D|Rem]) ->
+ Dir = case lists:last(D) of
+ $/ ->
+ D;
+ _ ->
+ D++"/"
+ end,
+
+ case catch file:read_file(Dir++FileName) of
+ {ok, Bin} ->
+ FileList = binary_to_list(Bin),
+ {ok, FileList, Dir++FileName};
+ {error, _} ->
+ read_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".
+
+
+
+