/** @file altlex.c An alternate lexer to SDCC.lex. In development - ie messy and just plain wrong. Inspired by the gcc lexer, c-lex.c. */ #include "common.h" #include "reswords.h" #include #define DUMP_OUTPUT 0 /* Right. What are the parts of the C stream? From SDCC.lex: D = [0..9] digits L = [a..z A..Z _] alphanumerics and _ H = [a..f A..F 0-9] Hex digits E = [eE+-0-9] Digits in a float FS = [fFlL] Specifiers for a float IS = [uUlL] Specifiers for a int L[LD]* A 'token' - cant think of a good name Check tokens against the reserved words. If match return the token id. else If in the typedef table, do stuff... Blah. See check_type() 0[xX]{H}+ Hex number - PENDING: specifiers 0{D}+ Octal number - PENDING: specifiers {D}+ Decimal - PENDING: specifiers Floats PENDING Exceptions: Comment start Strip until end of comment. ... Ellipses So the inputs are: Skip whitespace switch class L Try to read a token D Try to read a number Punct Try to read punct */ extern int fatalError; extern int lineno; extern char *filename; FILE *yyin; int mylineno; char *currFname; char *yytext; static char linebuf[10000]; static int linepos, linelen; static int end_of_file; #ifdef __GNUC__ #define INLINE inline #else #define INLINE #endif #define ERRSINK stderr static void error (const char *sz,...) { va_list ap; fatalError++; if (filename && lineno) { fprintf (ERRSINK, "%s(%d):", filename, lineno); } fprintf (ERRSINK, "error *** "); va_start (ap, sz); vfprintf (ERRSINK, sz, ap); va_end (ap); fprintf (ERRSINK, "\n"); fflush (ERRSINK); } static int underflow (void) { linelen = fread (linebuf, 1, sizeof (linebuf), yyin); if (linelen <= 0) return EOF; linepos = 0; return linebuf[linepos++]; } static int INLINE ygetc (void) { if (linepos < linelen) return linebuf[linepos++]; else return underflow (); }; static int INLINE yungetc (int c) { linebuf[--linepos] = c; return 0; } #define GETC() ygetc() #define UNGETC(_a) yungetc(_a) //#define GETC() fgetc(yyin); //#define UNGETC(_a) ungetc(_a, yyin) #define ISL(_a) (isalnum(_a) || _a == '_') #define ISALNUM(_a) isalnum(_a) #define ISHEX(_a) isxdigit(_a) static char * stringLiteral (void) { static char line[1000]; int ch; char *str = line; *str++ = '\"'; /* put into the buffer till we hit the */ /* first \" */ while (1) { ch = GETC (); if (!ch) break; /* end of input */ /* if it is a \ then everything allowed */ if (ch == '\\') { *str++ = ch; /* backslash in place */ *str++ = GETC (); /* following char in place */ continue; /* carry on */ } /* if new line we have a new line break */ if (ch == '\n') break; /* if this is a quote then we have work to do */ /* find the next non whitespace character */ /* if that is a double quote then carry on */ if (ch == '\"') { while ((ch = GETC ()) && isspace (ch)); if (!ch) break; if (ch != '\"') { UNGETC (ch); break; } continue; } *str++ = ch; } *str++ = '\"'; *str = '\0'; return line; } static void discard_comments (int type) { int c; if (type == '*') { do { c = GETC (); if (c == '*') { c = GETC (); if (c == '/') return; } else if (c == EOF) return; } while (1); } else if (type == '/') { do { c = GETC (); } while (c != '\n' && c != EOF); } else { assert (0); } } /* will return 1 if the string is a part of a target specific keyword */ static INLINE int isTargetKeyword (const char *s) { int i; if (port->keywords == NULL) return 0; for (i = 0; port->keywords[i]; i++) { if (strcmp (port->keywords[i], s) == 0) return 1; } return 0; } static INLINE int check_token (const char *sz) { const struct reserved_words *p; p = is_reserved_word (sz, strlen (sz)); if (p) { if (!p->is_special || isTargetKeyword (sz)) return p->token; } /* check if it is in the typedef table */ if (findSym (TypedefTab, NULL, sz)) { strncpyz (yylval.yychar, sz, sizeof(yylval.yychar)); return TYPE_NAME; } else { strncpyz (yylval.yychar, sz, sizeof(yylval.yychar)); return IDENTIFIER; } } static void handle_pragma (void) { char line[128], *p; int c; c = GETC (); while (c == '\t' || c == ' ') c = GETC (); p = line; while (!isspace (c)) { *p++ = c; c = GETC (); } *p = '\0'; if (line[0] == '\0') error ("Missing argument to pragma"); else { /* First give the port a chance */ if (port->process_pragma && !port->process_pragma (line)) return; /* PENDING: all the SDCC shared pragmas */ /* Nothing handled it */ error ("Unrecognised #pragma %s", line); } } static void handle_line (void) { int c; char line[128], *p; c = GETC (); while (c == '\t' || c == ' ') c = GETC (); p = line; while (isdigit (c)) { *p++ = c; c = GETC (); } *p = '\0'; if (line[0] == '\0') error ("Error in number in #line"); /* This is weird but cpp seems to add an extra three to the line no */ mylineno = atoi (line) - 3; lineno = mylineno; /* Fetch the filename if there is one */ while (c == '\t' || c == ' ') c = GETC (); if (c == '\"') { p = line; c = GETC (); while (c != '\"' && c != EOF && c != '\n') { *p++ = c; c = GETC (); } if (c == '\"') { *p = '\0'; currFname = Safe_strdup (line); } filename = currFname; } } static INLINE void invalid_directive (void) { error ("Invalid directive"); } static INLINE int check_newline (void) { int c; mylineno++; lineno = mylineno; /* Skip any leading white space */ c = GETC (); while (c == '\t' || c == ' ') c = GETC (); /* Were only interested in #something */ if (c != '#') return c; c = GETC (); while (c == '\t' || c == ' ') c = GETC (); /* The text in the stream is the type of directive */ switch (c) { case 'l': /* Start of line? */ if (GETC () == 'i' && GETC () == 'n' && GETC () == 'e') { c = GETC (); if (c == '\t' || c == ' ') handle_line (); else invalid_directive (); } else invalid_directive (); break; case 'p': /* Start of pragma? */ if (GETC () == 'r' && GETC () == 'a' && GETC () == 'g' && GETC () == 'm' && GETC () == 'a') { c = GETC (); if (c == '\t' || c == ' ') handle_pragma (); else invalid_directive (); } else invalid_directive (); break; default: invalid_directive (); } /* Discard from here until the start of the next line */ while (c != '\n' && c != EOF) c = GETC (); return c; } static int skip_whitespace (int c) { while (1) { switch (c) { case ' ': case '\t': case '\f': case '\v': case '\b': case '\r': c = GETC (); break; case '\n': c = check_newline (); default: return c; } } } void yyerror (const char *s) { if (end_of_file) error ("%s at end of of input", s); else if (yytext[0] == '\0') error ("%s at null character", s); else if (yytext[0] == '"') error ("%s before string constant", s); else if (yytext[0] == '\'') error ("%s before character constant", s); else error ("%s before %s", s, yytext); } static int _yylex (void) { int c; static char line[128]; char *p; yytext = line; c = GETC (); while (1) { switch (c) { case ' ': case '\t': case '\f': case '\v': case '\b': /* Skip whitespace */ c = GETC (); break; case '\r': case '\n': c = skip_whitespace (c); break; case '#': UNGETC (c); c = check_newline (); break; default: goto past_ws; } } past_ws: /* Handle comments first */ if (c == '/') { int c2 = GETC (); if (c2 == '*' || c2 == '/') { discard_comments (c2); c = GETC (); } else UNGETC (c2); } switch (c) { case EOF: end_of_file = TRUE; line[0] = '\0'; return 0; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': /* Start of a token. Parse. */ p = line; *p++ = c; c = GETC (); while (ISL (c)) { *p++ = c; c = GETC (); } *p = '\0'; UNGETC (c); return check_token (line); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': p = line; *p++ = c; c = GETC (); if (c == 'x' || c == 'X') { *p++ = c; c = GETC (); } while (ISHEX (c)) { *p++ = c; c = GETC (); } if (c == 'U' || c == 'u' || c == 'L' || c == 'l') { *p++ = c; c = GETC (); } if (c == 'U' || c == 'u' || c == 'L' || c == 'l') { *p++ = c; c = GETC (); } *p = '\0'; UNGETC (c); yylval.val = constVal (line); return CONSTANT; case '\"': /* A string */ p = stringLiteral (); yylval.val = strVal (p); return (STRING_LITERAL); case '\'': /* Possible formats: ['\n', '\\', '\'', '\"'...] ['a'...] */ p = line; *p++ = c; c = GETC (); if (c == '\\') { *p++ = c; c = GETC (); /* Fall through */ } *p++ = c; c = GETC (); *p++ = c; *p = '\0'; if (c != '\'') { error ("Unrecognised character constant %s", line); } yylval.val = charVal (line); return CONSTANT; case '=': case '&': case '!': case '-': case '+': case '*': case '/': case '%': case '<': case '>': case '^': case '|': { /* Cases which can be compounds */ /* The types and classes of composites are: >>= <<= += -= *= /= %= &= ^= |= >> << ++ -- && || <= >= == != -> So a composite started by char 'x' can be: 1. Followed by itself then an equals 2. Followed by itself 3. Followed by an equals 4. Be a '->' 5. Be by itself */ int next = GETC (); /* Class 1 and 2 */ if (next == c) { next = GETC (); /* Class 1 */ if (next == '=') { switch (c) { case '>': // >>= yylval.yyint = RIGHT_ASSIGN; return RIGHT_ASSIGN; case '<': // <<= yylval.yyint = LEFT_ASSIGN; return LEFT_ASSIGN; default: error ("Unrecognised token %c%c=", c, c); } } else { /* Push the next char back on and find the class */ UNGETC (next); /* Case 2 */ switch (c) { case '>': // >> return RIGHT_OP; case '<': // << return LEFT_OP; case '+': return INC_OP; case '-': return DEC_OP; case '&': return AND_OP; case '|': return OR_OP; case '=': return EQ_OP; default: error ("Unrecognised token %c%c", c, c); } } } /* Case 3 */ else if (next == '=') { int result = 0; switch (c) { case '+': result = ADD_ASSIGN; break; case '-': result = SUB_ASSIGN; break; case '*': result = MUL_ASSIGN; break; case '/': result = DIV_ASSIGN; break; case '%': result = MOD_ASSIGN; break; case '&': result = AND_ASSIGN; break; case '^': result = XOR_ASSIGN; break; case '|': result = OR_ASSIGN; break; case '<': result = LE_OP; break; case '>': result = GE_OP; break; case '!': result = NE_OP; break; default: error ("Unrecognised token %c=", c); } if (result) { yylval.yyint = result; return result; } } /* Case 4 */ else if (c == '-' && next == '>') { return PTR_OP; } /* Case 5 */ else { UNGETC (next); return c; } break; } case '{': NestLevel++; return c; case '}': NestLevel--; return c; case '.': c = GETC (); if (c == '.') { c = GETC (); if (c == '.') { return VAR_ARGS; } } UNGETC (c); return '.'; case '[': case ']': return c; case ',': case ':': case '(': case ')': case '~': case '?': case ';': /* Special characters that cant be part of a composite */ return c; default: error ("Unhandled character %c", c); } return 0; } #define ENTRY(_a) case (_a): printf(#_a); break; int yylex (void) { int ret = _yylex (); #if DUMP_OUTPUT static int lastpos = 0; char tmp; printf ("Returning "); switch (ret) { /* Wrapper */ ENTRY (IDENTIFIER); ENTRY (TYPE_NAME); ENTRY (CONSTANT); ENTRY (STRING_LITERAL); ENTRY (SIZEOF); ENTRY (PTR_OP); ENTRY (INC_OP); ENTRY (DEC_OP); ENTRY (LEFT_OP); ENTRY (RIGHT_OP); ENTRY (LE_OP); ENTRY (GE_OP); ENTRY (EQ_OP); ENTRY (NE_OP); ENTRY (AND_OP); ENTRY (OR_OP); ENTRY (MUL_ASSIGN); ENTRY (DIV_ASSIGN); ENTRY (MOD_ASSIGN); ENTRY (ADD_ASSIGN); ENTRY (SUB_ASSIGN); ENTRY (LEFT_ASSIGN); ENTRY (RIGHT_ASSIGN); ENTRY (AND_ASSIGN); ENTRY (XOR_ASSIGN); ENTRY (OR_ASSIGN); ENTRY (TYPEDEF); ENTRY (EXTERN); ENTRY (STATIC); ENTRY (AUTO); ENTRY (REGISTER); ENTRY (CODE); ENTRY (EEPROM); ENTRY (INTERRUPT); ENTRY (SFR); ENTRY (AT); ENTRY (SBIT); ENTRY (REENTRANT); ENTRY (USING); ENTRY (XDATA); ENTRY (DATA); ENTRY (IDATA); ENTRY (PDATA); ENTRY (VAR_ARGS); ENTRY (CRITICAL); ENTRY (NONBANKED); ENTRY (BANKED); ENTRY (CHAR); ENTRY (SHORT); ENTRY (INT); ENTRY (LONG); ENTRY (SIGNED); ENTRY (UNSIGNED); ENTRY (FLOAT); ENTRY (FIXED16X16); ENTRY (DOUBLE); ENTRY (CONST); ENTRY (VOLATILE); ENTRY (VOID); ENTRY (BIT); ENTRY (STRUCT); ENTRY (UNION); ENTRY (ENUM); ENTRY (ELIPSIS); ENTRY (RANGE); ENTRY (FAR); ENTRY (_XDATA); ENTRY (_CODE); ENTRY (_GENERIC); ENTRY (_NEAR); ENTRY (_PDATA); ENTRY (_IDATA); ENTRY (_EEPROM); ENTRY (CASE); ENTRY (DEFAULT); ENTRY (IF); ENTRY (ELSE); ENTRY (SWITCH); ENTRY (WHILE); ENTRY (DO); ENTRY (FOR); ENTRY (GOTO); ENTRY (CONTINUE); ENTRY (BREAK); ENTRY (RETURN); ENTRY (INLINEASM); ENTRY (IFX); ENTRY (ADDRESS_OF); ENTRY (GET_VALUE_AT_ADDRESS); ENTRY (SPIL); ENTRY (UNSPIL); ENTRY (GETHBIT); ENTRY (BITWISEAND); ENTRY (UNARYMINUS); ENTRY (IPUSH); ENTRY (IPOP); ENTRY (PCALL); ENTRY (ENDFUNCTION); ENTRY (JUMPTABLE); ENTRY (RRC); ENTRY (RLC); ENTRY (CAST); ENTRY (CALL); ENTRY (PARAM); ENTRY (NULLOP); ENTRY (BLOCK); ENTRY (LABEL); ENTRY (RECEIVE); ENTRY (SEND); default: printf ("default: %c", ret); } tmp = linebuf[linepos]; linebuf[linepos] = '\0'; printf (" for %s (%u bytes)\n", linebuf + lastpos, linepos - lastpos); linebuf[linepos] = tmp; lastpos = linepos; fflush (stdout); #endif return ret; } #define TEST(_a) (_a) ? (void)0 : printf("Test %s failed\n", #_a); int altlex_testparse (const char *input) { /* Fiddle with the read-ahead buffer to insert ourselves */ strncpyz (linebuf, input, sizeof(linebuf)); linelen = strlen (linebuf) + 1; linepos = 0; return yylex (); } int altlex_testchar (const char *input) { value *val; if (altlex_testparse (input) != CONSTANT) return -2; val = yylval.val; if (val->type->class != SPECIFIER) return -3; if (SPEC_NOUN (val->type) != V_CHAR) return -4; if (SPEC_SCLS (val->type) != S_LITERAL) return -5; return SPEC_CVAL (val->type).v_int; } int altlex_testnum (const char *input) { value *val; if (altlex_testparse (input) != CONSTANT) return -2; val = yylval.val; if (val->type->class != SPECIFIER) return -3; if (SPEC_NOUN (val->type) != V_INT) return -4; if (SPEC_SCLS (val->type) != S_LITERAL) return -5; if (SPEC_USIGN (val->type)) return SPEC_CVAL (val->type).v_uint; else return SPEC_CVAL (val->type).v_int; } int altlex_runtests (void) { /* These conditions are ripped directly from SDCC.lex */ /* First check the parsing of the basic tokens */ TEST (altlex_testparse (">>=") == RIGHT_ASSIGN); TEST (altlex_testparse ("<<=") == LEFT_ASSIGN); TEST (altlex_testparse ("+=") == ADD_ASSIGN); TEST (altlex_testparse ("-=") == SUB_ASSIGN); TEST (altlex_testparse ("*=") == MUL_ASSIGN); TEST (altlex_testparse ("/=") == DIV_ASSIGN); TEST (altlex_testparse ("%=") == MOD_ASSIGN); TEST (altlex_testparse ("&=") == AND_ASSIGN); TEST (altlex_testparse ("^=") == XOR_ASSIGN); TEST (altlex_testparse ("|=") == OR_ASSIGN); TEST (altlex_testparse (">>") == RIGHT_OP); TEST (altlex_testparse ("<<") == LEFT_OP); TEST (altlex_testparse ("++") == INC_OP); TEST (altlex_testparse ("--") == DEC_OP); TEST (altlex_testparse ("->") == PTR_OP); TEST (altlex_testparse ("&&") == AND_OP); TEST (altlex_testparse ("||") == OR_OP); TEST (altlex_testparse ("<=") == LE_OP); TEST (altlex_testparse (">=") == GE_OP); TEST (altlex_testparse ("==") == EQ_OP); TEST (altlex_testparse ("!=") == NE_OP); TEST (altlex_testparse (";") == ';'); TEST (altlex_testparse ("{") == '{'); TEST (altlex_testparse ("}") == '}'); TEST (altlex_testparse (",") == ','); TEST (altlex_testparse (":") == ':'); TEST (altlex_testparse ("=") == '='); TEST (altlex_testparse ("(") == '('); TEST (altlex_testparse (")") == ')'); TEST (altlex_testparse ("[") == '['); TEST (altlex_testparse ("]") == ']'); TEST (altlex_testparse (".") == '.'); TEST (altlex_testparse ("&") == '&'); TEST (altlex_testparse ("!") == '!'); TEST (altlex_testparse ("~") == '~'); TEST (altlex_testparse ("-") == '-'); TEST (altlex_testparse ("+") == '+'); TEST (altlex_testparse ("*") == '*'); TEST (altlex_testparse ("/") == '/'); TEST (altlex_testparse ("%") == '%'); TEST (altlex_testparse ("<") == '<'); TEST (altlex_testparse (">") == '>'); TEST (altlex_testparse ("^") == '^'); TEST (altlex_testparse ("|") == '|'); TEST (altlex_testparse ("?") == '?'); /* Now some character constants */ TEST (altlex_testchar ("'1'") == '1'); TEST (altlex_testchar ("'a'") == 'a'); TEST (altlex_testchar ("'A'") == 'A'); TEST (altlex_testchar ("'z'") == 'z'); TEST (altlex_testchar ("'Z'") == 'Z'); TEST (altlex_testchar ("'\n'") == '\n'); TEST (altlex_testchar ("'\\\\'") == '\\'); TEST (altlex_testchar ("'\\''") == '\''); /* And some numbers */ TEST (altlex_testnum ("0") == 0); TEST (altlex_testnum ("1") == 1); TEST (altlex_testnum ("075") == 075); TEST (altlex_testnum ("0xfeed") == 0xfeed); TEST (altlex_testnum ("0xFEED") == 0xFEED); TEST (altlex_testnum ("0x00005678") == 0x5678); /* Keywords */ TEST (altlex_testparse ("auto") == AUTO); TEST (altlex_testparse ("break") == BREAK); TEST (altlex_testparse ("case") == CASE); TEST (altlex_testparse ("char") == CHAR); TEST (altlex_testparse ("const") == CONST); TEST (altlex_testparse ("continue") == CONTINUE); TEST (altlex_testparse ("default") == DEFAULT); TEST (altlex_testparse ("do") == DO); /* Prints a warning */ // TEST(altlex_testparse("double") == FLOAT); TEST (altlex_testparse ("else") == ELSE); TEST (altlex_testparse ("enum") == ENUM); TEST (altlex_testparse ("extern") == EXTERN); TEST (altlex_testparse ("float") == FLOAT); TEST (altlex_testparse ("fixed16x16") == FIXED16X16); TEST (altlex_testparse ("for") == FOR); TEST (altlex_testparse ("goto") == GOTO); TEST (altlex_testparse ("if") == IF); TEST (altlex_testparse ("int") == INT); TEST (altlex_testparse ("interrupt") == INTERRUPT); TEST (altlex_testparse ("long") == LONG); TEST (altlex_testparse ("register") == REGISTER); TEST (altlex_testparse ("return") == RETURN); TEST (altlex_testparse ("short") == SHORT); TEST (altlex_testparse ("signed") == SIGNED); TEST (altlex_testparse ("sizeof") == SIZEOF); TEST (altlex_testparse ("static") == STATIC); TEST (altlex_testparse ("struct") == STRUCT); TEST (altlex_testparse ("switch") == SWITCH); TEST (altlex_testparse ("typedef") == TYPEDEF); TEST (altlex_testparse ("union") == UNION); TEST (altlex_testparse ("unsigned") == UNSIGNED); TEST (altlex_testparse ("void") == VOID); TEST (altlex_testparse ("volatile") == VOLATILE); TEST (altlex_testparse ("while") == WHILE); TEST (altlex_testparse ("...") == VAR_ARGS); #if 0 /* Platform specific keywords */ TEST (altlex_testparse ("sram") ==) { count (); TKEYWORD (XDATA); } TEST (altlex_testparse ("using") ==) { count (); TKEYWORD (USING); } TEST (altlex_testparse ("near") ==) { count (); TKEYWORD (DATA); } TEST (altlex_testparse ("at") ==) { count (); TKEYWORD (AT); } TEST (altlex_testparse ("bit") ==) { count (); TKEYWORD (BIT); } TEST (altlex_testparse ("code") ==) { count (); TKEYWORD (CODE); } TEST (altlex_testparse ("critical") ==) { count (); TKEYWORD (CRITICAL); } TEST (altlex_testparse ("data") ==) { count (); TKEYWORD (DATA); } TEST (altlex_testparse ("far") ==) { count (); TKEYWORD (XDATA); } TEST (altlex_testparse ("eeprom") ==) { count (); TKEYWORD (EEPROM); } TEST (altlex_testparse ("flash") ==) { count (); TKEYWORD (CODE); } TEST (altlex_testparse ("idata") ==) { count (); TKEYWORD (IDATA); } TEST (altlex_testparse ("nonbanked") ==) { count (); TKEYWORD (NONBANKED); } TEST (altlex_testparse ("banked") ==) { count (); TKEYWORD (BANKED); } TEST (altlex_testparse ("pdata") ==) { count (); TKEYWORD (PDATA); } TEST (altlex_testparse ("reentrant") ==) { count (); TKEYWORD (REENTRANT); } TEST (altlex_testparse ("sfr") ==) { count (); TKEYWORD (SFR); } TEST (altlex_testparse ("sbit") ==) { count (); TKEYWORD (SBIT); } TEST (altlex_testparse ("xdata") ==) { count (); TKEYWORD (XDATA); } TEST (altlex_testparse ("_data") ==) { count (); TKEYWORD (_NEAR); } TEST (altlex_testparse ("_code") ==) { count (); TKEYWORD (_CODE); } TEST (altlex_testparse ("_eeprom") ==) { count (); TKEYWORD (_EEPROM); } TEST (altlex_testparse ("_flash") ==) { count (); TKEYWORD (_CODE); } TEST (altlex_testparse ("_generic") ==) { count (); TKEYWORD (_GENERIC); } TEST (altlex_testparse ("_near") ==) { count (); TKEYWORD (_NEAR); } TEST (altlex_testparse ("_sram") ==) { count (); TKEYWORD (_XDATA); } TEST (altlex_testparse ("_xdata") ==) { count (); TKEYWORD (_XDATA); } TEST (altlex_testparse ("_pdata") ==) { count (); TKEYWORD (_PDATA); } TEST (altlex_testparse ("_idata") ==) { count (); TKEYWORD (_IDATA); } #endif return 0; }