Serious-Engine/Sources/Engine/Base/Scanner.l
2016-04-06 13:38:09 +02:00

281 lines
6.9 KiB
Plaintext

%{
#include "ParsingSymbols.h"
#include "parser.h"
#include <Engine/Base/FileName.h>
#include <Engine/Base/CTString.h>
#include <Engine/Base/CTString.inl>
#include <Engine/Base/Shell.h>
#include <Engine/Base/Console.h>
#include <Engine/Templates/DynamicStackArray.cpp>
#define YY_DECL int yylex (YYSTYPE *lvalp)
#define yylval (*lvalp)
#ifdef __cplusplus
extern "C" { int yywrap(void); }
#endif
int yywrap(void)
{
// no more bufers
return 1;
};
static int _iBlockDepth = 0;
static int _iDummyDepth = 0;
static CTString _strCmd = ""; // currently parsed command
static int _ctCmdParam = 1; // current parameter index
static BOOL _bCmdParamCountErrorReported = FALSE;
void TranscriptEsc(CTString &str)
{
char *pchSrc = (char *)(const char *)str;
char *pchDst = (char *)(const char *)str;
// if quoted
if (pchDst[0] == '"') {
int len = strlen(pchDst);
pchDst[len-1] = 0;
memmove(pchDst, pchDst+1, len-1);
}
for (;;pchSrc++, pchDst++) {
if (*pchSrc==0) {
break;
}
if (*pchSrc!='\\') {
*pchDst = *pchSrc;
continue;
}
pchSrc++;
switch(*pchSrc) {
case 'n': *pchDst = 10; break;
case 'r': *pchDst = 13; break;
default: *pchDst = *pchSrc; break;
}
}
*pchDst=0;
}
%}
%x COMMENT
%x DUMMYBLOCK
%x INCLUDE
%x COMMAND
DIGIT [0-9]
HEXDIGIT [0-9A-Fa-f]
IDENTIFIERFIRST [A-Za-z_]
IDENTIFIEROTHER [A-Za-z0-9_]
DOUBLEQUOTE \"
STRINGCONTENT ([^\"]|(\\\"))
CHARCONTENT ([^\']|(\\\'))
NONEXP_FLT ({DIGIT}+"."{DIGIT}*)
EXP_FLT (({DIGIT}+("."({DIGIT}*)?)?)("E"|"e")("+"|"-")?{DIGIT}+)
PARAMCONTENT ([^\ \n\";]|(\\\ ))
%%
/*\0 yyterminate();*/
/* Include file parsing. */
"include" BEGIN(INCLUDE);
<INCLUDE>[ \t]*"\"" /* eat the whitespace until first quote */
<INCLUDE>[^"\""]*"\"" { /* get the include file name until second quote */
if (ShellGetBufferStackDepth() >= SHELL_MAX_INCLUDE_LEVEL) {
_pShell->ErrorF("Script files nested too deeply");
}
char strFileName[256];
strcpy(strFileName, yytext);
strFileName[strlen(strFileName)-1] = 0;
CTString strIncludeFile;
try {
strIncludeFile.Load_t(CTString(strFileName));
ShellPushBuffer(strFileName, strIncludeFile, FALSE);
} catch(char *strError) {
_pShell->ErrorF("Cannot load script file '%s': %s", yytext, strError);
}
BEGIN(INITIAL);
}
<INCLUDE>. { /* something unrecognized inside include statement */
_pShell->ErrorF("Wrong syntax for include statement");
BEGIN(INITIAL);
}
/* command parsing */
<COMMAND>{PARAMCONTENT}*|"\""{STRINGCONTENT}*"\"" { // for each parameter
_ctCmdParam++;
CTString strParam = yytext;
TranscriptEsc(strParam);
// insert the parameter in the command string
CTString strParamNo = CTString(1, "%%%d", _ctCmdParam);
if (strParam.FindSubstr(strParamNo)!=-1) {
_pShell->ErrorF("Parameter substitution recursion detected!");
} else {
INDEX ctFound;
for(ctFound=0;; ctFound++) {
if (!_strCmd.ReplaceSubstr(strParamNo, strParam)) {
break;
}
}
// if not found and parameter count error not reported yet
if (ctFound==0 && !_bCmdParamCountErrorReported) {
// report error
_bCmdParamCountErrorReported = TRUE;
_pShell->ErrorF("Too many parameters for command expansion");
}
}
}
<COMMAND>\n|; {
// at the end of the command
// switch to the new input buffer with that command
ShellPushBuffer(ShellGetBufferName(), _strCmd, FALSE);
BEGIN(INITIAL);
}
<<EOF>> {
if (ShellPopBuffer()) {
yyterminate();
}
}
/* special data types */
"FLOAT" { return(k_FLOAT);}
"INDEX" { return(k_INDEX);}
"CTString" { return(k_CTString);}
/* keywords */
"void" { return(k_void); }
"const" { return(k_const); }
"user" { return(k_user); }
"persistent" { return(k_persistent); }
"extern" { return(k_extern); }
"pre" { return(k_pre); }
"post" { return(k_post); }
"help" { return(k_help); }
"if" { return(k_if); }
"else" { return(k_else); }
"else"" "*"if" { return(k_else_if); }
"<=" { return (LEQ); }
">=" { return (GEQ); }
"==" { return (EQ); }
"!=" { return (NEQ); }
">>" { return (SHR); }
"<<" { return (SHL); }
"&&" { return (LOGAND); }
"||" { return (LOGOR); }
/* single character operators and punctuations */
";"|"("|")"|"="|"+"|"-"|"<"|">"|"!"|"|"|"&"|"*"|"/"|"%"|"^"|"["|"]"|":"|","|"."|"?"|"~" {
return(yytext[0]);}
/* constants */
{DIGIT}+ { yylval.val.iIndex = atoi(yytext); return(c_int); }
"0x"{HEXDIGIT}+ { yylval.val.iIndex = strtoul(yytext+2, NULL, 16); return(c_int); }
{NONEXP_FLT}("f"|"F")? { yylval.val.fFloat = (float) atof(yytext); return(c_float); }
{EXP_FLT}("f"|"F")? { yylval.val.fFloat = (float) atof(yytext); return(c_float); }
"\""{STRINGCONTENT}*"\"" {
CTString &strNew = _shell_astrTempStrings.Push();
// remove double-quotes
strNew = yytext;
// parse escape symbols and remove double quotes
TranscriptEsc(strNew);
yylval.val.strString = (const char*)strNew;
return(c_string);
}
"'"{CHARCONTENT}"'" { return(c_char); }
/* identifier */
{IDENTIFIERFIRST}{IDENTIFIEROTHER}* {
// get the symbol
yylval.pssSymbol = _pShell->GetSymbol(yytext, FALSE);
BOOL bCommand = FALSE;
// if it is string
if (_shell_ast[yylval.pssSymbol->ss_istType].st_sttType==STT_STRING) {
// get the value
CTString str = *(CTString*)yylval.pssSymbol->ss_pvValue;
// if the value tells that it is a command
if (str.RemovePrefix("!command ")) {
// parse the command
bCommand = TRUE;
_strCmd = str;
_ctCmdParam = 0;
_bCmdParamCountErrorReported = FALSE;
BEGIN(COMMAND);
}
}
// normally, just return the identifier
if (!bCommand) {
return(identifier);
}
}
${IDENTIFIERFIRST}{IDENTIFIEROTHER}* { // special case of identifier, used to bypass command parsing
// get the symbol
yylval.pssSymbol = _pShell->GetSymbol(yytext+1, FALSE);
return(identifier);
}
/* eat up or execute blocks */
"{" {
_iBlockDepth++;
if (!_bExecNextBlock) {
_iDummyDepth++;
BEGIN(DUMMYBLOCK);
}
return block_beg;
}
"}" {
_iBlockDepth--;
if (_iBlockDepth<0) {
_pShell->ErrorF("Mismatched '}'");
}
return block_end;
}
<DUMMYBLOCK>"{" {
_iBlockDepth++;
_iDummyDepth++;
}
<DUMMYBLOCK>"}" {
_iBlockDepth--;
_iDummyDepth--;
if (_iDummyDepth==0) {
BEGIN(INITIAL);
return block_end;
}
}
<DUMMYBLOCK>. {}
/* eat up comments */
"/*" { BEGIN(COMMENT); }
<COMMENT>"*/" { BEGIN(INITIAL); }
<COMMENT>. {}
"//"[^\n]*\n { ShellCountOneLine(); }
/* eat up whitespace */
<INITIAL,COMMAND>[ \t]+ {
}
/* eat up linefeeds and count lines in all conditions */
<*>\n {
ShellCountOneLine();
}
/* for all unrecognized characters */
<INITIAL,COMMAND>. {
// report an error
_pShell->ErrorF("Unrecognized character '%c' (ASCII 0x%02x)", yytext[0], yytext[0] );
}
%%