/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ //************************************************************************** //** //** token.c //** //************************************************************************** // HEADER FILES ------------------------------------------------------------ #include "token.h" #include "inout.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- typedef enum { CHR_EOF, CHR_LETTER, CHR_NUMBER, CHR_QUOTE, CHR_SPECIAL } chr_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void ProcessLetterToken(void); static void ProcessNumberToken(void); static void ProcessQuoteToken(void); static void ProcessSpecialToken(void); static qboolean CheckForKeyword(void); static void NextChr(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- tokenType_t tk_Token; int tk_Line; int tk_IntNumber; float tk_FloatNumber; char *tk_String; char tk_SourceName[MAX_FILE_NAME_LENGTH]; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static char Chr; static char *FileStart; static char *FilePtr; static char *FileEnd; static qboolean SourceOpen; static char ASCIIToChrCode[256]; static char TokenStringBuffer[MAX_QUOTED_LENGTH]; static qboolean IncLineNumber; static char TempBuffer[2048]; static struct { char *name; tokenType_t token; } Keywords[] = { "model", TK_MODEL, "mesh", TK_MESH, "vertices", TK_VERTICES, "edges", TK_EDGES, "position", TK_POSITION, "polygons", TK_POLYGONS, "nodes", TK_NODES, "rotation", TK_ROTATION, "scaling", TK_SCALING, "translation", TK_TRANSLATION, "vertex", TK_VERTEX, "HRCH", TK_HRCH, "Softimage", TK_SOFTIMAGE, "material", TK_MATERIAL, "spline", TK_SPLINE, "Named", TK_C_NAMED, "object", TK_OBJECT, "Tri", TK_C_TRI, "Vertices", TK_C_VERTICES, "Faces", TK_C_FACES, "Vertex", TK_C_VERTEX, "list", TK_LIST, "Face", TK_C_FACE, "Hexen", TK_C_HEXEN, "Triangles", TK_C_TRIANGLES, "Version", TK_C_VERSION, "faces", TK_FACES, "face", TK_FACE, "origin", TK_ORIGIN, "DK_clusters", TK_CLUSTERS, "DK_cluster_ncvs", TK_NUM_CLUSTER_VERTICES, "name", TK_NAME, "DK_cluster_name", TK_CLUSTER_NAME, "DK_cluster_state", TK_CLUSTER_STATE, "actor_data", TK_ACTOR_DATA, "uvTexture", TK_UVTEXTURE, NULL, -1 }; static char *TokenNames[] = { "", "", "", "", "", "", "", "(", ")", "{", "}", "[", "]", ":", "mesh", "model", "nodes", "rotation", "scaling", "translation", "polygons", "position", "vertex", "vertices", "HRCH", "Softimage" }; // CODE -------------------------------------------------------------------- //========================================================================== // // TK_Init // //========================================================================== void TK_Init(void) { int i; for(i = 0; i < 256; i++) { ASCIIToChrCode[i] = CHR_SPECIAL; } for(i = '0'; i <= '9'; i++) { ASCIIToChrCode[i] = CHR_NUMBER; } for(i = 'A'; i <= 'Z'; i++) { ASCIIToChrCode[i] = CHR_LETTER; } for(i = 'a'; i <= 'z'; i++) { ASCIIToChrCode[i] = CHR_LETTER; } ASCIIToChrCode[ASCII_QUOTE] = CHR_QUOTE; ASCIIToChrCode[ASCII_UNDERSCORE] = CHR_LETTER; ASCIIToChrCode[EOF_CHARACTER] = CHR_EOF; tk_String = TokenStringBuffer; IncLineNumber = FALSE; SourceOpen = FALSE; } //========================================================================== // // TK_OpenSource // //========================================================================== void TK_OpenSource(char *fileName) { int size; TK_CloseSource(); size = LoadFile(fileName, (void **)&FileStart); strcpy(tk_SourceName, fileName); SourceOpen = TRUE; FileEnd = FileStart+size; FilePtr = FileStart; tk_Line = 1; tk_Token = TK_NONE; NextChr(); } //========================================================================== // // TK_CloseSource // //========================================================================== void TK_CloseSource(void) { if(SourceOpen) { free(FileStart); SourceOpen = FALSE; } } //========================================================================== // // TK_Fetch // //========================================================================== tokenType_t TK_Fetch(void) { while(Chr == ASCII_SPACE) { NextChr(); } if(Chr == '-') { ProcessNumberToken(); } else switch(ASCIIToChrCode[(byte)Chr]) { case CHR_EOF: tk_Token = TK_EOF; break; case CHR_LETTER: ProcessLetterToken(); break; case CHR_NUMBER: ProcessNumberToken(); break; case CHR_QUOTE: ProcessQuoteToken(); break; default: ProcessSpecialToken(); break; } return tk_Token; } //========================================================================== // // TK_Require // //========================================================================== void TK_Require(tokenType_t tokType) { if(tokType == TK_FLOATNUMBER && tk_Token == TK_INTNUMBER) { tk_FloatNumber = (float)tk_IntNumber; tk_Token = TK_FLOATNUMBER; return; } if(tk_Token != tokType) { Error("File '%s', line %d:\nExpected '%s', found '%s'.\n", tk_SourceName, tk_Line, TokenNames[tokType], TokenNames[tk_Token]); } } void TK_FetchRequire(tokenType_t tokType) { TK_Fetch(); TK_Require(tokType); } tokenType_t TK_RequireFetch(tokenType_t tokType) { TK_Require(tokType); return TK_Fetch(); } tokenType_t TK_FetchRequireFetch(tokenType_t tokType) { TK_Fetch(); TK_Require(tokType); return TK_Fetch(); } tokenType_t TK_Beyond(tokenType_t tokType) { while(tk_Token != tokType) { if(TK_Fetch() == TK_EOF) { Error("File '%s':\nCould not find token '%s'.\n", // FIXME: TokenNames table not big enuff tk_SourceName, TokenNames[tokType]); } } return TK_Fetch(); } void TK_BeyondRequire(tokenType_t bTok, tokenType_t rTok) { TK_Beyond(bTok); TK_Require(rTok); } tokenType_t TK_Search(tokenType_t tokType) { while(tk_Token != tokType) { if(TK_Fetch() == TK_EOF) { return TK_EOF; } } return TK_Fetch(); } tokenType_t TK_Get(tokenType_t tokType) { while(tk_Token != tokType) { if(TK_Fetch() == TK_EOF) { Error("File '%s':\nCould not find token '%s'.\n", tk_SourceName, TokenNames[tokType]); } } return tk_Token; } //========================================================================== // // ProcessLetterToken // //========================================================================== static void ProcessLetterToken(void) { int i; char *text; i = 0; text = TokenStringBuffer; while(ASCIIToChrCode[(byte)Chr] == CHR_LETTER || ASCIIToChrCode[(byte)Chr] == CHR_NUMBER) { if(++i == MAX_IDENTIFIER_LENGTH) { Error("File '%s', line %d:\nIdentifier too long.\n", tk_SourceName, tk_Line); } *text++ = Chr; NextChr(); } *text = 0; if(CheckForKeyword() == FALSE) { tk_Token = TK_IDENTIFIER; } } //========================================================================== // // CheckForKeyword // //========================================================================== static qboolean CheckForKeyword(void) { int i; for(i = 0; Keywords[i].name != NULL; i++) { if(strcmp(tk_String, Keywords[i].name) == 0) { tk_Token = Keywords[i].token; return TRUE; } } return FALSE; } //========================================================================== // // ProcessNumberToken // //========================================================================== static void ProcessNumberToken(void) { char *buffer; buffer = TempBuffer; *buffer++ = Chr; NextChr(); while(ASCIIToChrCode[(byte)Chr] == CHR_NUMBER) { *buffer++ = Chr; NextChr(); } if(Chr == '.') { // Float *buffer++ = Chr; NextChr(); // Skip period while(ASCIIToChrCode[(byte)Chr] == CHR_NUMBER) { *buffer++ = Chr; NextChr(); } *buffer = 0; tk_FloatNumber = (float)atof(TempBuffer); tk_Token = TK_FLOATNUMBER; return; } // Integer *buffer = 0; tk_IntNumber = atoi(TempBuffer); tk_Token = TK_INTNUMBER; } //========================================================================== // // ProcessQuoteToken // //========================================================================== static void ProcessQuoteToken(void) { int i; char *text; i = 0; text = TokenStringBuffer; NextChr(); while(Chr != ASCII_QUOTE) { if(Chr == EOF_CHARACTER) { Error("File '%s', line %d:\n inside string.\n", tk_SourceName, tk_Line); } if(++i > MAX_QUOTED_LENGTH-1) { Error("File '%s', line %d:\nString literal too long.\n", tk_SourceName, tk_Line); } *text++ = Chr; NextChr(); } *text = 0; NextChr(); tk_Token = TK_STRING; } //========================================================================== // // ProcessSpecialToken // //========================================================================== static void ProcessSpecialToken(void) { char c; c = Chr; NextChr(); switch(c) { case '(': tk_Token = TK_LPAREN; break; case ')': tk_Token = TK_RPAREN; break; case '{': tk_Token = TK_LBRACE; break; case '}': tk_Token = TK_RBRACE; break; case '[': tk_Token = TK_LBRACKET; break; case ']': tk_Token = TK_RBRACKET; break; case ':': tk_Token = TK_COLON; break; default: tk_Token = TK_UNKNOWNCHAR; break; } } //========================================================================== // // NextChr // //========================================================================== static void NextChr(void) { if(FilePtr >= FileEnd) { Chr = EOF_CHARACTER; return; } if(IncLineNumber == TRUE) { tk_Line++; IncLineNumber = FALSE; } Chr = *FilePtr++; if(Chr < ASCII_SPACE) { if(Chr == '\n') { IncLineNumber = TRUE; } Chr = ASCII_SPACE; } }