/* ----------------------------------------------------------------------------- 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 *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 *_symbols, Symbol::Type _type) : s(_s), e(_e), o(_o), symbols(_symbols), type(_type) {} }; //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void ParseContext(ParserContext &); bool ParseMemberVariable(ParserContext &ctx) { AutoPtr _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 _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 _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(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(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(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 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; } //------------------------------------------------------------------------------