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 | |
55 | static 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 | |
155 | void 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 | |
165 | int 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 | |
199 | int 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 | |
251 | int 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 | |