1 /**
2  * Authors:
3  *  Mike Bierlee, m.bierlee@lostmoment.com
4  * Copyright: 2023 Mike Bierlee
5  * License:
6  *  This software is licensed under the terms of the MIT license.
7  *  The full terms of the license can be found in the LICENSE.txt file.
8  */
9 
10 module preprocessor.artifacts;
11 
12 import preprocessor.parsing : ParseContext, calculateLineColumn;
13 
14 import std.conv : to;
15 
16 alias SourceMap = string[string];
17 alias MacroMap = string[string];
18 
19 enum FileMacro = "FILE";
20 enum LineMacro = "LINE";
21 enum DateMacro = "DATE";
22 enum TimeMacro = "TIME";
23 enum TimestampMacro = "TIMESTAMP";
24 
25 static const string[] builtInMacros = [
26     FileMacro, LineMacro, DateMacro, TimeMacro, TimestampMacro
27 ];
28 
29 /**
30  * A context containing information regarding the build process,
31  * such a sources.
32  */
33 struct BuildContext {
34     /// Sources to be processed.
35     SourceMap sources;
36 
37     /** 
38      * When specified, only these sources will be processed.
39      * Sources specified in "sources" will still be able to be included
40      * and processed, but are treated as libraries.
41      * When empty, all sources in "sources" will be processed instead.
42      */
43     SourceMap mainSources;
44 
45     /**
46      * A map of pre-defined macros. 
47      * Built-in macros will override these.
48      */
49     MacroMap macros;
50 
51     /**
52      * The maximum amount of inclusions allowed. This is to prevent 
53      * an endless inclusion cycle. Defaults to 4000.
54      */
55     uint inclusionLimit = 4000;
56 
57     /** 
58      * Whether the parser should ignore #elif, #else and #endif
59      * directives that didn't come after a #if directive.
60      * If true they will be kept in the result.
61      */
62     bool ignoreUnmatchedConditionalDirectives = false;
63 
64     /// Wheter to enable processing of #include directives.
65     bool enableIncludeDirectives = true;
66 
67     /// Wheter to enable processing of conditional directives.
68     bool enableConditionalDirectives = true;
69 
70     /// Wheter to enable processing of #define directives.
71     bool enableMacroDefineDirectives = true;
72 
73     /// Wheter to enable processing of #undef directives.
74     bool enableMacroUndefineDirectives = true;
75 
76     /// Wheter to enable processing of #error directives.
77     bool enableErrorDirectives = true;
78 
79     /// Wheter to enable processing of #pragma directives.
80     bool enablePragmaDirectives = true;
81 
82     /** 
83      * Disables all directives.
84      * They can then be individually enabled again.
85      */
86     void disableAllDirectives() {
87         enableIncludeDirectives = false;
88         enableConditionalDirectives = false;
89         enableMacroDefineDirectives = false;
90         enableMacroUndefineDirectives = false;
91         enableErrorDirectives = false;
92         enablePragmaDirectives = false;
93     }
94 
95     /** 
96      * Enables all directives.
97      * They can then be individually disabled again.
98      */
99     void enableAllDirectives() {
100         enableIncludeDirectives = true;
101         enableConditionalDirectives = true;
102         enableMacroDefineDirectives = true;
103         enableMacroUndefineDirectives = true;
104         enableErrorDirectives = true;
105         enablePragmaDirectives = true;
106     }
107 }
108 
109 /** 
110  * Result with modified source files.
111  */
112 struct ProcessingResult {
113     /// The processed (main) sources.
114     SourceMap sources;
115 
116     /**
117      * Textual date of when the processing started
118      * e.g: Feb 16 2023
119      */
120     string date;
121 
122     /**
123      * Textual time of when the processing started
124      * e.g: 22:31:01
125      */
126     string time;
127 
128     /**
129      * Textual timestamp of when the processing started
130      * e.g: Thu Feb 16 22:38:10 2023
131      */
132     string timestamp;
133 }
134 
135 /** 
136  * An exception typically thrown when there are parsing errors while preprocessing.
137  */
138 class ParseException : PreprocessException {
139     this(in ref ParseContext parseCtx, string msg, string file = __FILE__, size_t line = __LINE__) {
140         auto errorMessage = "Parse error: " ~ msg;
141         super(parseCtx, parseCtx.codePos, errorMessage, file, line);
142     }
143 }
144 
145 /** 
146  * An exception thrown when something fails while preprocessing.
147  * Except for parsing errors, they will be thrown as a ParseException.
148  */
149 class PreprocessException : Exception {
150     this(in ref ParseContext parseCtx, string msg, string file = __FILE__, size_t line = __LINE__) {
151         this(parseCtx, parseCtx.codePos, msg, file, line);
152     }
153 
154     this(in ref ParseContext parseCtx, ulong codePos, string msg, string file = __FILE__, size_t line = __LINE__) {
155         ulong srcLine;
156         ulong srcColumn;
157         calculateLineColumn(parseCtx, codePos, srcLine, srcColumn);
158         auto parseErrorMsg = "Error processing " ~ parseCtx.name ~ "(" ~ srcLine.to!string ~ "," ~ srcColumn
159             .to!string ~ "): " ~ msg;
160         super(parseErrorMsg, file, line);
161     }
162 }