]> git.sur5r.net Git - cc65/blob - src/ca65/nexttok.c
Use the CHECK macros from the common dir
[cc65] / src / ca65 / nexttok.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 nexttok.c                                 */
4 /*                                                                           */
5 /*              Get next token and handle token level functions              */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 2000     Ullrich von Bassewitz                                        */
10 /*              Wacholderweg 14                                              */
11 /*              D-70597 Stuttgart                                            */
12 /* EMail:       uz@musoftware.de                                             */
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 #include <stdio.h>
37
38 /* common */
39 #include "check.h"
40                    
41 /* ca65 */
42 #include "error.h"
43 #include "expr.h"
44 #include "scanner.h"
45 #include "toklist.h"
46 #include "nexttok.h"
47
48
49
50 /*****************************************************************************/
51 /*                                   Data                                    */
52 /*****************************************************************************/
53
54
55
56 static unsigned RawMode = 0;            /* Raw token mode flag/counter */
57
58
59
60 /*****************************************************************************/
61 /*                                   Code                                    */
62 /*****************************************************************************/
63
64
65
66 static TokList* CollectTokens (unsigned Start, unsigned Count)
67 /* Read a list of tokens that is terminated by a right paren. For all tokens
68  * starting at the one with index Start, and ending at (Start+Count-1), place
69  * them into a token list, and return this token list.
70  */
71 {
72     /* Create the token list */
73     TokList* List = NewTokList ();
74
75     /* Read the token list */
76     unsigned Current = 0;
77     unsigned Parens  = 0;
78     while (Parens != 0 || Tok != TOK_RPAREN) {
79
80         /* Check for end of line or end of input */
81         if (Tok == TOK_SEP || Tok == TOK_EOF) {
82             Error (ERR_UNEXPECTED_EOL);
83             return List;
84         }
85
86         /* Collect tokens in the given range */
87         if (Current >= Start && Current < Start+Count) {
88             /* Add the current token to the list */
89             AddCurTok (List);
90         }
91
92         /* Check for and count parenthesii */
93         if (Tok == TOK_LPAREN) {
94             ++Parens;
95         } else if (Tok == TOK_RPAREN) {
96             --Parens;
97         }
98
99         /* Get the next token */
100         ++Current;
101         NextTok ();
102     }
103
104     /* Eat the closing paren */
105     ConsumeRParen ();
106
107     /* Return the list of collected tokens */
108     return List;
109 }
110
111
112
113 static void FuncConcat (void)
114 /* Handle the .CONCAT function */
115 {
116     char        Buf[MAX_STR_LEN+1];
117     char*       B;
118     unsigned    Length;
119     unsigned    L;
120
121     /* Skip it */
122     NextTok ();
123
124     /* Left paren expected */
125     ConsumeLParen ();
126
127     /* Concatenate any number of strings */
128     B = Buf;
129     B[0] = '\0';
130     Length = 0;
131     while (1) {
132
133         /* Next token must be a string */
134         if (Tok != TOK_STRCON) {
135             Error (ERR_STRCON_EXPECTED);
136             SkipUntilSep ();
137             return;
138         }
139
140         /* Get the length of the string const and check total length */
141         L = strlen (SVal);
142         if (Length + L > MAX_STR_LEN) {
143             Error (ERR_STRING_TOO_LONG);
144             /* Try to recover */
145             SkipUntilSep ();
146             return;
147         }
148
149         /* Add the new string */
150         memcpy (B, SVal, L);
151         Length += L;
152         B      += L;
153
154         /* Skip the string token */
155         NextTok ();
156
157         /* Comma means another argument */
158         if (Tok == TOK_COMMA) {
159             NextTok ();
160         } else {
161             /* Done */
162             break;
163         }
164     }
165
166     /* Terminate the string */
167     *B = '\0';
168
169     /* We expect a closing parenthesis, but will not skip it but replace it
170      * by the string token just created.
171      */
172     if (Tok != TOK_RPAREN) {
173         Error (ERR_RPAREN_EXPECTED);
174     } else {
175         Tok = TOK_STRCON;
176         strcpy (SVal, Buf);
177     }
178 }
179
180
181
182 static void FuncLeft (void)
183 /* Handle the .LEFT function */
184 {
185     long        Count;
186     TokList*    List;
187
188     /* Skip it */
189     NextTok ();
190
191     /* Left paren expected */
192     ConsumeLParen ();
193
194     /* Count argument */
195     Count = ConstExpression ();
196     if (Count < 0 || Count > 100) {
197         Error (ERR_RANGE);
198         Count = 1;
199     }
200     ConsumeComma ();
201
202     /* Read the token list */
203     List = CollectTokens (0, (unsigned) Count);
204
205     /* Since we want to insert the list before the now current token, we have
206      * to save the current token in some way and then skip it. To do this, we
207      * will add the current token at the end of the token list (so the list
208      * will never be empty), push the token list, and then skip the current
209      * token. This will replace the current token by the first token from the
210      * list (which will be the old current token in case the list was empty).
211      */
212     AddCurTok (List);
213
214     /* Insert it into the scanner feed */
215     PushTokList (List, ".LEFT");
216
217     /* Skip the current token */
218     NextTok ();
219 }
220
221
222
223 static void FuncMid (void)
224 /* Handle the .MID function */
225 {
226     long        Start;
227     long        Count;
228     TokList*    List;
229
230     /* Skip it */
231     NextTok ();
232
233     /* Left paren expected */
234     ConsumeLParen ();
235
236     /* Start argument */
237     Start = ConstExpression ();
238     if (Start < 0 || Start > 100) {
239         Error (ERR_RANGE);
240         Start = 0;
241     }
242     ConsumeComma ();
243
244     /* Count argument */
245     Count = ConstExpression ();
246     if (Count < 0 || Count > 100) {
247         Error (ERR_RANGE);
248         Count = 1;
249     }
250     ConsumeComma ();
251
252     /* Read the token list */
253     List = CollectTokens ((unsigned) Start, (unsigned) Count);
254
255     /* Since we want to insert the list before the now current token, we have
256      * to save the current token in some way and then skip it. To do this, we
257      * will add the current token at the end of the token list (so the list
258      * will never be empty), push the token list, and then skip the current
259      * token. This will replace the current token by the first token from the
260      * list (which will be the old current token in case the list was empty).
261      */
262     AddCurTok (List);
263
264     /* Insert it into the scanner feed */
265     PushTokList (List, ".MID");
266
267     /* Skip the current token */
268     NextTok ();
269 }
270
271
272
273 static void FuncRight (void)
274 /* Handle the .RIGHT function */
275 {
276     long        Count;
277     TokList*    List;
278
279     /* Skip it */
280     NextTok ();
281
282     /* Left paren expected */
283     ConsumeLParen ();
284
285     /* Count argument */
286     Count = ConstExpression ();
287     if (Count < 0 || Count > 100) {
288         Error (ERR_RANGE);
289         Count = 1;
290     }
291     ConsumeComma ();
292
293     /* Read the complete token list */
294     List = CollectTokens (0, 9999);
295
296     /* Delete tokens from the list until Count tokens are remaining */
297     while (List->Count > Count) {
298         /* Get the first node */
299         TokNode* T = List->Root;
300
301         /* Remove it from the list */
302         List->Root = List->Root->Next;
303
304         /* Free the node */
305         FreeTokNode (T);
306
307         /* Corrent the token counter */
308         List->Count--;
309     }
310
311     /* Since we want to insert the list before the now current token, we have
312      * to save the current token in some way and then skip it. To do this, we
313      * will add the current token at the end of the token list (so the list
314      * will never be empty), push the token list, and then skip the current
315      * token. This will replace the current token by the first token from the
316      * list (which will be the old current token in case the list was empty).
317      */
318     AddCurTok (List);
319
320     /* Insert it into the scanner feed */
321     PushTokList (List, ".RIGHT");
322
323     /* Skip the current token */
324     NextTok ();
325 }
326
327
328
329 static void FuncString (void)
330 /* Handle the .STRING function */
331 {
332     char Buf[MAX_STR_LEN+1];
333
334     /* Skip it */
335     NextTok ();
336
337     /* Left paren expected */
338     ConsumeLParen ();
339
340     /* Accept identifiers or numeric expressions */
341     if (Tok == TOK_IDENT) {
342         /* Save the identifier, then skip it */
343         strcpy (Buf, SVal);
344         NextTok ();
345     } else {
346         /* Numeric expression */
347         long Val = ConstExpression ();
348         sprintf (Buf, "%ld", Val);
349     }
350
351     /* We expect a closing parenthesis, but will not skip it but replace it
352      * by the string token just created.
353      */
354     if (Tok != TOK_RPAREN) {
355         Error (ERR_RPAREN_EXPECTED);
356     } else {
357         Tok = TOK_STRCON;
358         strcpy (SVal, Buf);
359     }
360 }
361
362
363
364 void NextTok (void)
365 /* Get next token and handle token level functions */
366 {
367     /* Get the next raw token */
368     NextRawTok ();
369
370     /* In raw mode, pass the token unchanged */
371     if (RawMode == 0) {
372
373         /* Execute token handling functions */
374         switch (Tok) {
375
376             case TOK_CONCAT:
377                 FuncConcat ();
378                 break;
379
380             case TOK_LEFT:
381                 FuncLeft ();
382                 break;
383
384             case TOK_MID:
385                 FuncMid ();
386                 break;
387
388             case TOK_RIGHT:
389                 FuncRight ();
390                 break;
391
392             case TOK_STRING:
393                 FuncString ();
394                 break;
395
396             default:
397                 /* Quiet down gcc */
398                 break;
399
400         }
401     }
402 }
403
404
405
406 void Consume (enum Token Expected, unsigned ErrMsg)
407 /* Consume Expected, print an error if we don't find it */
408 {
409     if (Tok == Expected) {
410         NextTok ();
411     } else {
412         Error (ErrMsg);
413     }
414 }
415
416
417
418 void ConsumeSep (void)
419 /* Consume a separator token */
420 {
421     /* Accept an EOF as separator */
422     if (Tok != TOK_EOF) {
423         if (Tok != TOK_SEP) {
424             Error (ERR_TOO_MANY_CHARS);
425             SkipUntilSep ();
426         } else {
427             NextTok ();
428         }
429     }
430 }
431
432
433
434 void ConsumeLParen (void)
435 /* Consume a left paren */
436 {
437     Consume (TOK_LPAREN, ERR_LPAREN_EXPECTED);
438 }
439
440
441
442 void ConsumeRParen (void)
443 /* Consume a right paren */
444 {
445     Consume (TOK_RPAREN, ERR_RPAREN_EXPECTED);
446 }
447
448
449
450 void ConsumeComma (void)
451 /* Consume a comma */
452 {
453     Consume (TOK_COMMA, ERR_COMMA_EXPECTED);
454 }
455
456
457
458 void SkipUntilSep (void)
459 /* Skip tokens until we reach a line separator or end of file */
460 {
461     while (Tok != TOK_SEP && Tok != TOK_EOF) {
462         NextTok ();
463     }
464 }
465
466
467
468 void EnterRawTokenMode (void)
469 /* Enter raw token mode. In raw mode, token handling functions are not
470  * executed, but the function tokens are passed untouched to the upper
471  * layer. Raw token mode is used when storing macro tokens for later
472  * use.
473  * Calls to EnterRawTokenMode and LeaveRawTokenMode may be nested.
474  */
475 {
476     ++RawMode;
477 }
478
479
480
481 void LeaveRawTokenMode (void)
482 /* Leave raw token mode. */
483 {
484     PRECONDITION (RawMode > 0);
485     --RawMode;
486 }
487
488
489