libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmTextTemplate.c 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmLinkedHeap.h"
  9. #include "cmText.h"
  10. #include "cmFile.h"
  11. #include "cmTextTemplate.h"
  12. /*
  13. $var$ // global var
  14. ${name$ $var0$ $var1$ $}$ // name.var0 name.var1
  15. ${name$ $var0$ $var1$ $}$ // name.var0 name.var1
  16. replace(tpl,"val","var0") // var0 = val
  17. replace(tpl,"val","name","var0",NULL) // name.var0 = val - named assignment
  18. replace(tpl,"val0","val0","name") // name.0=val0; name.1=val1 - place assignment
  19. repeat( tpl, "name" ) // create a copy of "name" just after name
  20. clean( tpl, "name" ) // remove unused variables from "name"
  21. */
  22. cmTtH_t cmTtNullHandle = cmSTATIC_NULL_HANDLE;
  23. enum
  24. {
  25. kVarTtId, // variable node
  26. kValTtId, // value (leaf) node
  27. kTextTtId // text
  28. };
  29. typedef struct cmTtToken_str
  30. {
  31. unsigned typeId;
  32. cmChar_t* text;
  33. cmChar_t* end;
  34. struct cmTtToken_str* next;
  35. struct cmTtToken_str* prev;
  36. } cmTtToken_t;
  37. typedef struct cmTtNode_str
  38. {
  39. unsigned typeId;
  40. cmChar_t* label;
  41. cmTtToken_t* token;
  42. struct cmTtNode_str* parent;
  43. struct cmTtNode_str* children;
  44. struct cmTtNode_str* sibling;
  45. } cmTtNode_t;
  46. typedef struct
  47. {
  48. cmErr_t err;
  49. cmLHeapH_t lhH;
  50. cmChar_t* buf;
  51. cmChar_t* fn;
  52. cmTtNode_t* tree;
  53. cmTtToken_t* tokens;
  54. } cmTt_t;
  55. cmTt_t* _cmTtHandleToPtr( cmTtH_t h )
  56. {
  57. cmTt_t* p = (cmTt_t*)h.h;
  58. assert( p != NULL );
  59. return p;
  60. }
  61. cmTtRC_t _cmTtFinalize( cmTt_t* p )
  62. {
  63. cmTtRC_t rc = kOkTtRC;
  64. if( p == NULL )
  65. return rc;
  66. cmLHeapDestroy(&p->lhH);
  67. cmMemPtrFree(&p->buf);
  68. cmMemFree(p);
  69. return rc;
  70. }
  71. cmTtToken_t* _cmTtCreateToken( cmTt_t* p, unsigned typeId, cmChar_t* s0, cmChar_t* s1 )
  72. {
  73. cmTtToken_t* tp = p->tokens;
  74. cmTtToken_t* t = cmLhAllocZ(p->lhH,cmTtToken_t,1);
  75. t->typeId = kVarTtId;
  76. t->text = s0;
  77. t->end = s1;
  78. if( tp == NULL )
  79. p->tokens = t;
  80. else
  81. {
  82. while( tp->next!=NULL )
  83. tp=tp->next;
  84. tp->next = t;
  85. t->prev = tp;
  86. }
  87. return tp;
  88. }
  89. cmTtRC_t _cmTtScan( cmTt_t* p, cmChar_t* s)
  90. {
  91. enum { kBeg, kEnd, kQuote };
  92. cmTtRC_t rc = kOkTtRC;
  93. unsigned i = 0;
  94. unsigned line = 1;
  95. unsigned state = kBeg;
  96. cmChar_t* s0 = s;
  97. for(; rc==kOkTtRC && s[i]; ++i)
  98. {
  99. cmChar_t c = s[i];
  100. if( c == '\n')
  101. ++line;
  102. switch(state)
  103. {
  104. case kBeg: // searching for begin '$'
  105. switch( c )
  106. {
  107. case '$':
  108. {
  109. _cmTtCreateToken(p,kTextTtId,s0,s+i-1);
  110. state = kEnd;
  111. s0 = s + i;
  112. }
  113. break;
  114. case '"':
  115. state = kQuote;
  116. break;
  117. }
  118. case kEnd: // searching for end '$'
  119. switch(c)
  120. {
  121. case '$':
  122. {
  123. _cmTtCreateToken(p,kVarTtId,s0,s+i);
  124. state = kBeg;
  125. s0 = s + i + 1;
  126. }
  127. break;
  128. case '\n':
  129. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A end-of-line was encountered inside a template variable on line %i in '%s'.",line,p->fn);
  130. break;
  131. case '"':
  132. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a template variable on line %i in '%s'.",line,p->fn);
  133. break;
  134. }
  135. break;
  136. case kQuote: // searching for '"'
  137. switch(c)
  138. {
  139. case '"':
  140. state = kBeg;
  141. break;
  142. case '\n':
  143. rc = cmErrMsg(&p->err,kSyntaxErrTtRC,"A double-quote character was found inside a quoted string on line %i in '%s'.",line,p->fn);
  144. break;
  145. }
  146. break;
  147. default:
  148. { assert(0); }
  149. }
  150. }
  151. return rc;
  152. }
  153. bool _cmTtTokenIsBegin( cmTtToken_t* tp )
  154. {
  155. assert(tp->text!=NULL && tp->text[0]=='$');
  156. return tp->typeId==kVarTtId && tp->text[1]=='{';
  157. }
  158. bool _cmTtTokenIsEnd( cmTtToken_t* tp )
  159. {
  160. assert(tp->text!=NULL && tp->text[0]=='$');
  161. return tp->typeId==kVarTtId && tp->text[1]=='}';
  162. }
  163. cmTtNode_t* _cmTtCreateNode( cmTt_t* p, cmTtNode_t* parent, unsigned typeId, cmTtToken_t* tp )
  164. {
  165. cmTtNode_t* nnp = cmLhAllocZ(p->lhH,cmTtNode_t,1);
  166. nnp->typeId = typeId;
  167. nnp->token = tp;
  168. nnp->parent = parent;
  169. if( parent != NULL )
  170. {
  171. if( parent->children == NULL )
  172. parent->children = nnp;
  173. else
  174. {
  175. cmTtNode_t* np = nnp->children;
  176. while( np->sibling != NULL )
  177. np=np->sibling;
  178. np->sibling = nnp;
  179. }
  180. }
  181. return nnp;
  182. }
  183. cmTtToken_t* _cmTtBuildTree( cmTt_t* p, cmTtNode_t* np, cmTtToken_t* tp )
  184. {
  185. cmTtToken_t* ftp = tp;
  186. int cnt = 0;
  187. while( tp != NULL )
  188. {
  189. if( _cmTtTokenIsBegin(tp) )
  190. {
  191. // attach preceding text to new right-most leaf-node on 'np'.
  192. _cmTtCreateNode(p,np,kTextTtId,ftp);
  193. // break the token chain before the 'begin' token
  194. if( tp->prev != NULL )
  195. tp->prev->next = NULL;
  196. tp->prev = NULL;
  197. // create a new child variable node and advance to token string
  198. tp = _cmTtBuildTree(p, _cmTtCreateNode(p,np,kVarTtId,NULL), tp->next );
  199. ftp = tp;
  200. ++cnt;
  201. }
  202. if( _cmTtTokenIsEnd(tp) )
  203. {
  204. --cnt;
  205. // break the token chain after the 'end' token
  206. if( tp->next != NULL )
  207. tp->next->prev = NULL;
  208. tp->next = NULL;
  209. // create a new right-most leaf-node
  210. _cmTtCreateNode(p,np,kTextTtId,ftp);
  211. tp = tp->next;
  212. break;
  213. }
  214. tp = tp->next;
  215. }
  216. if( cnt != 0 )
  217. cmErrMsg(&p->err,kSyntaxErrTtRC,"The template file '%s' appears to have unbalanced begin/end markers.",cmStringNullGuard(p->fn));
  218. return tp;
  219. }
  220. cmTtRC_t cmTextTemplateInitialize( cmCtx_t* ctx, cmTtH_t* hp, const cmChar_t* fn )
  221. {
  222. cmTtRC_t rc;
  223. if((rc = cmTextTemplateFinalize(hp)) != kOkTtRC )
  224. return rc;
  225. cmTt_t* p = cmMemAllocZ(cmTt_t,1);
  226. cmErrSetup(&p->err,&ctx->rpt,"TextTemplate");
  227. // create the local linked heap
  228. if( cmLHeapIsValid(p->lhH = cmLHeapCreate(1024, ctx )) == false )
  229. {
  230. rc = cmErrMsg(&p->err,kLHeapFailTtRC,"Lheap Mgr. allocation failed.");
  231. goto errLabel;
  232. }
  233. // read the template file
  234. if((p->buf = cmFileFnToBuf(fn,p->err.rpt,NULL)) == NULL )
  235. {
  236. rc = cmErrMsg(&p->err,kFileFailTtRC,"Unable to open the file '%s'.",cmStringNullGuard(fn));
  237. goto errLabel;
  238. }
  239. // store the template file name
  240. p->fn = cmLhAllocStr(p->lhH,fn);
  241. // break the template file into tokens
  242. if((rc = _cmTtScan(p,p->buf)) != kOkTtRC )
  243. goto errLabel;
  244. // create the root node
  245. p->tree = _cmTtCreateNode(p,NULL,kVarTtId,NULL);
  246. // build the node tree
  247. _cmTtBuildTree(p,p->tree,p->tokens);
  248. // check for errors
  249. rc = cmErrLastRC(&p->err);
  250. errLabel:
  251. if( rc != kOkTtRC )
  252. _cmTtFinalize(p);
  253. return rc;
  254. }
  255. cmTtRC_t cmTextTemplateFinalize( cmTtH_t* hp )
  256. {
  257. cmTtRC_t rc = kOkTtRC;
  258. if( hp==NULL || cmTextTemplateIsValid(*hp)==false )
  259. return rc;
  260. cmTt_t* p = _cmTtHandleToPtr(*hp);
  261. if((rc = _cmTtFinalize(p)) != kOkTtRC )
  262. return rc;
  263. hp->h = NULL;
  264. return rc;
  265. }
  266. bool cmTextTemplateIsValid( cmTtH_t h )
  267. { return h.h != NULL; }
  268. void cmTextTemplatePrintTokens( cmTtH_t h, cmRpt_t* rpt )
  269. {
  270. cmTt_t* p = _cmTtHandleToPtr(h);
  271. cmTtToken_t* tp = p->tokens;
  272. cmChar_t* ep = p->buf + strlen(p->buf);
  273. for(; tp!=NULL; tp=tp->next)
  274. {
  275. bool fl = tp->end < ep;
  276. cmChar_t c = fl ? tp->end[1] : 0;
  277. cmRptPrintf(rpt,"%s",tp->text);
  278. if( fl )
  279. tp->end[1] = c;
  280. }
  281. }
  282. cmTtRC_t cmTextTemplateTest( cmCtx_t* ctx, const cmChar_t* fn )
  283. {
  284. cmTtRC_t rc;
  285. cmTtH_t h = cmTtNullHandle;
  286. if((rc = cmTextTemplateInitialize(ctx,&h,fn)) != kOkTtRC )
  287. return rc;
  288. cmTextTemplatePrintTokens(h,&ctx->rpt);
  289. cmTextTemplateFinalize(&h);
  290. return rc;
  291. }