mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
332 lines
8.5 KiB
Text
332 lines
8.5 KiB
Text
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
%option noyywrap
|
|
%option noinput
|
|
%option nounput
|
|
%option never-interactive
|
|
%option case-insensitive
|
|
|
|
%option outfile="engines/director/lingo/lingo-lex.cpp"
|
|
|
|
%{
|
|
|
|
#define YY_NO_UNISTD_H
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_fread
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_stdin
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_getc
|
|
|
|
#include "common/str.h"
|
|
|
|
#include "director/director.h"
|
|
#include "director/lingo/lingo.h"
|
|
#include "director/lingo/lingo-ast.h"
|
|
#include "director/lingo/lingo-codegen.h"
|
|
#include "director/lingo/lingo-gr.h"
|
|
#include "director/lingo/lingo-the.h"
|
|
|
|
using namespace Director;
|
|
|
|
static const char *inputbuffer;
|
|
static uint inputlen;
|
|
|
|
// Push lines in stack
|
|
static void pushLine(uint num) {
|
|
LingoCompiler *compiler = g_lingo->_compiler;
|
|
|
|
if (num > inputlen)
|
|
return;
|
|
|
|
compiler->_lines[2] = compiler->_lines[1];
|
|
compiler->_lines[1] = compiler->_lines[0];
|
|
compiler->_lines[0] = &inputbuffer[num];
|
|
}
|
|
|
|
static void count() {
|
|
LingoCompiler *compiler = g_lingo->_compiler;
|
|
|
|
if (debugChannelSet(-1, kDebugParse))
|
|
debug("LEXER: Read '%s' at %d:%d", yytext, compiler->_linenumber, compiler->_colnumber);
|
|
|
|
char *p = yytext;
|
|
|
|
while (*p) {
|
|
if (*p == '\n') {
|
|
compiler->_linenumber++;
|
|
compiler->_colnumber = 0;
|
|
pushLine(compiler->_bytenumber + 1);
|
|
} else if (*p == '\xC2' && *(p + 1) == '\xAC') { // continuation
|
|
compiler->_linenumber++;
|
|
compiler->_colnumber = 0;
|
|
} else {
|
|
compiler->_colnumber++;
|
|
}
|
|
p++;
|
|
compiler->_bytenumber++;
|
|
}
|
|
}
|
|
|
|
static Common::String *cleanupString(const char *s) {
|
|
Common::String *res = new Common::String;
|
|
|
|
while (*s) {
|
|
if (*s == '\xC2' && *(s + 1) == '\xAC') { // continuation
|
|
s += 2;
|
|
*res += ' '; // replace with space
|
|
continue;
|
|
}
|
|
*res += *s;
|
|
s++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void skipWhitespace(const char **ptr) {
|
|
while (true) {
|
|
if (**ptr == ' ' || **ptr == '\t') {
|
|
*ptr += 1;
|
|
} else if (**ptr == '\xC2' && *(*ptr + 1) == '\xAC') { // continuation
|
|
*ptr += 2;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Common::String *readUntilWhitespace(const char **ptr) {
|
|
Common::String *res = new Common::String;
|
|
|
|
while (true) {
|
|
if (**ptr == ' ' || **ptr == '\t') {
|
|
break;
|
|
}
|
|
if (**ptr == '\xC2' && *(*ptr + 1) == '\xAC') { // continuation
|
|
break;
|
|
}
|
|
*res += **ptr;
|
|
*ptr += 1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static Common::String *readUntilNull(const char **ptr) {
|
|
Common::String *res = new Common::String;
|
|
|
|
while (**ptr) {
|
|
if (**ptr == '\xC2' && *(*ptr + 1) == '\xAC') { // continuation
|
|
*ptr += 2;
|
|
*res += ' '; // replace with space
|
|
continue;
|
|
}
|
|
*res += **ptr;
|
|
*ptr += 1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
%}
|
|
|
|
identifier [_[:alpha:]][_\.[:alnum:]]*
|
|
constfloat [[:digit:]]+\.[[:digit:]]*
|
|
constinteger [[:digit:]]+
|
|
conststring \"[^\"\r\n]*\"
|
|
operator [-+*/%^:,()><&\[\]]
|
|
newline (" "|\t|\xC2\xAC)*[\n\r]
|
|
spc (" "|\t|\xC2\xAC)
|
|
eventname (keyDown|keyUp|mouseDown|mouseUp|timeOut)
|
|
unparsedstmt [^\r\n]*
|
|
|
|
%%
|
|
|
|
{spc}+ { count(); }
|
|
|
|
[#]{identifier} { count(); yylval.s = new Common::String(yytext + 1); return tSYMBOL; } // D3, skip '#'
|
|
|
|
abbreviated { count(); return tABBREVIATED; }
|
|
abbrev { count(); return tABBREV; }
|
|
abbr { count(); return tABBR; }
|
|
after { count(); return tAFTER; } // D3
|
|
and { count(); return tAND; }
|
|
before { count(); return tBEFORE; } // D3
|
|
cast { count(); return tCAST; }
|
|
castlib { count(); return tCASTLIB; } // D5
|
|
castlibs { count(); return tCASTLIBS; } // D5
|
|
char { count(); return tCHAR; } // D3
|
|
chars { count(); return tCHARS; }
|
|
contains { count(); return tCONTAINS; }
|
|
date { count(); return tDATE; }
|
|
delete { count(); return tDELETE; }
|
|
down { count(); return tDOWN; }
|
|
else { count(); return tELSE; }
|
|
end({spc}+{identifier})? {
|
|
count();
|
|
|
|
const char *ptr = &yytext[3]; // Skip 'end '
|
|
skipWhitespace(&ptr);
|
|
|
|
if (!scumm_stricmp(ptr, "if"))
|
|
return tENDIF;
|
|
else if (!scumm_stricmp(ptr, "repeat"))
|
|
return tENDREPEAT;
|
|
else if (!scumm_stricmp(ptr, "tell"))
|
|
return tENDTELL;
|
|
|
|
yylval.s = new Common::String(ptr);
|
|
|
|
return tENDCLAUSE;
|
|
}
|
|
exit { count(); return tEXIT; }
|
|
^{spc}*factory { count(); return tFACTORY; }
|
|
field { count(); return tFIELD; }
|
|
frame { count(); return tFRAME; }
|
|
global { count(); return tGLOBAL; }
|
|
go({spc}+to)? { count(); return tGO; }
|
|
hilite { count(); return tHILITE; }
|
|
if { count(); return tIF; }
|
|
instance { count(); return tINSTANCE; }
|
|
intersects { count(); return tINTERSECTS;}
|
|
into { count(); return tINTO; }
|
|
in { count(); return tIN; }
|
|
item { count(); return tITEM; }
|
|
items { count(); return tITEMS; }
|
|
last { count(); return tLAST; }
|
|
line { count(); return tLINE; }
|
|
lines { count(); return tLINES; }
|
|
long { count(); return tLONG; }
|
|
macro { count(); return tMACRO; }
|
|
member { count(); return tMEMBER; }
|
|
menu { count(); return tMENU; }
|
|
menus { count(); return tMENUS; }
|
|
menuItem { count(); return tMENUITEM;}
|
|
menuItems { count(); return tMENUITEMS; }
|
|
method { count(); return tMETHOD; }
|
|
mod { count(); return tMOD;}
|
|
movie { count(); return tMOVIE; }
|
|
next { count(); return tNEXT; }
|
|
not { count(); return tNOT; }
|
|
number { count(); return tNUMBER; }
|
|
of { count(); return tOF; }
|
|
on { count(); return tON; } // D3
|
|
open { count(); return tOPEN; }
|
|
or { count(); return tOR; }
|
|
play { count(); return tPLAY; }
|
|
previous { count(); return tPREVIOUS; }
|
|
property { count(); return tPROPERTY; } // D4
|
|
put { count(); return tPUT; }
|
|
repeat { count(); return tREPEAT; }
|
|
return { count(); return tRETURN; }
|
|
script { count(); return tSCRIPT; }
|
|
scummvmAssertError { count(); return tASSERTERROR; }
|
|
set { count(); return tSET; }
|
|
short { count(); return tSHORT; }
|
|
sound { count(); return tSOUND; }
|
|
sprite { count(); return tSPRITE; }
|
|
starts { count(); return tSTARTS; }
|
|
tell { count(); return tTELL; }
|
|
the { count(); return tTHE; }
|
|
then { count(); return tTHEN; }
|
|
time { count(); return tTIME; }
|
|
to { count(); return tTO; }
|
|
when{spc}+{eventname}{spc}+then{spc}+{unparsedstmt} {
|
|
count();
|
|
|
|
const char *ptr = &yytext[5]; // Skip 'when '
|
|
skipWhitespace(&ptr);
|
|
|
|
Common::String *eventName = readUntilWhitespace(&ptr);
|
|
|
|
skipWhitespace(&ptr);
|
|
ptr += 5; // Skip 'then '
|
|
skipWhitespace(&ptr);
|
|
|
|
Common::String *stmt = readUntilNull(&ptr);
|
|
|
|
yylval.w.eventName = eventName;
|
|
yylval.w.stmt = stmt;
|
|
return tWHEN;
|
|
}
|
|
while { count(); return tWHILE; }
|
|
window { count(); return tWINDOW; }
|
|
with { count(); return tWITH; }
|
|
within { count(); return tWITHIN; }
|
|
word { count(); return tWORD; }
|
|
words { count(); return tWORDS; }
|
|
xtras { count(); return tXTRAS; } // D5
|
|
|
|
[<][>] { count(); return tNEQ; }
|
|
[>][=] { count(); return tGE; }
|
|
[<][=] { count(); return tLE; }
|
|
[&][&] { count(); return tCONCAT; }
|
|
[=] { count(); return tEQ; }
|
|
|
|
{identifier} {
|
|
count();
|
|
yylval.s = new Common::String(yytext);
|
|
|
|
return tVARID;
|
|
}
|
|
{constfloat} { count(); yylval.f = atof(yytext); return tFLOAT; }
|
|
{constinteger} { count(); yylval.i = strtol(yytext, NULL, 10); return tINT; }
|
|
{operator} { count(); return *yytext; }
|
|
{newline} { count(); return '\n'; }
|
|
{conststring} { count(); yylval.s = cleanupString(&yytext[1]); yylval.s->deleteLastChar(); return tSTRING; }
|
|
. { count(); }
|
|
|
|
%%
|
|
|
|
extern int yydebug;
|
|
|
|
namespace Director {
|
|
|
|
int LingoCompiler::parse(const char *code) {
|
|
inputbuffer = code;
|
|
_bytenumber = 0;
|
|
inputlen = strlen(code);
|
|
|
|
_lines[0] = _lines[1] = _lines[2] = code;
|
|
|
|
YY_BUFFER_STATE bp;
|
|
|
|
if (debugChannelSet(-1, kDebugParse))
|
|
yydebug = 1;
|
|
else
|
|
yydebug = 0;
|
|
|
|
yy_delete_buffer(YY_CURRENT_BUFFER);
|
|
|
|
bp = yy_scan_string(code);
|
|
yy_switch_to_buffer(bp);
|
|
yyparse();
|
|
yy_delete_buffer(bp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // End of namespace Director
|