|
|
/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
tpl.cpp
Abstract:
template file interpreter for tracewpp.exe Author:
Gor Nishanov (gorn) 03-Apr-1999
Revision History:
Gor Nishanov (gorn) 03-Apr-1999 -- hacked together to prove that this can work GorN: 29-Sep-2000 - fix WHERE clause handling
ToDo:
Clean it up
--*/
#define UNICODE
#include <stdio.h>
#include <windows.h>
#pragma warning(disable: 4786)
#pragma warning(disable: 4503) // decorated length
#pragma warning(disable: 4512) // cannot generate assignment
#pragma warning(disable: 4100) // '_P' : unreferenced formal parameter
#pragma warning(disable: 4018) // signed/unsigned mismatch
#pragma warning(disable: 4267) // 'return' : conversion from 'size_t' to 'int'
#include <xmemory>
#include <xstring>
#include <set>
#include <map>
#pragma warning(disable: 4663 4018)
#include <vector>
//#pragma warning(default: 4018 4663) // signed/unsigned mismatch
#pragma warning(default: 4100)
#include "ezparse.h"
#include "fieldtable.h"
#include "tpl.h"
LPCSTR FieldNames[] = { #define FIELD_NAME(f) #f,
INSERT_FIELD_NAMES #undef FIELD_NAME
};
OBJECT_MAP ObjectMap;
typedef std::map<std::string, FieldId, strless> FIELD_MAP;
FIELD_MAP FieldMap;
void PopulateFieldMap() { #define FIELD_NAME(_name_) FieldMap[#_name_] = fid_ ## _name_;
INSERT_FIELD_NAMES #undef FIELD_NAME
FIELD_MAP::iterator i; }
////////////////////////////////////////////////////////////////////////////////////////////
struct LoopVar : FieldHolder { Enumerator * Enum; std::string Name;
LoopVar() {}
DWORD PrintField(int fieldId, FILE* f, const Enumerator** pEnum) const { return Enum->GetData()->PrintField(fieldId, f, pEnum); } };
///////////////////////////////////////////////////////////////////////////////////////////
char Delimiter = '`';
std::string COMMENT("*");
std::string FORALL("FORALL"); std::string ENDFOR("ENDFOR");
std::string IF("IF"); std::string ENDIF("ENDIF");
std::string DELIMITER("DELIMITER"); std::string INCLUDE("INCLUDE"); std::string ENV("ENV");
typedef enum Action { actText, actVar, actLoop, actIf, actInclude, actLiteralString, } Action;
#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union
struct Chunk { struct { Action action : 8; UCHAR level : 8; SHORT loopEnd:16; }; union { struct { LPCSTR textBeg; LPCSTR textEnd; }; struct { FieldHolder * p; FieldId FieldNo; std::string Filter; }; };
Enumerator* getEnum() { const Enumerator* Enum; p->PrintField(FieldNo, 0, &Enum); return (Enumerator*)Enum; } void printField(FILE* out) const { p->PrintField(FieldNo, out, 0); }
Chunk(){} // to make vector happy
Chunk (Action Act, FieldHolder* fh, FieldId fid, int lvl, const std::string& filter): action(Act),FieldNo(fid),p(fh),level((UCHAR)lvl),Filter(filter) {} Chunk (FieldHolder* fh, FieldId fid):action(actVar),FieldNo(fid),p(fh) {} Chunk (LPCSTR b, LPCSTR e):action(actText),textBeg(b),textEnd(e) {} Chunk (Action act, LPCSTR b, LPCSTR e):action(act),textBeg(b),textEnd(e) {} explicit Chunk(std::string const& Text):action(actLiteralString),Filter(Text) {} };
#define MAX_LOOP_LEVEL 127
struct TemplateProcessor { LoopVar Loop[MAX_LOOP_LEVEL]; std::vector<Chunk> Chunks;
void RunIt(int beg, int end, FILE* out) { for(int i = beg; i < end; ) { switch(Chunks[i].action) { case actLiteralString: { fwrite(Chunks[i].Filter.c_str(), Chunks[i].Filter.length(), 1, out ); ++i; break; } case actText: { for(LPCSTR p = Chunks[i].textBeg; p < Chunks[i].textEnd; ++p) { if (*p != '\r') putc(*p, out); } ++i; break; } case actVar: { Chunks[i].printField(out); ++i; break; } case actIf: { if (!Chunks[i].p->Hidden(Chunks[i].Filter)) { RunIt(i+1, Chunks[i].loopEnd, out); } i = Chunks[i].loopEnd; break; } case actLoop: { Enumerator * Enum = Chunks[i].getEnum(); Loop[Chunks[i].level].Enum = Enum; for(Enum->Reset(Chunks[i].Filter); Enum->Valid(); Enum->Next(Chunks[i].Filter) ) { RunIt(i+1, Chunks[i].loopEnd, out); } delete Enum; i = Chunks[i].loopEnd; break; } case actInclude: { ProcessTemplate(Chunks[i].textBeg, Chunks[i].textEnd, out); ++i; break; } } } }
void DoId(LPCSTR q, LPCSTR p, FieldId& fid, FieldHolder*& fh) { LPCSTR dot = q;
while (q < p && isspace(*q)) ++q; while (q < p && isspace(p[-1])) --p;
while (dot < p && *dot != '.') ++dot;
std::string ObjectName(q, dot); OBJECT_MAP::iterator it = ObjectMap.find( ObjectName );
if (it == ObjectMap.end()) { ReportError("Var not found: %s\n", ObjectName.c_str() ); exit(1); } else { std::string FieldName; if (dot == p) { fid = (FieldId)fid___default__; FieldName.assign("__default__"); } else { ++dot; while (p < dot && isspace(*dot)) ++dot;
FieldName.assign(dot,p); FIELD_MAP::iterator fit = FieldMap.find( FieldName.c_str() ); if (fit == FieldMap.end()) { ReportError("FieldNotFound: %s.%s\n", ObjectName.c_str(), FieldName.c_str() ); exit(1); } else { fid = fit->second; } } } fh = it->second; }
void DoVar(LPCSTR q, LPCSTR p) { FieldHolder* fh; FieldId fid;
DoId(q,p, fid, fh); Chunks.push_back( Chunk(fh, fid) ); }
void DoLoop(int loopLevel, LPCSTR beg, LPCSTR end) { FieldHolder* fh; FieldId fid;
std::string LoopVar; std::string LoopSet; std::string Filter;
LPCSTR p,q;
p = beg+6; while (p < end && isspace(*p)) ++p; q = p; while(p < end && !isspace(*p)) ++p;
LoopVar.assign(q,p); Loop[loopLevel].Name = LoopVar;
p += 4; while (p < end && isspace(*p)) ++p; q = p; while(p < end && !isspace(*p)) ++p; DoId(q,p, fid, fh); LoopSet.assign(q, p);
p += 7; while (p < end && isspace(*p)) ++p; q = p; if (q < end) { p = end; while(p > q && isspace(*--p)); if (p < end && !isspace(*p)) ++p; }
Filter.assign(q,p);
Flood("FORALL %s IN %s WHERE %s\n", LoopVar.c_str(), LoopSet.c_str(), Filter.c_str()); ObjectMap[LoopVar] = &Loop[loopLevel];
Chunks.push_back( Chunk(actLoop, fh, fid, loopLevel, Filter) ); }
void DoIf(int loopLevel, LPCSTR beg, LPCSTR end) { FieldHolder* fh; FieldId fid;
std::string Object; std::string Filter;
LPCSTR p,q;
p = beg+3; while (p < end && isspace(*p)) ++p; q = p; while(p < end && !isspace(*p)) ++p;
DoId(q,p, fid, fh); // Split id //
Object.assign(q, p);
while (p < end && isspace(*p)) ++p; while (p < end && isspace(end[-1]) ) --end;
Filter.assign(p,end);
Flood("IF %s %s\n", Object.c_str(), Filter.c_str() ); Chunks.push_back( Chunk(actIf, fh, fid, loopLevel, Filter) ); }
DWORD CompileAndRun( IN LPCSTR begin, IN LPCSTR end, IN FILE* out ) { LPCSTR p = begin, PlainText = begin; int loop = -1; int loopLevel = -1; bool comment;
Chunks.erase(Chunks.begin(), Chunks.end()); Chunks.reserve(128);
for(;;) { LPCSTR q; for(;;) { if (p == end) { Chunks.push_back( Chunk(PlainText, p) ); goto done; } if (*p == Delimiter) break; ++p; } q = ++p; comment = (p < end && *p == '*'); for(;;) { if (p == end) { ReportError("Unmatched delimiters\n"); exit(1); } if (*p == Delimiter) { if (comment) { if (p[-1] == '*') break; } else { break; } } ++p; } if (q-1 > PlainText) { Chunks.push_back( Chunk(PlainText, q-1) ); } if (p == q) { // PERFPERF If the previous chunk was a text, we can extend it
Chunks.push_back( Chunk(q-1, p) ); } else { std::string x(q,p); if (x.compare(0, IF.size(), IF) == 0) { int previous = loop; // KLUDGE merge with FORALL
if (loopLevel == MAX_LOOP_LEVEL) { ReportError("Too many nested blocks!\n"); exit(1); } ++loopLevel;
loop = static_cast<int>( Chunks.size() );
DoIf(loopLevel, q,p);
if (previous >= 32765) { ReportError("Too many chunks. Make loopEnd a UINT, %d\n", previous); exit(1); } Chunks.back().loopEnd = (SHORT)previous;
while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else if (x.compare(0, FORALL.size(), FORALL) == 0) { int previous = loop;
if (loopLevel == MAX_LOOP_LEVEL) { ReportError("Too many nested loops!\n"); exit(1); } ++loopLevel;
loop = static_cast<int>( Chunks.size() );
DoLoop(loopLevel, q,p);
if (previous >= 32765) { ReportError("Too many chunks. Make loopEnd a UINT, %d\n", previous); exit(1); } Chunks.back().loopEnd = (SHORT)previous;
while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else if (x.compare(0, DELIMITER.size(), DELIMITER) == 0 && x.size() > 10) { Delimiter = x[10]; } else if (x.compare(0, ENV.size(), ENV) == 0) { // we need to replace this field with
// the value of the specified env variable
LPCSTR val = getenv( std::string(q+4,p).c_str() ); if (val != NULL) { Chunks.push_back( Chunk(std::string(val)) ); } } else if (x.compare(0, COMMENT.size(), COMMENT) == 0) { // eat away the whitespace
while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else if (x.compare(0, INCLUDE.size(), INCLUDE) == 0) { // Doesn't work
Chunks.push_back( Chunk(actInclude, q + INCLUDE.size() + 1, p) ); } else if ((x.compare(0, ENDIF.size(), ENDIF) == 0) || (x.compare(0, ENDFOR.size(), ENDFOR) == 0)) {
// KLUDGE make them separate or rename both to simply END
// End will be set in ENDFOR //
if (loop == -1) { ReportError("ENDFOR without FORALL\n"); exit(1); }
ObjectMap.erase( Loop[loopLevel].Name );
int previous = Chunks[loop].loopEnd;
// BUGBUG have a check that confirms that we didn't run out of space
Chunks[loop].loopEnd = (SHORT)Chunks.size();
loop = previous; --loopLevel;
while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else { DoVar(q, p); } } PlainText = ++p; } done:; if (loop != -1) { ReportError("No ENDFOR for loop, %d\n", loop); exit(1); }
RunIt(0, static_cast<int>( Chunks.size() ), out);
return 0; } };
DWORD processTemplate( IN LPCSTR begin, IN LPCSTR end, IN EZPARSE_CALLBACK, IN PVOID Context, IN PEZPARSE_CONTEXT ) { FILE *out = (FILE*)Context; TemplateProcessor tpl; return tpl.CompileAndRun(begin,end,out); }
|