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