1/*****************************************************************************/
2/* */
3/* scanstrbuf.c */
4/* */
5/* Small scanner for input from a StrBuf */
6/* */
7/* */
8/* */
9/* (C) 2002-2009, Ullrich von Bassewitz */
10/* Roemerstrasse 52 */
11/* D-70794 Filderstadt */
12/* EMail: uz@cc65.org */
13/* */
14/* */
15/* This software is provided 'as-is', without any expressed or implied */
16/* warranty. In no event will the authors be held liable for any damages */
17/* arising from the use of this software. */
18/* */
19/* Permission is granted to anyone to use this software for any purpose, */
20/* including commercial applications, and to alter it and redistribute it */
21/* freely, subject to the following restrictions: */
22/* */
23/* 1. The origin of this software must not be misrepresented; you must not */
24/* claim that you wrote the original software. If you use this software */
25/* in a product, an acknowledgment in the product documentation would be */
26/* appreciated but is not required. */
27/* 2. Altered source versions must be plainly marked as such, and must not */
28/* be misrepresented as being the original software. */
29/* 3. This notice may not be removed or altered from any source */
30/* distribution. */
31/* */
32/*****************************************************************************/
33
34
35
36/* common */
37#include "chartype.h"
38#include "tgttrans.h"
39
40/* cc65 */
41#include "datatype.h"
42#include "error.h"
43#include "hexval.h"
44#include "ident.h"
45#include "scanstrbuf.h"
46
47
48
49/*****************************************************************************/
50/* Helper functions */
51/*****************************************************************************/
52
53
54
55static int ParseChar (StrBuf* B)
56/* Parse a character. Converts \n into EOL, etc. */
57{
58 unsigned I;
59 unsigned Val;
60 int C;
61
62 /* Check for escape chars */
63 if ((C = SB_Get (B)) == '\\') {
64 switch (SB_Peek (B)) {
65 case '?':
66 C = '?';
67 SB_Skip (B);
68 break;
69 case 'a':
70 C = '\a';
71 SB_Skip (B);
72 break;
73 case 'b':
74 C = '\b';
75 SB_Skip (B);
76 break;
77 case 'f':
78 C = '\f';
79 SB_Skip (B);
80 break;
81 case 'r':
82 C = '\r';
83 SB_Skip (B);
84 break;
85 case 'n':
86 C = '\n';
87 SB_Skip (B);
88 break;
89 case 't':
90 C = '\t';
91 SB_Skip (B);
92 break;
93 case 'v':
94 C = '\v';
95 SB_Skip (B);
96 break;
97 case '\"':
98 C = '\"';
99 SB_Skip (B);
100 break;
101 case '\'':
102 C = '\'';
103 SB_Skip (B);
104 break;
105 case '\\':
106 C = '\\';
107 SB_Skip (B);
108 break;
109 case 'x':
110 case 'X':
111 /* Hex character constant */
112 SB_Skip (B);
113 C = HexVal (SB_Get (B)) << 4;
114 C |= HexVal (SB_Get (B));
115 break;
116 case '0':
117 case '1':
118 case '2':
119 case '3':
120 case '4':
121 case '5':
122 case '6':
123 case '7':
124 /* Octal constant */
125 I = 0;
126 Val = SB_Get (B) - '0';
127 while (SB_Peek (B) >= '0' && SB_Peek (B) <= '7' && ++I <= 3) {
128 Val = (Val << 3) | (SB_Get (B) - '0');
129 }
130 C = (int) Val;
131 if (Val > 256) {
132 Error ("Character constant out of range");
133 C = ' ';
134 }
135 break;
136 default:
137 Error ("Illegal character constant 0x%02X", SB_Get (B));
138 C = ' ';
139 break;
140 }
141 }
142
143 /* Return the character */
144 return C;
145}
146
147
148
149/*****************************************************************************/
150/* Code */
151/*****************************************************************************/
152
153
154
155void SB_SkipWhite (StrBuf* B)
156/* Skip whitespace in the string buffer */
157{
158 while (IsBlank (SB_Peek (B))) {
159 SB_Skip (B);
160 }
161}
162
163
164
165int SB_GetSym (StrBuf* B, StrBuf* Ident, const char* SpecialChars)
166/* Get a symbol from the string buffer. If SpecialChars is not NULL, it
167** points to a string that contains characters allowed within the string in
168** addition to letters, digits and the underline. Note: The identifier must
169** still begin with a letter.
170** Returns 1 if a symbol was found and 0 otherwise but doesn't output any
171** errors.
172*/
173{
174 /* Handle a NULL argument for SpecialChars transparently */
175 if (SpecialChars == 0) {
176 SpecialChars = "";
177 }
178
179 /* Clear Ident */
180 SB_Clear (Ident);
181
182 if (IsIdent (SB_Peek (B))) {
183 char C = SB_Peek (B);
184 do {
185 SB_AppendChar (Ident, C);
186 SB_Skip (B);
187 C = SB_Peek (B);
188 } while (IsIdent (C) || IsDigit (C) ||
189 (C != '\0' && strchr (SpecialChars, C) != 0));
190 SB_Terminate (Ident);
191 return 1;
192 } else {
193 return 0;
194 }
195}
196
197
198
199int SB_GetString (StrBuf* B, StrBuf* S)
200/* Get a string from the string buffer. Returns 1 if a string was found and 0
201** otherwise. Errors are only output in case of invalid strings (missing end
202** of string).
203*/
204{
205 char C;
206
207 /* Clear S */
208 SB_Clear (S);
209
210 /* A string starts with quote marks */
211 if (SB_Peek (B) == '\"') {
212
213 /* String follows, be sure to concatenate strings */
214 while (SB_Peek (B) == '\"') {
215
216 /* Skip the quote char */
217 SB_Skip (B);
218
219 /* Read the actual string contents */
220 while ((C = SB_Peek (B)) != '\"') {
221 if (C == '\0') {
222 Error ("Unexpected end of string");
223 break;
224 }
225 SB_AppendChar (S, ParseChar (B));
226 }
227
228 /* Skip the closing quote char if there was one */
229 SB_Skip (B);
230
231 /* Skip white space, read new input */
232 SB_SkipWhite (B);
233 }
234
235 /* Terminate the string */
236 SB_Terminate (S);
237
238 /* Success */
239 return 1;
240
241 } else {
242
243 /* Not a string */
244 SB_Terminate (S);
245 return 0;
246 }
247}
248
249
250
251int SB_GetNumber (StrBuf* B, long* Val)
252/* Get a number from the string buffer. Accepted formats are decimal, octal,
253** hex and character constants. Numeric constants may be preceeded by a
254** minus or plus sign. The function returns 1 if a number was found and
255** zero otherwise. Errors are only output for invalid numbers.
256*/
257{
258 int Sign;
259 char C;
260 unsigned Base;
261 unsigned DigitVal;
262
263
264 /* Initialize Val */
265 *Val = 0;
266
267 /* Handle character constants */
268 if (SB_Peek (B) == '\'') {
269
270 /* Character constant */
271 SB_Skip (B);
272 *Val = SignExtendChar (TgtTranslateChar (ParseChar (B)));
273 if (SB_Peek (B) != '\'') {
274 Error ("'\'' expected");
275 return 0;
276 } else {
277 /* Skip the quote */
278 SB_Skip (B);
279 return 1;
280 }
281 }
282
283 /* Check for a sign. A sign must be followed by a digit, otherwise it's
284 ** not a number
285 */
286 Sign = 1;
287 switch (SB_Peek (B)) {
288 case '-':
289 Sign = -1;
290 /* FALLTHROUGH */
291 case '+':
292 if (!IsDigit (SB_LookAt (B, SB_GetIndex (B) + 1))) {
293 return 0;
294 }
295 SB_Skip (B);
296 break;
297 }
298
299 /* We must have a digit now, otherwise its not a number */
300 C = SB_Peek (B);
301 if (!IsDigit (C)) {
302 return 0;
303 }
304
305 /* Determine the base */
306 if (C == '0') {
307 /* Hex or octal */
308 SB_Skip (B);
309 if (tolower (SB_Peek (B)) == 'x') {
310 SB_Skip (B);
311 Base = 16;
312 if (!IsXDigit (SB_Peek (B))) {
313 Error ("Invalid hexadecimal number");
314 return 0;
315 }
316 } else {
317 Base = 8;
318 }
319 } else {
320 Base = 10;
321 }
322
323 /* Read the number */
324 while (IsXDigit (C = SB_Peek (B)) && (DigitVal = HexVal (C)) < Base) {
325 *Val = (*Val * Base) + DigitVal;
326 SB_Skip (B);
327 }
328
329 /* Allow optional 'U' and 'L' modifiers */
330 C = SB_Peek (B);
331 if (C == 'u' || C == 'U') {
332 SB_Skip (B);
333 C = SB_Peek (B);
334 if (C == 'l' || C == 'L') {
335 SB_Skip (B);
336 }
337 } else if (C == 'l' || C == 'L') {
338 SB_Skip (B);
339 C = SB_Peek (B);
340 if (C == 'u' || C == 'U') {
341 SB_Skip (B);
342 }
343 }
344
345 /* Success, value read is in Val */
346 *Val *= Sign;
347 return 1;
348}
349