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     /// Wheter to enable macro expansion.
83     bool enableMacroExpansion = true;
84 
85     /** 
86      * Disables all directives.
87      * They can then be individually enabled again.
88      */
89     void disableAllDirectives() {
90         enableIncludeDirectives = false;
91         enableConditionalDirectives = false;
92         enableMacroDefineDirectives = false;
93         enableMacroUndefineDirectives = false;
94         enableErrorDirectives = false;
95         enablePragmaDirectives = false;
96     }
97 
98     /** 
99      * Enables all directives.
100      * They can then be individually disabled again.
101      */
102     void enableAllDirectives() {
103         enableIncludeDirectives = true;
104         enableConditionalDirectives = true;
105         enableMacroDefineDirectives = true;
106         enableMacroUndefineDirectives = true;
107         enableErrorDirectives = true;
108         enablePragmaDirectives = true;
109     }
110 }
111 
112 /** 
113  * Result with modified source files.
114  */
115 struct ProcessingResult {
116     /// The processed (main) sources.
117     SourceMap sources;
118 
119     /**
120      * Textual date of when the processing started
121      * e.g: Feb 16 2023
122      */
123     string date;
124 
125     /**
126      * Textual time of when the processing started
127      * e.g: 22:31:01
128      */
129     string time;
130 
131     /**
132      * Textual timestamp of when the processing started
133      * e.g: Thu Feb 16 22:38:10 2023
134      */
135     string timestamp;
136 }
137 
138 /** 
139  * An exception typically thrown when there are parsing errors while preprocessing.
140  */
141 class ParseException : PreprocessException {
142     this(in ref ParseContext parseCtx, string msg, string file = __FILE__, size_t line = __LINE__) {
143         auto errorMessage = "Parse error: " ~ msg;
144         super(parseCtx, parseCtx.codePos, errorMessage, file, line);
145     }
146 }
147 
148 /** 
149  * An exception thrown when something fails while preprocessing.
150  * Except for parsing errors, they will be thrown as a ParseException.
151  */
152 class PreprocessException : Exception {
153     this(in ref ParseContext parseCtx, string msg, string file = __FILE__, size_t line = __LINE__) {
154         this(parseCtx, parseCtx.codePos, msg, file, line);
155     }
156 
157     this(in ref ParseContext parseCtx, ulong codePos, string msg, string file = __FILE__, size_t line = __LINE__) {
158         ulong srcLine;
159         ulong srcColumn;
160         calculateLineColumn(parseCtx, codePos, srcLine, srcColumn);
161         auto parseErrorMsg = "Error processing " ~ parseCtx.name ~ "(" ~ srcLine.to!string ~ "," ~ srcColumn
162             .to!string ~ "): " ~ msg;
163         super(parseErrorMsg, file, line);
164     }
165 }