libcm/cmTextTemplate.c

374 lines
7.8 KiB
C
Raw Normal View History

2012-11-06 21:25:33 +00:00
#include "cmPrefix.h"
#include "cmGlobal.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmLinkedHeap.h"
#include "cmText.h"
#include "cmFile.h"
#include "cmTextTemplate.h"
/*
$var$ // global var
${name$ $var0$ $var1$ $}$ // name.var0 name.var1
${name$ $var0$ $var1$ $}$ // name.var0 name.var1
replace(tpl,"val","var0") // var0 = val
replace(tpl,"val","name","var0",NULL) // name.var0 = val - named assignment
replace(tpl,"val0","val0","name") // name.0=val0; name.1=val1 - place assignment
repeat( tpl, "name" ) // create a copy of "name" just after name
clean( tpl, "name" ) // remove unused variables from "name"
*/
cmTtH_t cmTtNullHandle = cmSTATIC_NULL_HANDLE;
enum
{
kVarTtId, // variable node
kValTtId, // value (leaf) node
kTextTtId // text
};
typedef struct cmTtToken_str
{
unsigned typeId;
cmChar_t* text;
cmChar_t* end;
struct cmTtToken_str* next;
struct cmTtToken_str* prev;
} cmTtToken_t;
typedef struct cmTtNode_str
{
unsigned typeId;
cmChar_t* label;
cmTtToken_t* token;
struct cmTtNode_str* parent;
struct cmTtNode_str* children;
struct cmTtNode_str* sibling;
} cmTtNode_t;
typedef struct
{
cmErr_t err;
cmLHeapH_t lhH;
cmChar_t* buf;
cmChar_t* fn;
cmTtNode_t* tree;
cmTtToken_t* tokens;
} cmTt_t;
cmTt_t* _cmTtHandleToPtr( cmTtH_t h )
{
cmTt_t* p = (cmTt_t*)h.h;
assert( p != NULL );
return p;
}
cmTtRC_t _cmTtFinalize( cmTt_t* p )
{
cmTtRC_t rc = kOkTtRC;
if( p == NULL )
return rc;
cmLHeapDestroy(&p->lhH);
cmMemPtrFree(&p->buf);
cmMemFree(p);
return rc;
}
cmTtToken_t* _cmTtCreateToken( cmTt_t* p, unsigned typeId, cmChar_t* s0, cmChar_t* s1 )
{
cmTtToken_t* tp = p->tokens;
cmTtToken_t* t = cmLhAllocZ(p->lhH,cmTtToken_t,1);
t->typeId = kVarTtId;
t->text = s0;
t->end = s1;
if( tp == NULL )
p->tokens = t;
else
{
while( tp->next!=NULL )
tp=tp->next;
tp->next = t;
t->prev = tp;
}
return tp;
}
cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
{
enum { kBeg, kEnd, kQuote };
cmTtRC_t rc = kOkTtRC;
unsigned i = 0;
unsigned line = 1;
unsigned state = kBeg;
cmChar_t* s0 = s;
for(; rc==kOkTtRC && s[i]; ++i)
{
cmChar_t c = s[i];
if( c == '\n')
++line;
switch(state)
{
case kBeg: // searching for begin '$'
switch( c )
{
case '$':
{
_cmTtCreateToken(p,kTextTtId,s0,s+i-1);
state = kEnd;
s0 = s + i;
}
break;
case '"':
state = kQuote;
break;
}
case kEnd: // searching for end '$'
switch(c)
{
case '$':
{
_cmTtCreateToken(p,kVarTtId,s0,s+i);
state = kBeg;
s0 = s + i + 1;
}
break;
case '\n':
rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A end-of-line was encountered inside a template variable on line %i in '%s'.",line,p->fn);
break;
case '"':
rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a template variable on line %i in '%s'.",line,p->fn);
break;
}
break;
case kQuote: // searching for '"'
switch(c)
{
case '"':
state = kBeg;
break;
case '\n':
rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a quoted string on line %i in '%s'.",line,p->fn);
break;
}
break;
default:
{ assert(0); }
}
}
return rc;
}
bool _cmTtTokenIsBegin( cmTtToken_t* tp )
{
assert(tp->text!=NULL && tp->text[0]=='$');
return tp->typeId==kVarTtId && tp->text[1]=='{';
}
bool _cmTtTokenIsEnd( cmTtToken_t* tp )
{
assert(tp->text!=NULL && tp->text[0]=='$');
return tp->typeId==kVarTtId && tp->text[1]=='}';
}
cmTtNode_t* _cmTtCreateNode( cmTt_t* p, cmTtNode_t* parent, unsigned typeId, cmTtToken_t* tp )
{
cmTtNode_t* nnp = cmLhAllocZ(p->lhH,cmTtNode_t,1);
nnp->typeId = typeId;
nnp->token = tp;
nnp->parent = parent;
if( parent != NULL )
{
if( parent->children == NULL )
parent->children = nnp;
else
{
cmTtNode_t* np = nnp->children;
while( np->sibling != NULL )
np=np->sibling;
np->sibling = nnp;
}
}
return nnp;
}
cmTtToken_t* _cmTtBuildTree( cmTt_t* p, cmTtNode_t* np, cmTtToken_t* tp )
{
cmTtToken_t* ftp = tp;
int cnt = 0;
while( tp != NULL )
{
if( _cmTtTokenIsBegin(tp) )
{
// attach preceding text to new right-most leaf-node on 'np'.
_cmTtCreateNode(p,np,kTextTtId,ftp);
// break the token chain before the 'begin' token
if( tp->prev != NULL )
tp->prev->next = NULL;
tp->prev = NULL;
// create a new child variable node and advance to token string
tp = _cmTtBuildTree(p, _cmTtCreateNode(p,np,kVarTtId,NULL), tp->next );
ftp = tp;
++cnt;
}
if( _cmTtTokenIsEnd(tp) )
{
--cnt;
// break the token chain after the 'end' token
if( tp->next != NULL )
tp->next->prev = NULL;
tp->next = NULL;
// create a new right-most leaf-node
_cmTtCreateNode(p,np,kTextTtId,ftp);
tp = tp->next;
break;
}
tp = tp->next;
}
if( cnt != 0 )
cmErrMsg(&p->err,kSyntaxErrTtRC,"The template file '%s' appears to have unbalanced begin/end markers.",cmStringNullGuard(p->fn));
return tp;
}
cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn )
{
cmTtRC_t rc;
if((rc = cmTextTemplateFinalize(hp)) != kOkTtRC )
return rc;
cmTt_t* p = cmMemAllocZ(cmTt_t,1);
cmErrSetup(&p->err,&ctx->rpt,"TextTemplate");
// create the local linked heap
if( cmLHeapIsValid(p->lhH = cmLHeapCreate(1024, ctx )) == false )
{
rc = cmErrMsg(&p->err,kLHeapFailTtRC,"Lheap Mgr. allocation failed.");
goto errLabel;
}
// read the template file
if((p->buf = cmFileFnToBuf(fn,p->err.rpt,NULL)) == NULL )
{
rc = cmErrMsg(&p->err,kFileFailTtRC,"Unable to open the file '%s'.",cmStringNullGuard(fn));
goto errLabel;
}
// store the template file name
p->fn = cmLhAllocStr(p->lhH,fn);
// break the template file into tokens
if((rc = _cmTtScan(p,p->buf)) != kOkTtRC )
goto errLabel;
// create the root node
p->tree = _cmTtCreateNode(p,NULL,kVarTtId,NULL);
// build the node tree
_cmTtBuildTree(p,p->tree,p->tokens);
// check for errors
rc = cmErrLastRC(&p->err);
errLabel:
if( rc != kOkTtRC )
_cmTtFinalize(p);
return rc;
}
cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp )
{
cmTtRC_t rc = kOkTtRC;
if( hp==NULL || cmTextTemplateIsValid(*hp)==false )
return rc;
cmTt_t* p = _cmTtHandleToPtr(*hp);
if((rc = _cmTtFinalize(p)) != kOkTtRC )
return rc;
hp->h = NULL;
return rc;
}
bool cmTextTemplateIsValid( cmTtH_t h )
{ return h.h != NULL; }
void cmTextTemplatePrintTokens( cmTtH_t h, cmRpt_t* rpt )
{
cmTt_t* p = _cmTtHandleToPtr(h);
cmTtToken_t* tp = p->tokens;
cmChar_t* ep = p->buf + strlen(p->buf);
for(; tp!=NULL; tp=tp->next)
{
bool fl = tp->end < ep;
cmChar_t c = fl ? tp->end[1] : 0;
cmRptPrintf(rpt,"%s",tp->text);
if( fl )
tp->end[1] = c;
}
}
cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn )
{
cmTtRC_t rc;
cmTtH_t h = cmTtNullHandle;
if((rc = cmTextTemplateInitialize(ctx,&h,fn)) != kOkTtRC )
return rc;
cmTextTemplatePrintTokens(h,&ctx->rpt);
cmTextTemplateFinalize(&h);
return rc;
}