xref: /haiku/src/tools/gensyscalls/gensyscalls.cpp (revision 2e463a12a9f0f8054990ffda360138d8fa020d26)
1 // gensyscalls.cpp
2 //
3 // Copyright (c) 2004, Ingo Weinhold <bonefish@cs.tu-berlin.de>
4 // All rights reserved. Distributed under the terms of the OpenBeOS License.
5 
6 #include <fcntl.h>
7 #include <fstream>
8 #include <list>
9 #include <stack>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string>
13 #include <vector>
14 
15 // usage
16 const char *kUsage =
17 "Usage: gensyscalls <header> <dispatcher template> <calls> <dispatcher>\n"
18 "\n"
19 "  <header>               - Input: The preprocessed header file with the\n"
20 "                           syscall prototypes.\n"
21 "  <dispatcher template>  - Input: A template used to generate the syscall\n"
22 "                           dispatcher source file.\n"
23 "  <calls>                - Output: The assembly source file implementing the\n"
24 "                           actual syscalls."
25 "  <dispatcher>           - Output: The syscall dispatcher source file.\n";
26 
27 // print_usage
28 static
29 void
30 print_usage(bool error)
31 {
32 	fprintf((error ? stderr : stdout), kUsage);
33 }
34 
35 // Type
36 struct Type {
37 	Type(const char *type) : type(type) {}
38 	Type(const string &type) : type(type) {}
39 
40 	string type;
41 };
42 
43 // Function
44 class Function {
45 public:
46 	Function() : fReturnType("") {}
47 
48 	void SetName(const string &name)
49 	{
50 		fName = name;
51 	}
52 
53 	const string &GetName() const
54 	{
55 		return fName;
56 	}
57 
58 	void AddParameter(const Type &type)
59 	{
60 		fParameters.push_back(type);
61 	}
62 
63 	int CountParameters() const
64 	{
65 		return fParameters.size();
66 	}
67 
68 	const Type &ParameterAt(int index) const
69 	{
70 		return fParameters[index];
71 	}
72 
73 	void SetReturnType(const Type &type)
74 	{
75 		fReturnType = type;
76 	}
77 
78 	const Type &GetReturnType() const
79 	{
80 		return fReturnType;
81 	}
82 
83 private:
84 	string			fName;
85 	vector<Type>	fParameters;
86 	Type			fReturnType;
87 };
88 
89 // Exception
90 struct Exception : exception {
91 	Exception()
92 		: fMessage()
93 	{
94 	}
95 
96 	Exception(const string &message)
97 		: fMessage(message)
98 	{
99 	}
100 
101 	virtual ~Exception() {}
102 
103 	virtual const char *what() const
104 	{
105 		return fMessage.c_str();
106 	}
107 
108 private:
109 	string	fMessage;
110 };
111 
112 // EOFException
113 struct EOFException : public Exception {
114 	EOFException() {}
115 	EOFException(const string &message) : Exception(message) {}
116 	virtual ~EOFException() {}
117 };
118 
119 // IOException
120 struct IOException : public Exception {
121 	IOException() {}
122 	IOException(const string &message) : Exception(message) {}
123 	virtual ~IOException() {}
124 };
125 
126 // ParseException
127 struct ParseException : public Exception {
128 	ParseException() {}
129 	ParseException(const string &message) : Exception(message) {}
130 	virtual ~ParseException() {}
131 };
132 
133 
134 // Tokenizer
135 class Tokenizer {
136 public:
137 	Tokenizer(istream &input)
138 		: fInput(input),
139 		  fHasCurrent(false)
140 	{
141 	}
142 
143 	string GetCurrentToken()
144 	{
145 		if (!fHasCurrent)
146 			throw Exception("GetCurrentToken(): No current token!");
147 		return fTokens.front();
148 	}
149 
150 	string GetNextToken()
151 	{
152 		return GetNextToken(NULL);
153 	}
154 
155 	string GetNextToken(stack<string> &skippedTokens)
156 	{
157 		return GetNextToken(&skippedTokens);
158 	}
159 
160 	string GetNextToken(stack<string> *skippedTokens)
161 	{
162 		if (fHasCurrent) {
163 			fTokens.pop_front();
164 			fHasCurrent = false;
165 		}
166 		while (fTokens.empty())
167 			_ReadLine();
168 		fHasCurrent = true;
169 		if (skippedTokens)
170 			skippedTokens->push(fTokens.front());
171 printf("next token: `%s'\n", fTokens.front().c_str());
172 		return fTokens.front();
173 	}
174 
175 	void ExpectToken(const string &expectedToken)
176 	{
177 		string token = GetCurrentToken();
178 		if (expectedToken != token) {
179 			throw ParseException(string("Unexpected token `") + token
180 				+ "'. Expected was `" + expectedToken + "'.");
181 		}
182 	}
183 
184 	void ExpectNextToken(const string &expectedToken)
185 	{
186 		GetNextToken();
187 		ExpectToken(expectedToken);
188 	}
189 
190 	bool CheckToken(const string &expectedToken)
191 	{
192 		string token = GetCurrentToken();
193 		return (expectedToken == token);
194 	}
195 
196 	bool CheckNextToken(const string &expectedToken)
197 	{
198 		GetNextToken();
199 		bool result = CheckToken(expectedToken);
200 		if (!result)
201 			PutCurrentToken();
202 		return result;
203 	}
204 
205 	void PutCurrentToken()
206 	{
207 		if (!fHasCurrent)
208 			throw Exception("GetCurrentToken(): No current token!");
209 		fHasCurrent = false;
210 	}
211 
212 	void PutToken(string token)
213 	{
214 		if (fHasCurrent) {
215 			fTokens.pop_front();
216 			fHasCurrent = false;
217 		}
218 		fTokens.push_front(token);
219 	}
220 
221 	void PutTokens(stack<string> &tokens)
222 	{
223 		if (fHasCurrent) {
224 			fTokens.pop_front();
225 			fHasCurrent = false;
226 		}
227 		while (!tokens.empty()) {
228 			fTokens.push_front(tokens.top());
229 			tokens.pop();
230 		}
231 	}
232 
233 private:
234 	void _ReadLine()
235 	{
236 		// read the line
237 		char buffer[10240];
238 		if (!fInput.getline(buffer, sizeof(buffer)))
239 			throw EOFException("Unexpected end of input.");
240 		// parse it
241 		int len = strlen(buffer);
242 		int tokenStart = 0;
243 		for (int i = 0; i < len; i++) {
244 			char c = buffer[i];
245 			if (isspace(c)) {
246 				if (tokenStart < i)
247 					fTokens.push_back(string(buffer + tokenStart, buffer + i));
248 				tokenStart = i + 1;
249 				continue;
250 			}
251 			switch (buffer[i]) {
252 				case '#':
253 				case '(':
254 				case ')':
255 				case '*':
256 				case '&':
257 				case '[':
258 				case ']':
259 				case ';':
260 				case ',':
261 					if (tokenStart < i) {
262 						fTokens.push_back(string(buffer + tokenStart,
263 												 buffer + i));
264 					}
265 					fTokens.push_back(string(buffer + i, buffer + i + 1));
266 					tokenStart = i + 1;
267 					break;
268 			}
269 		}
270 		if (tokenStart < len)
271 			fTokens.push_back(string(buffer + tokenStart, buffer + len));
272 	}
273 
274 private:
275 	istream			&fInput;
276 	list<string>	fTokens;
277 	bool			fHasCurrent;
278 };
279 
280 
281 // Main
282 class Main {
283 public:
284 
285 	int Run(int argc, char **argv)
286 	{
287 		// parse parameters
288 		if (argc >= 2
289 			&& (string(argv[1]) == "-h" || string(argv[1]) == "--help")) {
290 			print_usage(false);
291 			return 0;
292 		}
293 		if (argc != 5) {
294 			print_usage(true);
295 			return 1;
296 		}
297 //		fDispatcherTemplateFile = argv[2];
298 //		fCallsFile = argv[3];
299 //		fDispatcherFile = argv[4];
300 		// open the files
301 		fHeaderFile.open(argv[1], ifstream::in);
302 		if (!fHeaderFile.is_open())
303 			throw new IOException(string("Failed to open `") + argv[1] + "'.");
304 		// parse the syscalls
305 		vector<Function> syscalls;
306 		_ParseSyscalls(syscalls);
307 		for (int i = 0; i < (int)syscalls.size(); i++) {
308 			const Function &syscall = syscalls[i];
309 			printf("syscall: `%s'\n", syscall.GetName().c_str());
310 			for (int k = 0; k < (int)syscall.CountParameters(); k++)
311 				printf("  arg: `%s'\n", syscall.ParameterAt(k).type.c_str());
312 			printf("  return type: `%s'\n",
313 				syscall.GetReturnType().type.c_str());
314 
315 		}
316 printf("Found %lu syscalls.\n", syscalls.size());
317 		return 0;
318 	}
319 
320 
321 private:
322 	void _ParseSyscalls(vector<Function> &syscalls)
323 	{
324 		Tokenizer tokenizer(fHeaderFile);
325 		// find "#pragma syscalls begin"
326 		while (true) {
327 			while (tokenizer.GetNextToken() != "#");
328 			stack<string> skippedTokens;
329 			if (tokenizer.GetNextToken(skippedTokens) == "pragma"
330 				&& tokenizer.GetNextToken(skippedTokens) == "syscalls"
331 				&& tokenizer.GetNextToken(skippedTokens) == "begin") {
332 				break;
333 			}
334 			tokenizer.PutTokens(skippedTokens);
335 		}
336 		// parse the functions
337 		while (!tokenizer.CheckNextToken("#")) {
338 			Function syscall;
339 			_ParseSyscall(tokenizer, syscall);
340 			syscalls.push_back(syscall);
341 		}
342 		// expect "pragma syscalls end"
343 		tokenizer.ExpectNextToken("pragma");
344 		tokenizer.ExpectNextToken("syscalls");
345 		tokenizer.ExpectNextToken("end");
346 	}
347 
348 	void _ParseSyscall(Tokenizer &tokenizer, Function &syscall)
349 	{
350 		// get return type and function name
351 		vector<string> returnType;
352 		while (tokenizer.GetNextToken() != "(") {
353 			string token = tokenizer.GetCurrentToken();
354 			// strip leading "extern"
355 			if (!returnType.empty() || token != "extern")
356 				returnType.push_back(token);
357 		}
358 		if (returnType.size() < 2) {
359 			throw ParseException("Error while parsing function "
360 				"return type.");
361 		}
362 		syscall.SetName(returnType[returnType.size() - 1]);
363 		returnType.pop_back();
364 		string returnTypeString(returnType[0]);
365 		for (int i = 1; i < (int)returnType.size(); i++) {
366 			returnTypeString += " ";
367 			returnTypeString += returnType[i];
368 		}
369 		syscall.SetReturnType(returnTypeString);
370 		// get arguments
371 		if (!tokenizer.CheckNextToken(")")) {
372 			_ParseParameter(tokenizer, syscall);
373 			while (!tokenizer.CheckNextToken(")")) {
374 				tokenizer.ExpectNextToken(",");
375 				_ParseParameter(tokenizer, syscall);
376 			}
377 		}
378 		tokenizer.ExpectNextToken(";");
379 	}
380 
381 	void _ParseParameter(Tokenizer &tokenizer, Function &syscall)
382 	{
383 		vector<string> type;
384 		while (tokenizer.GetNextToken() != ")"
385 			&& tokenizer.GetCurrentToken() != ",") {
386 			string token = tokenizer.GetCurrentToken();
387 			type.push_back(token);
388 			if (token == "(") {
389 				// This must be a function pointer. We deal with that in a
390 				// separate method.
391 				_ParseFunctionPointerParameter(tokenizer, syscall, type);
392 				return;
393 			}
394 		}
395 		tokenizer.PutCurrentToken();
396 		if (type.size() < 2) {
397 			if (type.size() == 1 && type[0] == "void") {
398 				// that's OK
399 				return;
400 			}
401 			throw ParseException("Error while parsing function "
402 				"parameter.");
403 		}
404 		type.pop_back(); // last component is the parameter name
405 		string typeString(type[0]);
406 		for (int i = 1; i < (int)type.size(); i++) {
407 			typeString += " ";
408 			typeString += type[i];
409 		}
410 		syscall.AddParameter(typeString);
411 	}
412 
413 	void _ParseFunctionPointerParameter(Tokenizer &tokenizer, Function &syscall,
414 		vector<string> &type)
415 	{
416 		// When this method is entered, the return type and the opening
417 		// parenthesis must already be parse and stored in the supplied type
418 		// vector.
419 		if (type.size() < 2) {
420 			throw ParseException("Error parsing function parameter. "
421 				"No return type.");
422 		}
423 		// read all the "*"s there are
424 		while (tokenizer.CheckNextToken("*"))
425 			type.push_back("*");
426 		// now comes the parameter name, if specified -- skip it
427 		if (!tokenizer.CheckNextToken(")")) {
428 			tokenizer.GetNextToken();
429 			tokenizer.ExpectNextToken(")");
430 		}
431 		type.push_back(")");
432 		// the function parameters
433 		tokenizer.ExpectNextToken("(");
434 		type.push_back("(");
435 		while (!tokenizer.CheckNextToken(")"))
436 			type.push_back(tokenizer.GetNextToken());
437 		type.push_back(")");
438 		// compose the type string and add it to the syscall parameters
439 		string typeString(type[0]);
440 		for (int i = 1; i < (int)type.size(); i++) {
441 			typeString += " ";
442 			typeString += type[i];
443 		}
444 		syscall.AddParameter(typeString);
445 	}
446 
447 private:
448 	ifstream	fHeaderFile;
449 	const char	*fDispatcherTemplateFile;
450 	const char	*fCallsFile;
451 	const char	*fDispatcherFile;
452 };
453 
454 
455 // main
456 int
457 main(int argc, char **argv)
458 {
459 	try {
460 		return Main().Run(argc, argv);
461 	} catch (Exception &exception) {
462 		fprintf(stderr, "%s\n", exception.what());
463 		return 1;
464 	}
465 }
466 
467