1/*
2 * Copyright 2008, Haiku.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Michael Pfeiffer <laplace@users.sourceforge.net>
7 */
8
9#include "AutoDelete.h"
10#include "CharacterClasses.h"
11#include "Parser.h"
12
13Parser::Parser(const char* file)
14	: fScanner(file)
15{
16	if (InitCheck() == B_OK) {
17		NextChar();
18	}
19}
20
21status_t Parser::InitCheck()
22{
23	return fScanner.InitCheck();
24}
25
26void Parser::SkipWhitespaces()
27{
28	while (IsWhitespace(GetCurrentChar())) {
29		NextChar();
30	}
31}
32
33void Parser::SkipComment()
34{
35	while (GetCurrentChar() != kEof && GetCurrentChar() != kCr) {
36		NextChar();
37	}
38	NextChar();
39}
40
41void Parser::SkipWhitespaceSeparator()
42{
43	while (IsWhitespaceSeparator(GetCurrentChar())) {
44		NextChar();
45	}
46}
47
48bool Parser::ParseKeyword(Statement* statement)
49{
50	// ["?"]
51	if (GetCurrentChar() == '?') {
52		NextChar();
53		statement->SetType(Statement::kQuery);
54	}
55	// Keyword
56	BString* keyword = fScanner.ScanIdent();
57	if (keyword == NULL) {
58		Error("Identifier expected");
59		return false;
60	}
61	statement->SetKeyword(keyword);
62	return true;
63}
64
65bool Parser::ParseTranslation(Value* value, int separator)
66{
67	BString* translation = fScanner.ScanTranslationValue(separator);
68	if (translation == NULL) {
69		Error("Out of memory scanning translationn!");
70		return false;
71	}
72	value->SetTranslation(translation);
73	return true;
74}
75
76bool Parser::ParseOption(Statement* statement)
77{
78	// ["^"]
79	bool isSymbolValue = GetCurrentChar() == '^';
80	if (isSymbolValue) {
81		NextChar();
82	}
83
84	// [ ... Option ...]
85	if (IsOptionChar(GetCurrentChar())) {
86
87		BString* option = fScanner.ScanOption();
88		if (option == NULL) {
89			Error("Out of memory scanning option!");
90			return false;
91		}
92		Value::Type type;
93		if (isSymbolValue) {
94			type = Value::kSymbolValue;
95		} else {
96			type = Value::kStringValue;
97		}
98		Value* value = new Value(option, type);
99		statement->SetOption(value);
100
101		SkipWhitespaceSeparator();
102		// ["/" Translation ]
103		if (GetCurrentChar() == '/') {
104			NextChar();
105			return ParseTranslation(value, ':');
106		}
107	} else {
108		if (isSymbolValue) {
109			Error("Expected symbol value!");
110			return false;
111		}
112	}
113	return true;
114}
115
116bool Parser::ParseValue(Statement* statement)
117{
118	if (GetCurrentChar() == '"') {
119		NextChar();
120
121		// "..."
122		AutoDelete<Value> value(new Value());
123
124		BString* string;
125		if (statement->GetOption() != NULL) {
126			string = fScanner.ScanInvocationValue();
127			value.Get()->SetType(Value::kInvocationValue);
128		} else {
129			string = fScanner.ScanQuotedValue();
130			value.Get()->SetType(Value::kQuotedValue);
131		}
132
133		if (string == NULL) {
134			Error("Expected value");
135			return false;
136		}
137
138		// " is expected
139		if (GetCurrentChar() != '"') {
140			Error("Expected \" at end of value");
141			return false;
142		}
143		NextChar();
144
145		value.Get()->SetValue(string);
146		statement->SetValue(value.Release());
147	} else if (GetCurrentChar() == '^') {
148		// ^ SymbolValue
149		BString* symbol = fScanner.ScanOption();
150		if (symbol == NULL) {
151			Error("Symbol expected!");
152			return false;
153		}
154		Value* value = new Value(symbol, Value::kSymbolValue);
155		statement->SetValue(value);
156	} else {
157		// StringValue
158		BString* stringValue = fScanner.ScanStringValue();
159		if (stringValue == NULL) {
160			Error("String value expected!");
161			return false;
162		}
163
164		Value* value = new Value(stringValue, Value::kStringValue);
165		statement->SetValue(value);
166	}
167	if (GetCurrentChar() == '/') {
168		NextChar();
169		return ParseTranslation(statement->GetValue(), kCr);
170	}
171	return true;
172}
173
174
175void Parser::UpdateStatementType(Statement* statement)
176{
177	if (statement->GetType() != Statement::kUnknown) return;
178
179	BString* keyword = statement->GetKeyword();
180	Statement::Type type;
181	if (keyword->FindFirst("Default") == 0) {
182		type = Statement::kDefault;
183		keyword->RemoveFirst("Default");
184	} else if (keyword->FindFirst("Param") == 0) {
185		type = Statement::kParam;
186		keyword->RemoveFirst("Param");
187	} else {
188		type = Statement::kValue;
189	}
190	statement->SetType(type);
191}
192
193// ["?"]Keyword [["^"]Option["/"Translation]]
194// [":"
195//   ["^"]Value ["/" Translation].
196// ]
197Statement* Parser::ParseStatement()
198{
199	AutoDelete<Statement> statement(new Statement());
200
201	if (!ParseKeyword(statement.Get())) {
202		return NULL;
203	}
204	SkipWhitespaceSeparator();
205
206	if (!ParseOption(statement.Get())) {
207		return NULL;
208	}
209
210	SkipWhitespaceSeparator();
211	// [":" ... ]
212	if (GetCurrentChar() == ':') {
213		NextChar();
214		SkipWhitespaceSeparator();
215		if (!ParseValue(statement.Get())) {
216			return NULL;
217		}
218	}
219	SkipWhitespaceSeparator();
220	if (GetCurrentChar() == kEof || GetCurrentChar() == kLf || GetCurrentChar() == kCr) {
221		UpdateStatementType(statement.Get());
222		Statement* result = statement.Release();
223		return result;
224	} else {
225		Error("Newline expected at end of statement");
226		return NULL;
227	}
228}
229
230Statement* Parser::Parse()
231{
232	while (true) {
233		int ch = GetCurrentChar();
234		if (ch == -1) {
235			return NULL;
236		}
237		if (IsWhitespace(ch)) {
238			SkipWhitespaces();
239		} else if (ch == '*') {
240			// begin of comment or statement
241			NextChar();
242			ch = GetCurrentChar();
243			if (ch == '%') {
244				SkipComment();
245			} else {
246				return ParseStatement();
247			}
248		} else {
249			Error("Expected *");
250			return NULL;
251		}
252	}
253}
254
255