home *** CD-ROM | disk | FTP | other *** search
/ hobbes.nmsu.edu 2008 / 2008-06-02_hobbes.nmsu.edu.zip / new / scummc-0.2.0-os2.zip / ScummC / src / scc_lexer.c < prev    next >
Encoding:
C/C++ Source or Header  |  2007-11-27  |  19.1 KB  |  692 lines

  1. /* ScummC
  2.  * Copyright (C) 2005-2006  Alban Bedel
  3.  *
  4.  * This program is free software; you can redistribute it and/or
  5.  * modify it under the terms of the GNU General Public License
  6.  * as published by the Free Software Foundation; either version 2
  7.  * of the License, or (at your option) any later version.
  8.  
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  17.  *
  18.  */
  19.  
  20. /** 
  21.  * @file scc_lexer.c
  22.  * @ingroup scc lex
  23.  * @brief ScummC lexer.
  24.  */
  25.  
  26. #include "config.h"
  27.  
  28. #include <stdlib.h>
  29. #include <inttypes.h>
  30. #include <string.h>
  31.  
  32. #include "scc_util.h"
  33. #include "scc_parse.h"
  34. #include "scc_parse.tab.h"
  35. #include "scc_lex.h"
  36.  
  37.  
  38. // List of all the keywords
  39. // it must be kept sorted bcs a binary search is used on it
  40. static scc_keyword_t scc_keywords[] = {
  41.     { "actor",      ACTOR,     -1 },
  42.     { "bit",        TYPE,      SCC_VAR_BIT },
  43.     { "break",      BRANCH,    SCC_BRANCH_BREAK },
  44.     { "byte",       TYPE,      SCC_VAR_BYTE },
  45.     { "case",       CASE,      -1 },
  46.     { "char",       TYPE,      SCC_VAR_CHAR },
  47.     { "chset",      RESTYPE,   SCC_RES_CHSET },
  48.     { "class",      CLASS,     -1 },
  49.     { "continue",   BRANCH,    SCC_BRANCH_CONTINUE },
  50.     { "cost",       RESTYPE,   SCC_RES_COST },
  51.     { "cutscene",   CUTSCENE,  -1 },
  52.     { "cycle",      CYCL,      -1 },
  53.     { "default",    DEFAULT,   -1 },
  54.     { "do",         DO,        -1 },
  55.     { "else",       ELSE,      -1 },
  56.     { "for",        FOR,       -1 },
  57.     { "global",     SCRTYPE,   SCC_RES_SCR },
  58.     { "if",         IF,        0 },
  59.     { "int",        TYPE,      SCC_VAR_WORD },
  60.     { "is",         IS,        -1 },
  61.     { "local",      SCRTYPE,   SCC_RES_LSCR },
  62.     { "nibble",     TYPE,      SCC_VAR_NIBBLE },
  63.     { "object",     OBJECT,    -1 },
  64.     { "override",   OVERRIDE,  -1 },
  65.     { "return",     RETURN,    SCC_BRANCH_RETURN },
  66.     { "room",       ROOM,      -1 },
  67.     { "script",     SCRIPT,    -1 },
  68.     { "sound",      RESTYPE,   SCC_RES_SOUND },
  69.     { "switch",     SWITCH,    -1 },
  70.     { "try",        TRY,       -1 },
  71.     { "unless",     IF,        1 },
  72.     { "until",      WHILE,     1 },
  73.     { "verb",       VERB,     -1 },
  74.     { "voice",      VOICE,     -1 },
  75.     { "while",      WHILE,     0 },
  76.     { "word",       TYPE,      SCC_VAR_WORD },
  77.     { NULL, -1, -1 },
  78. };
  79.  
  80.  
  81. #define CHECK_EOF(lx,ch)                               \
  82.         if(ch == 0) {                                  \
  83.             if(!lx->error)                             \
  84.                 scc_lex_error(lx,"Unexpected eof.");   \
  85.             return 0;                                  \
  86.         }
  87.  
  88.  
  89. static int scc_string_var_lexer(YYSTYPE *lvalp, YYLTYPE *llocp,scc_lex_t* lex) {
  90.     char c,d;
  91.     char* esc = "%ivVnsfc";
  92.     int tpos = 0;
  93.     scc_str_t* str;
  94.  
  95.     scc_lex_pop_lexer(lex);
  96.  
  97.     c = scc_lex_getc(lex); // eat the %
  98.     CHECK_EOF(lex,c);
  99.  
  100.     c = scc_lex_at(lex,0); // get the command
  101.     CHECK_EOF(lex,c);
  102.  
  103.     // check that it's a known escape
  104.     if(!strchr(esc,c))
  105.         return 0;
  106.     else
  107.         scc_lex_getc(lex); 
  108.  
  109.     if(c == '%') {
  110.         lvalp->str = strdup("%");
  111.         d = scc_lex_at(lex,0);
  112.         // little hack to get rid of the usless empty string
  113.         if(d == '"') {
  114.             scc_lex_getc(lex);
  115.             scc_lex_pop_lexer(lex);
  116.         }
  117.         return STRING;
  118.     }
  119.  
  120.     d = scc_lex_at(lex,0);
  121.     CHECK_EOF(lex,d);
  122.  
  123.     if(d != '{') return 0;
  124.     // read the word
  125.     do {
  126.         tpos++;
  127.         d = scc_lex_at(lex,tpos);
  128.         if(!d) {
  129.             if(lex->error) return 0;
  130.             break;
  131.         }
  132.     } while(SCC_ISALNUM(d) || d == '_');
  133.     
  134.     if(d != '}') return 0;
  135.     
  136.     lvalp->strvar = str = calloc(1,sizeof(scc_str_t));
  137.     // read out the string
  138.     scc_lex_getc(lex); // {
  139.     str->str = scc_lex_gets(lex,tpos-1);
  140.     scc_lex_getc(lex); // }
  141.     
  142.     switch(c) {
  143.     case 'i':
  144.         str->type = SCC_STR_INT;
  145.         break;
  146.     case 'v':
  147.         str->type = SCC_STR_VERB;
  148.         break;
  149.     case 'V':
  150.         str->type = SCC_STR_VOICE;
  151.         break;
  152.     case 'n':
  153.         str->type = SCC_STR_NAME;
  154.         break;
  155.     case 's':
  156.         str->type = SCC_STR_STR;
  157.         break;
  158.     case 'f':
  159.         str->type = SCC_STR_FONT;
  160.         break;
  161.     case 'c':  {
  162.         char* end;
  163.         int val  = strtol(str->str,&end,0);
  164.         if(end[0] != '\0' || val < 0 || val > 255) {
  165.             scc_lex_error(lex,"Invalid color number: %s\n",str->str);
  166.             return 0;
  167.         }
  168.         str->type = SCC_STR_COLOR;
  169.         str->str = realloc(str->str,2);
  170.         SCC_SET_16LE(str->str,0,val);
  171.     } break;
  172.     }
  173.  
  174.     d = scc_lex_at(lex,0);
  175.     if(d == '"') {
  176.         scc_lex_getc(lex);
  177.         scc_lex_pop_lexer(lex);
  178.     }
  179.  
  180.     return STRVAR;
  181. }
  182.  
  183. static void scc_unescape_string(char* str) {
  184.     int esc = 0,i;
  185.     
  186.     for(i = 0 ; str[i] != '\0' ; i++) {
  187.         if(esc) {
  188.             switch(str[i]) {
  189.             case 'n':
  190.                 str[i-1] = 0xFF;
  191.                 str[i] = 1;
  192.                 break;
  193.             case 'k':
  194.                 str[i-1] = 0xFF;
  195.                 str[i] = 2;
  196.                 break;
  197.             case 'w':
  198.                 str[i-1] = 0xFF;
  199.                 str[i] = 3;
  200.                 break;
  201.             case '\\':
  202.             case '\"':
  203.                 memmove(str+i-1,str+i,strlen(str+i)+1);
  204.                 i--;
  205.                 break;
  206.             case 'x':
  207.                 if(str[i+1] >= '0' && str[i+1] <= '9')
  208.                     esc = (str[i+1]-'0') << 4;
  209.                 else if(str[i+1] >= 'a' && str[i+1] <= 'f')
  210.                     esc = (str[i+1]-'a') << 4;
  211.                 else if(str[i+1] >= 'A' && str[i+1] <= 'F')
  212.                     esc = (str[i+1]-'A') << 4;
  213.                 else
  214.                     break;
  215.                 if(str[i+2] >= '0' && str[i+2] <= '9')
  216.                     esc |= (str[i+2]-'0');
  217.                 else if(str[i+2] >= 'a' && str[i+2] <= 'f')
  218.                   esc |= (str[i+2]-'a');
  219.                 else if(str[i+2] >= 'A' && str[i+2] <= 'F')
  220.                   esc |= (str[i+2]-'A');
  221.                 else
  222.                     break;
  223.                 str[i-1] = esc;
  224.                 memmove(str+i,str+i+3,strlen(str+i+3)+1);
  225.                 i--;
  226.                 break;
  227.             default:
  228.                 // TODO warn
  229.                 break;
  230.             }
  231.             esc = 0;
  232.             continue;
  233.         }
  234.         if(str[i] == '\\') esc = 1;
  235.     }
  236.  
  237. }
  238.  
  239. static int scc_string_lexer(YYSTYPE *lvalp, YYLTYPE *llocp,scc_lex_t* lex) {
  240.     unsigned pos;
  241.     char c;
  242.  
  243.     for(pos = 0 ; 1 ; pos++) {
  244.         c = scc_lex_at(lex,pos);
  245.         CHECK_EOF(lex,c);
  246.         if(c == '\\') { // skip escape
  247.             pos++;
  248.             continue;
  249.         }
  250.         if(c == '"' || c == '%') break;
  251.     }
  252.     
  253.     // finished with a % escape
  254.     if(c == '%') {
  255.         scc_lex_push_lexer(lex,scc_string_var_lexer);
  256.         // some string first ?
  257.         if(pos > 0) {
  258.             lvalp->str = scc_lex_gets(lex,pos);
  259.             //scc_lex_getc(lex); // eat the %
  260.             scc_unescape_string(lvalp->str);
  261.             return STRING;
  262.         }
  263.         // directly switch to the string var lexer
  264.         //scc_lex_getc(lex); // eat the %
  265.         return 0;
  266.     }
  267.  
  268.     
  269.     lvalp->str = scc_lex_gets(lex,pos);
  270.     scc_lex_getc(lex); // eat the closing "
  271.     scc_unescape_string(lvalp->str);
  272.     // switch back to the previous parser
  273.     scc_lex_pop_lexer(lex);
  274.     return STRING;    
  275. }
  276.  
  277. static int scc_preproc_lexer(scc_lex_t* lex,char* ppd,char* arg, int line, int col) {
  278.     int i,j,len;
  279.  
  280.     if(!strcmp(ppd,"include")) {
  281.         if(!arg || (len = strlen(arg)) < 3 || (arg[0] != '"' && arg[0] != '<') ||
  282.            arg[len-1] != (arg[0] == '"' ? '"' : '>')) {
  283.             scc_lex_error(lex,"Expecting include <FILENAME> or \"FILENAME\"");
  284.             return 0;
  285.         }
  286.         arg[len-1] = '\0';
  287.         arg++;
  288.         return scc_lex_push_buffer(lex,arg) ? -1 : 0;
  289.     } else if(!strcmp(ppd,"define")) {
  290.         if(!arg || !SCC_ISALPHA(arg[0])) {
  291.             scc_lex_error(lex,"Invalid define name: %s",arg);
  292.             return 0;
  293.         }
  294.         for(i = 1 ; arg[i] && arg[i] != ' ' && arg[i] != '\t' ; i++) {
  295.             if(!SCC_ISALNUM(arg[i]) && arg[i] != '_') {
  296.                 scc_lex_error(lex,"Invalid define name: %s",arg);
  297.                 return 0;
  298.             }
  299.         }
  300.         for(j = i ; arg[j] == ' ' || arg[j] == '\t' ; j++);
  301.         len = strlen(arg)-j;
  302.         {
  303.             char name[i+1];
  304.             memcpy(name,arg,i);
  305.             name[i] = 0;
  306.             if(len > 0) {
  307.                 char val[len+1];
  308.                 memcpy(val,arg+j,len);
  309.                 val[len] = 0;
  310.                 scc_lex_define(lex,name,val,line,col+j);
  311.             } else
  312.                 scc_lex_define(lex,name,NULL,line,col);
  313.         }
  314.         return -1;
  315.     }
  316.                 
  317.  
  318.     //printf("Got preproc: '%s' -> '%s'\n",ppd,arg);
  319.     scc_lex_error(lex,"Unknown preprocessor directive %s",ppd);
  320.     return 0;
  321. }
  322.  
  323.  
  324. int scc_main_lexer(YYSTYPE *lvalp, YYLTYPE *llocp,scc_lex_t* lex) {
  325.     char c,d;
  326.     char* str,*ppd;
  327.     int tpos = 0,pos = 0;
  328.     int define_line = -1, define_col = -1;
  329.     scc_keyword_t* keyword;
  330.  
  331.     c = scc_lex_at(lex,0);
  332.  
  333.     // EOF
  334.     if(!c) return 0;
  335.  
  336.     //scc_lex_get_line_column(lex,&llocp->first_line,&llocp->first_column);
  337.  
  338.     // a sym, a keyword or an integer
  339.     if(SCC_ISALNUM(c) || c == '_') {
  340.         // read the whole word
  341.         do {
  342.             tpos++;
  343.             c = scc_lex_at(lex,tpos);
  344.             if(!c) {
  345.                 if(lex->error) return 0;
  346.                 break;
  347.             }
  348.         } while(SCC_ISALNUM(c) || c == '_');
  349.         // Get the string
  350.         str = scc_lex_gets(lex,tpos);
  351.  
  352.         // Is it an integer ?
  353.         if(SCC_ISDIGIT(str[0])) {
  354.             char* end;
  355.             lvalp->integer = strtol(str,&end,0);
  356.             if(end[0] != '\0') {
  357.                 scc_lex_error(lex,"Failed to parse integer: '%s'",str);
  358.                 free(str);
  359.                 return 0;
  360.             }
  361.             free(str);
  362.             return INTEGER;
  363.         }
  364.  
  365.         // Is it a define ?
  366.         if(scc_lex_is_define(lex,str)) {
  367.             scc_lex_expand_define(lex,str);
  368.             return -1;
  369.         }
  370.  
  371.         // Is it a keyword ?
  372.         if((keyword = scc_is_keyword(str,scc_keywords,
  373.                                      sizeof(scc_keywords)/sizeof(scc_keyword_t)-1))) {
  374.             free(str);
  375.             if(keyword->val >= 0)
  376.                 lvalp->integer = keyword->val;
  377.             return keyword->type;
  378.         }
  379.         // then it's symbol
  380.         lvalp->str = str;
  381.         return SYM;
  382.     }
  383.  
  384.     scc_lex_getc(lex);
  385.  
  386. // && &= & || |= | ++ += + -- -= -
  387. #define OP_OPEQ_OQDBL(op,eq_val,dbl_type,dbl_val)  \
  388.         d = scc_lex_at(lex,0);                     \
  389.         CHECK_EOF(lex,d);                          \
  390.         if(d == op) {                              \
  391.             scc_lex_getc(lex);                     \
  392.             lvalp->integer = dbl_val;              \
  393.             return dbl_type;                       \
  394.         } else if(d == '=') {                      \
  395.             scc_lex_getc(lex);                     \
  396.             lvalp->integer = eq_val;               \
  397.             return ASSIGN;                         \
  398.         }                                          \
  399.         lvalp->integer = op;                       \
  400.         return op
  401.  
  402.  
  403. // == = != ! >= > <= < *= *
  404. #define OP_OPEQ(op,eq_type,eq_val)                 \
  405.         d = scc_lex_at(lex,0);                     \
  406.         CHECK_EOF(lex,d);                          \
  407.         if(d == '=') {                             \
  408.             scc_lex_getc(lex);                     \
  409.             lvalp->integer = eq_val;               \
  410.             return eq_type;                        \
  411.         }                                          \
  412.         lvalp->integer = op;                       \
  413.         return op                                  \
  414.  
  415.  
  416.     switch(c) {
  417.         // && &= &
  418.     case '&':
  419.         OP_OPEQ_OQDBL('&',AAND,LAND,LAND);
  420.  
  421.         // || |= |
  422.     case '|':
  423.         OP_OPEQ_OQDBL('|',AOR,LOR,LOR);
  424.  
  425.         // ++ += +
  426.     case '+':
  427.         OP_OPEQ_OQDBL('+',AADD,SUFFIX,INC);
  428.  
  429.         // -- -= -
  430.     case '-':
  431.         OP_OPEQ_OQDBL('-',ASUB,SUFFIX,DEC);
  432.         break;
  433.  
  434.     case '=':
  435.         d = scc_lex_at(lex,0);
  436.         CHECK_EOF(lex,d);
  437.         if(d == '=') {
  438.             scc_lex_getc(lex);
  439.             lvalp->integer = EQ;
  440.             return EQ;
  441.         }
  442.         lvalp->integer = '=';
  443.         return ASSIGN;
  444.  
  445.         // != !
  446.     case '!':
  447.         OP_OPEQ('!',NEQ,NEQ);
  448.  
  449.         // >= >
  450.     case '>':
  451.         OP_OPEQ('>',GE,GE);
  452.  
  453.         // <= <
  454.     case '<':
  455.         OP_OPEQ('<',LE,LE);
  456.  
  457.         // *= *
  458.     case '*':
  459.         OP_OPEQ('*',ASSIGN,AADD);
  460.  
  461.         // /= // /* // /
  462.     case '/':
  463.         d = scc_lex_at(lex,0);
  464.         if(d == '=') {  // divide and assign
  465.             scc_lex_getc(lex);
  466.             lvalp->integer = ADIV;
  467.             return ASSIGN;
  468.         } else if(d == '/') { // single line comments
  469.             tpos = scc_lex_strchr(lex,1,'\n');
  470.             if(tpos > 0)
  471.                 scc_lex_drop(lex,tpos+1);
  472.             return -1;
  473.         } else if(d == '*') { // multi line comments
  474.             do {
  475.                 tpos = scc_lex_strchr(lex,tpos+1,'/');
  476.                 if(tpos < 0) {
  477.                     // TODO warn instead
  478.                     if(!lex->error)
  479.                         scc_lex_error(lex,"Unfinished multi line comment");
  480.                     return 0;
  481.                 }
  482.             } while(scc_lex_at(lex,tpos-1) != '*');
  483.             scc_lex_drop(lex,tpos+1);
  484.             return -1;
  485.         }
  486.         // divide
  487.         lvalp->integer = '/';
  488.         return '/';
  489.  
  490.         // :: :
  491.     case ':':
  492.         d = scc_lex_at(lex,0);
  493.         if(d == ':') {
  494.             scc_lex_getc(lex);
  495.             return NS;
  496.         }
  497.         lvalp->integer = ':';
  498.         return ':';
  499.  
  500.         // ; , @ ( ) [ ] { } ? 
  501.     case ';':
  502.     case ',':
  503.     case '@':
  504.     case '(':
  505.     case ')':
  506.     case '[':
  507.     case ']':
  508.     case '{':
  509.     case '}':
  510.     case '?':
  511.         lvalp->integer = c;
  512.         return c;
  513.  
  514.         // single chars
  515.     case '\'':
  516.         c = scc_lex_getc(lex);
  517.         if(c == '\\')
  518.             c = scc_lex_getc(lex);
  519.         CHECK_EOF(lex,c);
  520.         d = scc_lex_getc(lex);
  521.         CHECK_EOF(lex,d);
  522.         if(d != '\'') {
  523.             scc_lex_error(lex,"Unterminated character constant.");
  524.             return 0;
  525.         }
  526.         lvalp->integer = c;
  527.         return INTEGER;
  528.  
  529.         // strings
  530.     case '"':
  531.         scc_lex_push_lexer(lex,scc_string_lexer);
  532.         return 0;
  533.  
  534.         // pre precessor
  535.     case '#':
  536.         // read the directive name
  537.         c = scc_lex_at(lex,tpos);
  538.         CHECK_EOF(lex,c);
  539.         // skip initial spaces
  540.         while(c == ' ' || c == '\t') {
  541.             tpos++;
  542.             c = scc_lex_at(lex,tpos);
  543.             CHECK_EOF(lex,c);
  544.         }
  545.         // check that the first char is an alpha
  546.         if(!SCC_ISALPHA(c)) {
  547.             scc_lex_error(lex,"Invalid preprocessor directive.");
  548.             return 0;
  549.         }
  550.         // drop the spaces
  551.         scc_lex_drop(lex,tpos);
  552.         // read the name
  553.         tpos = 0;
  554.         do {
  555.             tpos++;
  556.             c = scc_lex_at(lex,tpos);
  557.             CHECK_EOF(lex,c);
  558.         } while(SCC_ISALPHA(c));
  559.         // check we ended on a space
  560.         if(c != ' ' && c != '\t' && c != '\n') {
  561.             scc_lex_error(lex,"Invalid preprocessor directive.");
  562.             return 0;
  563.         }
  564.         // read the directive
  565.         ppd = scc_lex_gets(lex,tpos);
  566.  
  567.         str = NULL;
  568.         while(1) {
  569.             // skip spaces
  570.             tpos = 0;
  571.             while(c == ' ' || c == '\t') {
  572.                 tpos++;
  573.                 c = scc_lex_at(lex,tpos);
  574.                 CHECK_EOF(lex,c);
  575.             }
  576.             // only space, that's it for now
  577.             if(c == '\n') {
  578.                 scc_lex_drop(lex,tpos+1);
  579.                  break;
  580.             } else
  581.                 scc_lex_drop(lex,tpos);
  582.  
  583.             // Store the arg start position
  584.             if(define_line < 0)
  585.                 scc_lex_get_line_column(lex,&define_line,&define_col);
  586.  
  587.             // find the eol
  588.             tpos = scc_lex_strchr(lex,0,'\n');
  589.             if(tpos < 0) { // we should do better here
  590.                 scc_lex_error(lex,"Unexpected eof");
  591.                 return 0;
  592.             }
  593.             // count the leading spaces
  594.             pos = tpos;
  595.             do {
  596.                 pos--;
  597.                 c = scc_lex_at(lex,pos);
  598.                 CHECK_EOF(lex,c);
  599.             } while(c == ' ' || c == '\t');
  600.             // not finished on a \, we are done
  601.             if(c != '\\') {
  602.                 str = scc_lex_strcat(lex,str,pos+1);
  603.                 break;
  604.             }
  605.             // read that line
  606.             str = scc_lex_strcat(lex,str,pos);
  607.             // and drop the \ and the spaces
  608.             scc_lex_drop(lex,tpos+1-pos);
  609.         }
  610.  
  611.         // call the preproc
  612.         tpos = scc_preproc_lexer(lex,ppd,str,define_line,define_col);
  613.         free(ppd);
  614.         if(str) free(str);
  615.         return tpos;
  616.  
  617.     case ' ':
  618.     case '\n':
  619.     case '\t':
  620.         do {
  621.             c = scc_lex_at(lex,tpos);
  622.             tpos++;
  623.         } while (c == ' ' || c == '\n' || c == '\t');
  624.         scc_lex_drop(lex,tpos-1);
  625.         return -1;
  626.             
  627.  
  628.     default:
  629.         scc_lex_error(lex,"Unrecognized character: '%c'",c);
  630.         return 0;
  631.     }
  632.     
  633. }
  634.  
  635. #ifdef LEX_TEST
  636.  
  637. #include <stdio.h>
  638. #include <ctype.h>
  639.  
  640. static void set_start_pos(YYLTYPE *llocp,int line,int column) {
  641.     llocp->first_line = line+1;
  642.     llocp->first_column = column;
  643. }
  644.  
  645. static void set_end_pos(YYLTYPE *llocp,int line,int column) {
  646.     llocp->last_line = line+1;
  647.     llocp->last_column = column;
  648. }
  649.  
  650. int main(int argc,char** argv) {
  651.     int i,tok;
  652.     scc_lex_t* lex = scc_lex_new(scc_main_lexer,set_start_pos,set_end_pos);
  653.     YYSTYPE val;
  654.     YYLTYPE loc;
  655.  
  656.     for(i = 1 ; i < argc ; i++) {
  657.         scc_lex_push_buffer(lex,argv[i]);
  658.         while((tok = scc_lex_lex(&val,&loc,lex))) {
  659.             switch(tok) {
  660.             case SYM:
  661.                 printf("Sym: %s [%d:%d][%d:%d]\n",val.str,loc.first_line,
  662.                        loc.first_column,loc.last_line,
  663.                        loc.last_column);
  664.                 free(val.str);
  665.                 break;
  666.             case STRING:
  667.                 printf("String: '%s' [%d:%d][%d:%d]\n",val.str,loc.first_line,
  668.                        loc.first_column,loc.last_line,
  669.                        loc.last_column);
  670.                 free(val.str);
  671.                 break;
  672.             default:
  673.                 if(isgraph(tok))
  674.                     printf("Token: %c (%d) [%d:%d][%d:%d]\n",tok,tok,loc.first_line,
  675.                            loc.first_column,loc.last_line,
  676.                            loc.last_column);
  677.                 else
  678.                     printf("Token: %d [%d:%d][%d:%d]\n",tok,loc.first_line,
  679.                            loc.first_column,loc.last_line,
  680.                            loc.last_column);
  681.             }
  682.         }
  683.         if(lex->error) {
  684.             printf("Lex error: %s\n",lex->error);
  685.             scc_lex_clear_error(lex);
  686.         }
  687.     }
  688.     return 0;
  689. }
  690.         
  691. #endif
  692.