267 lines
6.6 KiB
C++
267 lines
6.6 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "script_squirrel/squirrel_analyzer.h"
|
|
#include "ascii/parser.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "platform.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::SquirrelAnalyzer;
|
|
using namespace GS::AsciiParser;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
/* static bool SymbolFindByNameFunctor(Symbol *s, const char *n) { return s->name == n; } */
|
|
static bool SourceFindByNameFunctor(Source *s, const char *n) { return s->name == n; }
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
struct ParserContext
|
|
{
|
|
const char *s, *e, *o;
|
|
SharedList <Symbol *> *symbols;
|
|
Symbol::Type type;
|
|
|
|
Offset CurrentOffset() const
|
|
{
|
|
Offset offset(1, 0);
|
|
for (const char *p = o; p < e; ++offset.line)
|
|
{
|
|
const char *eol = RunToEOL(p, e);
|
|
|
|
if (eol > s)
|
|
{
|
|
offset.column = s - p;
|
|
break;
|
|
}
|
|
if (eol >= e)
|
|
return Offset(1, 0);
|
|
|
|
p = SkipEOL(eol, e);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
ParserContext(const char *_s, const char *_e, const char *_o, SharedList <Symbol *> *_symbols, Symbol::Type _type) : s(_s), e(_e), o(_o), symbols(_symbols), type(_type) {}
|
|
};
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void ParseContext(ParserContext &);
|
|
bool ParseMemberVariable(ParserContext &ctx)
|
|
{
|
|
AutoPtr <Variable> _var(new Variable);
|
|
_var->offset = ctx.CurrentOffset();
|
|
_var->var_type = Variable::VarMember;
|
|
|
|
const char *e = SkipEntry(ctx.s, ctx.e);
|
|
_var->name.Set(ctx.s, e);
|
|
|
|
ctx.s = NextEntry(ctx.s, ctx.e) + 1;
|
|
ctx.symbols->Add(_var.Detach());
|
|
return true;
|
|
}
|
|
bool ParseGlobalVariable(ParserContext &ctx)
|
|
{
|
|
AutoPtr <Variable> _var(new Variable);
|
|
_var->offset = ctx.CurrentOffset();
|
|
_var->var_type = Variable::VarGlobal;
|
|
|
|
const char *e = SkipEntry(ctx.s, ctx.e);
|
|
_var->name.Set(ctx.s, e);
|
|
|
|
ctx.s = NextEntry(ctx.s, ctx.e) + 2;
|
|
ctx.symbols->Add(_var.Detach());
|
|
return true;
|
|
}
|
|
bool ParseLocalVariable(ParserContext &ctx)
|
|
{
|
|
AutoPtr <Variable> _var(new Variable);
|
|
_var->offset = ctx.CurrentOffset();
|
|
_var->var_type = Variable::VarLocal;
|
|
|
|
const char *s = NextEntry(ctx.s, ctx.e);
|
|
_var->name.Set(s, SkipEntry(s, ctx.e));
|
|
ctx.symbols->Add(_var.Detach());
|
|
|
|
// parse subsequent declarations.
|
|
s = NextEntry(s, ctx.e);
|
|
|
|
forever
|
|
{
|
|
/*
|
|
// TODO a naive test won't do as there is no mandatory end-of-statement in Squirrel
|
|
if (s[0] == '=')
|
|
{
|
|
// check if there are more variables declared by this statement
|
|
}
|
|
*/
|
|
if (s[0] == ',')
|
|
{
|
|
s = NextEntry(s + 1, ctx.e);
|
|
ctx.s = s;
|
|
|
|
_var = new Variable;
|
|
_var->offset = ctx.CurrentOffset();
|
|
_var->var_type = Variable::VarLocal;
|
|
|
|
_var->name.Set(s, SkipEntry(s, ctx.e));
|
|
ctx.symbols->Add(_var.Detach());
|
|
|
|
s = NextEntry(s, ctx.e);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
ctx.s = s;
|
|
return true;
|
|
}
|
|
bool ParseFunction(ParserContext &ctx)
|
|
{
|
|
AutoPtr <Function> _function(new Function);
|
|
_function->offset = ctx.CurrentOffset();
|
|
|
|
const char *s = NextEntry(ctx.s, ctx.e);
|
|
_function->name.Set(s, SkipEntry(s, ctx.e));
|
|
|
|
s = NextEntry(s, ctx.e);
|
|
|
|
// Parse prototype.
|
|
if (s[0] != '(')
|
|
return false;
|
|
|
|
const char *eoproto = RunToEOG(s, ctx.e, '(', ')');
|
|
if (eoproto == NULL)
|
|
return false;
|
|
|
|
_function->prototype.Set(s + 1, eoproto);
|
|
s = NextEntry(eoproto + 1, ctx.e);
|
|
|
|
// Parse function content.
|
|
if (s[0] != '{')
|
|
return false;
|
|
|
|
const char *eofunc = RunToEOG(s, ctx.e, '{', '}');
|
|
if (eofunc == NULL)
|
|
eofunc = ctx.e;
|
|
ParserContext func_ctx(s + 1, eofunc, ctx.o, &_function->symbols, _function->type);
|
|
|
|
ParseContext(func_ctx);
|
|
|
|
ctx.s = eofunc + 1;
|
|
ctx.symbols->Add(_function.Detach());
|
|
return true;
|
|
}
|
|
bool ParseClass(ParserContext &ctx)
|
|
{
|
|
AutoPtr <Class> _class(new Class);
|
|
_class->offset = ctx.CurrentOffset();
|
|
|
|
ctx.s = NextEntry(ctx.s, ctx.e);
|
|
_class->name.Set(ctx.s, SkipEntry(ctx.s, ctx.e));
|
|
|
|
const char *s = NextEntry(ctx.s, ctx.e);
|
|
|
|
if (!String::strccmp("extends", s))
|
|
{
|
|
s = NextEntry(s, ctx.e);
|
|
_class->extends.Set(s, SkipEntry(s, ctx.e));
|
|
|
|
ctx.s = SkipEntry(s, ctx.e);
|
|
|
|
s = NextEntry(s, ctx.e);
|
|
if (s == ctx.e)
|
|
return false;
|
|
}
|
|
|
|
// Parse class content.
|
|
if (s[0] != '{')
|
|
return false;
|
|
|
|
const char *eoclass = RunToEOG(s, ctx.e, '{', '}');
|
|
if (eoclass == NULL)
|
|
eoclass = ctx.e;
|
|
|
|
ParserContext class_ctx(s + 1, eoclass, ctx.o, &_class->symbols, _class->type);
|
|
ParseContext(class_ctx);
|
|
|
|
ctx.s = eoclass + 1;
|
|
ctx.symbols->Add(_class.Detach());
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void ParseContext(ParserContext &ctx)
|
|
{
|
|
for (bool r = true; r && (ctx.s < ctx.e); )
|
|
{
|
|
if (!String::strccmp("class", ctx.s))
|
|
r = ParseClass(ctx);
|
|
else if (!String::strccmp("function", ctx.s))
|
|
r = ParseFunction(ctx);
|
|
else if (!String::strccmp("local", ctx.s))
|
|
r = ParseLocalVariable(ctx);
|
|
else
|
|
{
|
|
const char *e = SkipEntry(ctx.s, ctx.e);
|
|
String tmp(ctx.s, e);
|
|
|
|
const char *s = NextEntry(ctx.s, ctx.e);
|
|
if (ctx.s == s)
|
|
++ctx.s; // skip
|
|
else
|
|
{
|
|
if ((s[0] == '<') && (s[1] == '-'))
|
|
r = ParseGlobalVariable(ctx);
|
|
else if ((ctx.type == Symbol::TypeClass) && (s[0] == '='))
|
|
r = ParseMemberVariable(ctx);
|
|
else
|
|
ctx.s = s;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Source *ParseSource(const char *s, const char *e)
|
|
{
|
|
AutoPtr <Source> source(new Source);
|
|
|
|
ParserContext ctx(s, e, s, &source->symbols, Symbol::TypeNone);
|
|
ParseContext(ctx);
|
|
|
|
return source.Detach();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Source *SquirrelAnalyzer::Analyze(const char *nut, Program &program, bool replace)
|
|
{
|
|
Source *old_source = ListFindEx(program.sources, SourceFindByNameFunctor, nut);
|
|
if (old_source && !replace)
|
|
return NULL;
|
|
|
|
String source;
|
|
{
|
|
Array <char> buffer;
|
|
if (!Platform::Get().io->FileLoad(nut, buffer))
|
|
return NULL;
|
|
source.Set(buffer.c_ptr(), buffer.c_ptr() + buffer.GetCount());
|
|
}
|
|
|
|
Source *new_source = ParseSource(source.c_str(), source.c_str() + source.Len());
|
|
if (!new_source)
|
|
return NULL;
|
|
|
|
new_source->name = nut;
|
|
|
|
program.sources.Remove(old_source);
|
|
program.sources.Add(new_source);
|
|
return new_source;
|
|
}
|
|
//------------------------------------------------------------------------------
|