1 /*
2  * Motif Tools Library, Version 3.1
3  * $Id: Lexer.c,v 1.3 2001/09/19 02:57:18 grmcdorman Exp $
4  *
5  * Written by David Flanagan.
6  * Copyright (c) 1992-2001 by David Flanagan.
7  * All Rights Reserved.  See the file COPYRIGHT for details.
8  * This is open source software.  See the file LICENSE for details.
9  * There is no warranty for this software.  See NO_WARRANTY for details.
10  *
11  * $Log: Lexer.c,v $
12  * Revision 1.3  2001/09/19 02:57:18  grmcdorman
13  * This change makes the following modifications:
14  *   A new program, printConfig, is provided. This is built by a
15  *   simple rule in the Makefile and not installed. It prints
16  *   significant defines from Xmt.tmpl.
17  *
18  *   XmtP.h is now generated from XmtP.h.in using printConfig. As
19  *   a result, code compiled outside of the Xmt Imakefiles will
20  *   have all of the Xmt.tmpl defines.
21  *
22  *   Source files are modified to use XmtP.h instead of Xmt.h.
23  *
24  *   WorkingBox.c is modified to use the new Progress widget.
25  *   It can be compiled in the old style if WORKINGBOX_USE_SCALE is
26  *   defined at compile time.
27  *
28  *   Because XmtP.h is generated dynamically, it is removed from CVS
29  *   with this check-in.
30  *
31  * Revision 1.2  2001/05/17 03:58:07  motiftools
32  * Added patch to lexer for handling c++ class::method() scoping rules if
33  * enabled with XMT_HAS_XMTPP flag. Credit goes to the authors of Xmt++ for
34  * lexer code -- Bin Mu and Harry Danilevsky.
35  *
36  * Revision 1.1.1.1  2001/02/10 13:47:18  motiftools
37  * Initial import of Xmt310 to CVS
38  *
39  *
40  */
41 
42 #include <ctype.h>
43 #include <Xmt/XmtP.h>
44 #include <Xmt/Lexer.h>
45 
46 #if NeedFunctionPrototypes
XmtLexerCreate(String * keywords,int num_keywords)47 XmtLexer XmtLexerCreate(String *keywords, int num_keywords)
48 #else
49 XmtLexer XmtLexerCreate(keywords, num_keywords)
50 String *keywords;
51 int num_keywords;
52 #endif
53 {
54     XmtLexer lexer = XtNew(XmtLexerRec);
55 
56     lexer->c = NULL;
57     lexer->token = XmtLexerNone;
58     lexer->intval = 0;
59     lexer->strval = NULL;
60     lexer->keywords = keywords;
61     lexer->num_keywords = num_keywords;
62     return lexer;
63 }
64 
65 #if NeedFunctionPrototypes
XmtLexerDestroy(XmtLexer lexer)66 void XmtLexerDestroy(XmtLexer lexer)
67 #else
68 void XmtLexerDestroy(lexer)
69 XmtLexer lexer;
70 #endif
71 {
72     XtFree((char *)lexer);
73 }
74 
75 #if NeedFunctionPrototypes
XmtLexerInit(XmtLexer lexer,StringConst str)76 void XmtLexerInit(XmtLexer lexer, StringConst str)
77 #else
78 void XmtLexerInit(lexer, str)
79 XmtLexer lexer;
80 StringConst str;
81 #endif
82 {
83     lexer->c = str;
84     lexer->token = XmtLexerNone;
85     lexer->intval = 0;
86     lexer->strval = NULL;
87 }
88 
89 #if NeedFunctionPrototypes
_XmtLexerGetToken(XmtLexer l)90 XmtLexerToken _XmtLexerGetToken(XmtLexer l)
91 #else
92 XmtLexerToken _XmtLexerGetToken(l)
93 XmtLexer l;
94 #endif
95 {
96     int total;
97     int len;
98     _Xconst char *mark;
99     int keyword;
100 
101     l->token = XmtLexerNone;
102 
103     if (l->c == NULL) {
104 	l->token = XmtLexerEndOfString;
105 	return l->token;
106     }
107 
108     /* skip whitespace */
109     while (isspace((int)*l->c)) l->c++;
110 
111     /* check for end-of-string */
112     if (*l->c == '\0') {
113 	l->token = XmtLexerEndOfString;
114 	return l->token;
115     }
116 
117     /* check punctuation */
118     switch (*l->c) {
119     case '(':  l->token = XmtLexerLParen; break;
120     case ')':  l->token = XmtLexerRParen; break;
121     case '[':  l->token = XmtLexerLBracket; break;
122     case ']':  l->token = XmtLexerRBracket; break;
123     case '{':  l->token = XmtLexerLBrace; break;
124     case '}':  l->token = XmtLexerRBrace; break;
125     case '<':  l->token = XmtLexerLess; break;
126     case '>':  l->token = XmtLexerGreater; break;
127     case '+':  l->token = XmtLexerPlus; break;
128     case '-':  l->token = XmtLexerMinus; break;
129     case '*':  l->token = XmtLexerAsterisk; break;
130     case '/':  l->token = XmtLexerSlash; break;
131     case '|':  l->token = XmtLexerBar; break;
132     case '=':  l->token = XmtLexerEqual; break;
133     case '#':  l->token = XmtLexerSharp; break;
134     case '~':  l->token = XmtLexerTwiddle; break;
135     case '%':  l->token = XmtLexerPercent; break;
136     case '$':  l->token = XmtLexerDollar; break;
137     case '.':  l->token = XmtLexerPeriod; break;
138     case '^':  l->token = XmtLexerCaret; break;
139     case ':':  l->token = XmtLexerColon; break;
140     case ';':  l->token = XmtLexerSemicolon; break;
141     case ',':  l->token = XmtLexerComma; break;
142     }
143 
144     if (l->token != XmtLexerNone) {
145 #ifdef XMT_HAS_XMTPP
146 	if (!(l->token != XmtLexerNone) ||
147 	    !(*(l->c+1) == ':')) {
148 	    l->c++;
149 	}
150 #else
151 	l->c++;
152 #endif
153 	return l->token;
154     }
155 
156     if (isdigit((int)*l->c)) {
157 	total = 0;
158 	while (isdigit((int)*l->c)) {
159 	    total *= 10;
160 	    total += (int)(*l->c - '0');
161 	    l->c++;
162 	}
163 	l->token = XmtLexerInteger;
164 	l->intval = total;
165     }
166     else if (*l->c == '"') { /* its a string */
167 	l->c++;
168 	mark = l->c;
169 	while((*l->c != '\0') && (*l->c != '"')) {
170 	    if ((*l->c == '\\') && (*(l->c+1) != '\0'))
171 		l->c++;
172 	    l->c++;
173 	}
174 	if (*l->c == '\0') {
175 	    XmtWarningMsg("XmtLexer", "badString",
176 			  "unterminated string.");
177 	    l->token = XmtLexerError;
178 	}
179 	else {
180 	    len = l->c - mark;
181 	    l->c++;
182 	    l->strval = XtMalloc(len+1);
183 	    strncpy(l->strval, mark, len);
184 	    l->strval[len] = '\0';
185 	    l->intval = len;
186 	    l->token = XmtLexerString;
187 	}
188     }
189     else if (isalnum(*l->c) || (*l->c == '_')) {
190 	/* its an ident.  Figure out how long and copy it. */
191 	mark = l->c;
192 #ifdef XMT_HAS_XMTPP
193         for(len=0; *l->c; len++, l->c++) {
194             if (isalnum((int)*l->c) || (*l->c == '_')) {
195                 continue;
196             } else if ((*l->c == ':') && ( *(l->c+1) == ':')) {
197                 len++;
198                 l->c++;
199             }
200             else break;
201         }
202 #else
203 	for(len=0; *l->c &&
204 		(isalnum((int)*l->c) || (*l->c == '_'));
205 	    len++,l->c++);
206 #endif
207 	l->strval = XtMalloc(len+1);
208 	strncpy(l->strval, mark, len);
209 	l->strval[len] = '\0';
210 	l->intval = len;
211 
212 	/* now go see if it is a keyword. */
213 	keyword = XmtBSearch(l->strval, l->keywords, l->num_keywords);
214 
215 	if (keyword != -1) {
216 	    l->intval = keyword;
217 	    l->token = XmtLexerKeyword;
218 	    XtFree(l->strval);
219 	    l->strval = l->keywords[keyword];
220 	}
221 	else
222 	    l->token = XmtLexerIdent;
223     }
224     else {  /* otherwise it is an unrecognized character. */
225 	XmtWarningMsg("XmtLexer", "badChar",
226 		      "unrecognized character `%c'.",
227 		      *l->c);
228 	l->c++;
229 	l->token = XmtLexerError;
230     }
231 
232     return l->token;
233 }
234 
235 /*
236  * Scan from the current lexer location until an unquoted character
237  * from the delimiters string is encountered.  Copy the string and return
238  * XmtLexerString.  If include is True, include the delimiter, otherwise
239  * don't.  If the end of string is found return XmtLexerEndOfString.
240  */
241 
242 #if NeedFunctionPrototypes
XmtLexerScan(XmtLexer l,StringConst delimiters,XmtWideBoolean include)243 XmtLexerToken XmtLexerScan(XmtLexer l, StringConst delimiters,
244 			   XmtWideBoolean include)
245 #else
246 XmtLexerToken XmtLexerScan(l, delimiters, include)
247 XmtLexer l;
248 StringConst delimiters;
249 int include;
250 #endif
251 {
252     _Xconst char *c, *d;
253     Boolean instring = False;
254 
255     if (l->c == '\0') {
256 	l->token = XmtLexerEndOfString;
257 	return XmtLexerEndOfString;
258     }
259 
260     for(c = l->c; *c; c++) {
261     	if (!instring) {
262 	    for(d = delimiters; *d && (*d != *c); d++);
263 	    if (*d) break;
264 	}
265 	if ((*c == '\\') && *(c+1)) c++;
266 	else if (*c == '"') instring = !instring;
267     }
268 
269     if (*c == '\0') {
270 	l->c = c;
271 	l->token = XmtLexerEndOfString;
272 	return XmtLexerEndOfString;
273     }
274 
275     /*
276      * if include is True, we include the terminating delimiter.
277      */
278     if (include) c++;
279 
280     l->intval = c - l->c;
281     l->strval = XtMalloc(l->intval +1);
282     strncpy(l->strval, l->c, l->intval);
283     l->strval[l->intval] = '\0';
284     l->c = c;
285     l->token = XmtLexerString;
286     return XmtLexerString;
287 }
288 
289 #if NeedFunctionPrototypes
GetArg(XmtLexer l)290 static String GetArg(XmtLexer l)
291 #else
292 static String GetArg(l)
293 XmtLexer l;
294 #endif
295 {
296     _Xconst char *c, *s;
297     register char *t;
298     Boolean instring;
299     int parenlevel;
300     int len;
301     char *arg;
302 
303     /*
304      * This procedure breaks the XmtLexer abstraction and scans the
305      * string directly.  It should only be called when the last token
306      * has been consumed.
307      */
308 
309     /* skip whitespace */
310     XmtLexerSkipWhite(l);
311 
312     /* special case for procedures with 0 args */
313     if (*l->c == ')') {
314 	return NULL;
315     }
316 
317     /* scan to an unquoted, unnested comma or right paren */
318     instring = False;
319     parenlevel = 0;
320     for(c = l->c; *c; c++) {
321 	if (*c == '\0') break;
322 	else if (!instring && parenlevel<=0 && (*c == ',' || *c == ')')) break;
323 	else if (*c == '"') instring = !instring;
324 	else if (!instring && *c == '(') parenlevel++;
325 	else if (!instring && *c == ')') parenlevel--;
326 	else if ((*c == '\\') && (*(c+1) != '\0')) c++;
327     }
328 
329     /* back up over any whitespace */
330     while(isspace(*(c-1))) c--;
331 
332     /* allocate space for a copy of the arg */
333     len = (int) (c - l->c);
334     arg = XtMalloc(len+1);
335 
336     /* copy it, unquoting things */
337     for(s = l->c, t = arg; s < c; s++) {
338 	if (*s == '\\') *t++ = *++s;
339 	else *t++ = *s;
340     }
341 
342     /* and terminate the string */
343     *t = '\0';
344 
345     /* put the lexer back in a known state */
346     l->c = c;
347     l->token = XmtLexerNone;
348 
349     return arg;
350 }
351 
352 #if NeedFunctionPrototypes
XmtLexerGetArgList(XmtLexer l,String * args,Cardinal max_args,Cardinal * num_args)353 Boolean XmtLexerGetArgList(XmtLexer l, String *args,
354 			   Cardinal max_args, Cardinal *num_args)
355 #else
356 Boolean XmtLexerGetArgList(l, args, max_args, num_args)
357 XmtLexer l;
358 String *args;
359 Cardinal max_args;
360 Cardinal *num_args;
361 #endif
362 {
363     XmtLexerToken tok;
364 
365     /* if no '(', then no arguments, but this is not an error. */
366     if (XmtLexerGetToken(l) != XmtLexerLParen) {
367 	*num_args = 0;
368 	return True;
369     }
370 
371     /* otherwise eat the lparen */
372     XmtLexerConsumeToken(l);
373 
374     /* now loop through the arguments */
375     *num_args = 0;
376     while(1) {
377 	/* make sure we haven't overflowed */
378 	if (*num_args >= max_args) {
379 	    XmtWarningMsg("_XmtParseArgList", "tooMany",
380 			  "too many arguments in argument list.");
381 	    goto error;
382 	}
383 
384 	/* get the next argument */
385 	args[*num_args] = GetArg(l);
386 	if (args[*num_args]) *num_args += 1;
387 
388 	/* consume a comma or a right paren */
389 	tok = XmtLexerGetToken(l);
390 	if (tok == XmtLexerRParen) {
391 	    XmtLexerConsumeToken(l);
392 	    return True;
393 	}
394 	else if (tok == XmtLexerComma)
395 	    XmtLexerConsumeToken(l);
396 	else {
397 	    XmtWarningMsg("_XmtParseArgList", "syntax",
398 			  "syntax error in argument list");
399 	    goto error;
400 	}
401     }
402 
403  error:
404     /* read past closing ')' */
405     while(((tok = XmtLexerGetToken(l)) != XmtLexerEndOfString) &&
406 	  (tok != XmtLexerRParen))
407 	XmtLexerConsumeToken(l);
408     XmtLexerConsumeToken(l);
409     return False;
410 }
411