1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2021 Tobias Kortkamp <tobik@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 #pragma once
29 
30 enum ASTType {
31 	AST_ROOT,
32 	AST_DELETED,
33 	AST_COMMENT,
34 	AST_EXPR,
35 	AST_IF,
36 	AST_FOR,
37 	AST_INCLUDE,
38 	AST_TARGET,
39 	AST_TARGET_COMMAND,
40 	AST_VARIABLE,
41 };
42 
43 const char *ASTType_tostring(enum ASTType);
44 
45 enum ASTCommentType {
46 	AST_COMMENT_LINE,
47 };
48 
49 const char *ASTCommentType_tostring(enum ASTCommentType);
50 
51 enum ASTExprType {
52 	AST_EXPR_ERROR,			// identifier:".error"
53 	AST_EXPR_EXPORT_ENV,		// identifier:".export-env"
54 	AST_EXPR_EXPORT_LITERAL,	// identifier:".export-literal"
55 	AST_EXPR_EXPORT,		// identifier:".export"
56 	AST_EXPR_INFO,			// identifier:".info"
57 	AST_EXPR_UNDEF,			// identifier:".undef"
58 	AST_EXPR_UNEXPORT_ENV,		// identifier:".unexport-env"
59 	AST_EXPR_UNEXPORT,		// identifier:".unexport"
60 	AST_EXPR_WARNING,		// identifier:".warning"
61 };
62 
63 const char *ASTExprType_identifier(enum ASTExprType);
64 const char *ASTExprType_tostring(enum ASTExprType);
65 
66 enum ASTIfType {
67 	AST_IF_IF,	// human:"if"
68 	AST_IF_DEF,	// human:"ifdef"
69 	AST_IF_ELSE,	// human:"else"
70 	AST_IF_MAKE,	// human:"ifmake"
71 	AST_IF_NDEF,	// human:"ifndef"
72 	AST_IF_NMAKE,	// human:"ifnmake"
73 };
74 
75 const char *ASTIfType_human(enum ASTIfType);
76 const char *ASTIfType_tostring(enum ASTIfType);
77 
78 enum ASTIncludeType {
79 	AST_INCLUDE_BMAKE,		// identifier:".include"
80 	AST_INCLUDE_POSIX,		// identifier:"include"
81 	AST_INCLUDE_POSIX_OPTIONAL,	// identifier:"-include"
82 	AST_INCLUDE_POSIX_OPTIONAL_S,	// identifier:"sinclude"
83 	AST_INCLUDE_OPTIONAL,		// identifier:".-include"
84 	AST_INCLUDE_OPTIONAL_D,		// identifier:".dinclude"
85 	AST_INCLUDE_OPTIONAL_S,		// identifier:".sinclude"
86 };
87 
88 const char *ASTIncludeType_identifier(enum ASTIncludeType);
89 const char *ASTIncludeType_tostring(enum ASTIncludeType);
90 
91 enum ASTTargetType {
92 	AST_TARGET_NAMED,
93 	AST_TARGET_UNASSOCIATED,
94 };
95 
96 const char *ASTTargetType_tostring(enum ASTTargetType);
97 
98 enum ASTTargetCommandFlag {
99 	AST_TARGET_COMMAND_FLAG_NONE = 0,			// human:"<none>"
100 	AST_TARGET_COMMAND_FLAG_ALWAYS_EXECUTE = 1 << 0,	// human:"+"
101 	AST_TARGET_COMMAND_FLAG_IGNORE_ERROR = 1 << 1,		// human:"-"
102 	AST_TARGET_COMMAND_FLAG_SILENT = 1 << 2,		// human:"@"
103 };
104 
105 const char *ASTTargetCommandFlag_human(enum ASTTargetCommandFlag);
106 const char *ASTTargetCommandFlag_tostring(enum ASTTargetCommandFlag);
107 
108 enum ASTVariableModifier {
109 	AST_VARIABLE_MODIFIER_APPEND,	// human:"+="
110 	AST_VARIABLE_MODIFIER_ASSIGN,	// human:"="
111 	AST_VARIABLE_MODIFIER_EXPAND,	// human:":="
112 	AST_VARIABLE_MODIFIER_OPTIONAL,	// human:"?="
113 	AST_VARIABLE_MODIFIER_SHELL,	// human:"!="
114 };
115 
116 const char *ASTVariableModifier_human(enum ASTVariableModifier);
117 const char *ASTVariableModifier_tostring(enum ASTVariableModifier);
118 
119 enum ASTWalkState {
120 	AST_WALK_CONTINUE,
121 	AST_WALK_STOP,
122 };
123 
124 const char *ASTWalkState_tostring(enum ASTWalkState);
125 
126 struct ASTComment {
127 	enum ASTCommentType type;
128 	struct Array *lines;
129 };
130 
131 struct ASTFor {
132 	// .for $bindings in $words
133 	// $body
134 	// .endfor
135 	struct Array *bindings;
136 	struct Array *words;
137 	struct Array *body;
138 	const char *comment;
139 	const char *end_comment;
140 	size_t indent;
141 };
142 
143 struct ASTIf {
144 	// .if $test
145 	// $body
146 	// .else
147 	// $orelse
148 	// .endif
149 	//
150 	// Elif:
151 	//
152 	// .if $test1
153 	// $body1
154 	// .elif $test2
155 	// $body2
156 	// .else
157 	// $orelse
158 	// .endif
159 	//
160 	// =>
161 	//
162 	// .if $test1
163 	// $body1
164 	// .else
165 	// .if $test2
166 	// $body2
167 	// .else
168 	// $orelse
169 	// .endif
170 	// .endif
171 	enum ASTIfType type;
172 	struct Array *test;
173 	struct Array *body;
174 	struct Array *orelse;
175 	const char *comment;
176 	const char *end_comment;
177 	size_t indent;
178 	struct AST *ifparent;
179 };
180 
181 struct ASTExpr {
182 	enum ASTExprType type;
183 	struct Array *words;
184 	const char *comment;
185 	size_t indent;
186 };
187 
188 struct ASTInclude {
189 	enum ASTIncludeType type;
190 	struct Array *body;
191 	const char *comment;
192 	size_t indent;
193 	const char *path;
194 	bool sys;
195 	bool loaded;
196 };
197 
198 struct ASTTarget {
199 	enum ASTTargetType type;
200 	struct Array *sources;
201 	struct Array *dependencies;
202 	struct Array *body;
203 	const char *comment;
204 };
205 
206 struct ASTTargetCommand {
207 	struct ASTTarget *target;
208 	struct Array *words;
209 	const char *comment;
210 	enum ASTTargetCommandFlag flags;
211 };
212 
213 struct ASTVariable {
214 	const char *name;
215 	enum ASTVariableModifier modifier;
216 	struct Array *words;
217 	const char *comment;
218 };
219 
220 struct ASTRoot {
221 	struct Array *body;
222 };
223 
224 struct ASTLineRange { // [a,b)
225 	size_t a;
226 	size_t b;
227 };
228 
229 struct AST {
230 	enum ASTType type;
231 	struct AST *parent;
232 	struct Mempool *pool;
233 	struct ASTLineRange line_start;
234 	struct ASTLineRange line_end;
235 	bool edited;
236 	struct {
237 		size_t goalcol;
238 	} meta;
239 	union {
240 		struct ASTRoot root;
241 		struct ASTComment comment;
242 		struct ASTExpr expr;
243 		struct ASTIf ifexpr;
244 		struct ASTInclude include;
245 		struct ASTFor forexpr;
246 		struct ASTTarget target;
247 		struct ASTTargetCommand targetcommand;
248 		struct ASTVariable variable;
249 	};
250 };
251 
252 void ast_free(struct AST *);
253 struct AST *ast_new(struct Mempool *, enum ASTType, struct ASTLineRange *, void *);
254 struct AST *ast_clone(struct Mempool *, struct AST *);
255 struct Array *ast_siblings(struct Mempool *, struct AST *);
256 void ast_parent_append_sibling(struct AST *, struct AST *, bool);
257 void ast_parent_insert_before_sibling(struct AST *, struct AST *);
258 void ast_print(struct AST *, FILE *);
259 void ast_balance(struct AST *);
260 
261 char *ast_line_range_tostring(struct ASTLineRange *, bool, struct Mempool *);
262 
263 #define AST_WALK_RECUR(x) \
264 	if ((x) == AST_WALK_STOP) { \
265 		return AST_WALK_STOP; \
266 	}
267 
268 #define AST_WALK_DEFAULT(f, node, ...) \
269 switch (node->type) { \
270 case AST_ROOT: \
271 	ARRAY_FOREACH(node->root.body, struct AST *, child) { \
272 		AST_WALK_RECUR(f(child, ##__VA_ARGS__)); \
273 	} \
274 	break; \
275 case AST_FOR: \
276 	ARRAY_FOREACH(node->forexpr.body, struct AST *, child) { \
277 		AST_WALK_RECUR(f(child, ##__VA_ARGS__)); \
278 	} \
279 	break; \
280 case AST_IF: \
281 	ARRAY_FOREACH(node->ifexpr.body, struct AST *, child) { \
282 		AST_WALK_RECUR(f(child, ##__VA_ARGS__)); \
283 	} \
284 	ARRAY_FOREACH(node->ifexpr.orelse, struct AST *, child) { \
285 		AST_WALK_RECUR(f(child, ##__VA_ARGS__)); \
286 	} \
287 	break; \
288 case AST_INCLUDE: \
289 	ARRAY_FOREACH(node->include.body, struct AST *, child) { \
290 		AST_WALK_RECUR(f(child, ##__VA_ARGS__)); \
291 	} \
292 	break; \
293 case AST_TARGET: \
294 	ARRAY_FOREACH(node->target.body, struct AST *, child) { \
295 		AST_WALK_RECUR(f(child, ##__VA_ARGS__)); \
296 	} \
297 	break; \
298 case AST_DELETED: \
299 case AST_COMMENT: \
300 case AST_EXPR: \
301 case AST_TARGET_COMMAND: \
302 case AST_VARIABLE: \
303 	break; \
304 }
305