My Project
NPLInterface.hpp
1 #pragma once
2 //-----------------------------------------------------------------------------
3 // Class: NPLInterface
4 // Authors: LiXizhi
5 // Emails: LiXizhi@yeah.net
6 // Company: ParaEngine Co.
7 // Date: 2010.2.23
8 // Desc: This file can be used by a ParaEngine plugin to parse NPL messages without the need to link with the ParaEngine library.
9 //-----------------------------------------------------------------------------
13 // #define DISABLE_STATIC_VARIABLE
14 #include <boost/intrusive_ptr.hpp>
15 #include "PEtypes.h"
16 #include "NPLTypes.h"
17 #include "INPL.h"
18 #include <vector>
19 #include <map>
20 
21 #pragma region CommonHeaders
22 #ifndef MAX_TABLE_STRING_LENGTH
23 #define MAX_TABLE_STRING_LENGTH 512
24 #endif
25 
26 #ifndef MAX_DEBUG_STRING_LENGTH
27 #define MAX_DEBUG_STRING_LENGTH 1024
28 #endif
29 
30 #ifndef OUTPUT_LOG
31 #define OUTPUT_LOG printf
32 #endif
33 
34 #ifndef LUA_NUMBER_FMT
35 #define LUA_NUMBER_FMT "%.14g"
36 #endif
37 
38 #ifdef WIN32
39 #define snprintf _snprintf
40 #endif
41 
42 
43 namespace NPLInterface
44 {
57  {
58  public:
59  mutable long m_ref_count;
60 
61  intrusive_ptr_single_thread_base() : m_ref_count(0){};
63  };
64 
68  template <typename T>
69  void intrusive_ptr_add_ref(T* ref)
70  {
71  // increment reference count of object *ref
72  ++(ref->m_ref_count);
73  }
74 
75  template <typename T>
76  void intrusive_ptr_release(T* ref)
77  {
78  // decrement reference count, and delete object when reference count reaches 0
79  if (--(ref->m_ref_count) == 0)
80  delete ref;
81  }
82 }
83 #pragma endregion CommonHeaders
84 
85 namespace NPLInterface
86 {
87  using std::vector;
88 
89  struct LexState;
90  class NPLObjectBase;
91  class NPLNumberObject;
92  class NPLBoolObject;
93  class NPLTable;
94  class NPLStringObject;
95  typedef boost::intrusive_ptr<NPLObjectBase> NPLObjectBase_ptr;
96  typedef boost::intrusive_ptr<NPLTable> NPLTable_ptr;
97  typedef boost::intrusive_ptr<NPLNumberObject> NPLNumberObject_ptr;
98  typedef boost::intrusive_ptr<NPLBoolObject> NPLBoolObject_ptr;
99  typedef boost::intrusive_ptr<NPLStringObject> NPLStringObject_ptr;
100 
101 #pragma region NPLParser
102  /* semantics information */
103  struct SemInfo
104  {
105  public:
106  double r;
107  string ts;
108  };
109 
110  struct Token
111  {
112  int token;
113  SemInfo seminfo;
114  };
115 
116  struct Zio {
117  size_t n; /* bytes still unread */
118  const char *p; /* current position in buffer */
119 
120  Zio(){};
121  Zio(const char *input, size_t nSize) :p(input), n(nSize){};
122  };
123 
125  struct LexState
126  {
127  int current; /* current character (charint) */
128  int linenumber; /* input line counter */
129  int lastline; /* line of last token `consumed' */
130  Token t; /* current token */
131  Token lookahead; /* look ahead token */
132  Zio* z; /* input stream */
133  vector<char> buff; /* buffer for tokens */
134  int nestlevel; /* level of nested non-terminals */
136  bool bSucceed;
137  };
138 
139 #define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
140 
141  /* ORDER RESERVED */
142  static const char * token2string[] = {
143  "and", "break", "do", "else", "elseif",
144  "end", "false", "for", "function", "if",
145  "in", "local", "nil", "not", "or", "repeat",
146  "return", "then", "true", "until", "while", "*name",
147  "..", "...", "==", ">=", "<=", "~=",
148  "*number", "*string", "<eof>"
149  };
150 
152  class NPLLex
153  {
154  public:
155  /* end of stream */
156  static const int EOZ = -1;
157  static const int MAX_INT = 65530;
158  static const int FIRST_RESERVED = 257;
159 
160  /* maximum number of chars that can be read without checking buffer size */
161  static const int MAXNOCHECK = 5;
162  /* extra space to allocate when growing buffer */
163  static const int EXTRABUFF = 32;
164 
165  /* maximum length of a reserved word */
166  static const int TOKEN_LEN = (sizeof("function") / sizeof(char));
167 
168  enum RESERVED {
169  /* terminal symbols denoted by reserved words */
170  TK_AND = FIRST_RESERVED, TK_BREAK,
171  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
172  TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
173  TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
174  /* other terminal symbols */
175  TK_NAME, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
176  TK_STRING, TK_EOS
177  };
178  /* number of reserved words */
179  static const int NUM_RESERVED = (int)TK_WHILE - FIRST_RESERVED + 1;
180 
181  private:
182  LexState m_lexState;
183  Zio m_zio;
184  public:
185  LexState * GetState(){ return &m_lexState; };
186 
187  LexState * SetInput(const char* input, int nLen)
188  {
189  m_zio.p = input;
190  m_zio.n = nLen;
191  if (nLen <= 0)
192  {
193  m_zio.n = strlen(input);
194  }
195 
196  m_lexState.bSucceed = true;
197  m_lexState.lookahead.token = TK_EOS; /* no look-ahead token */
198  m_lexState.z = &m_zio;
199  m_lexState.linenumber = 1;
200  m_lexState.lastline = 1;
201  m_lexState.buff.clear();
202 
203  next(&m_lexState); /* read first char */
204  if (m_lexState.current == '#') {
205  do { /* skip first line */
206  next(&m_lexState);
207  } while (m_lexState.current != '\n' && m_lexState.current != EOZ);
208  }
209 
210  return &m_lexState;
211  }
212 
213  static void ThrowError(LexState *ls, const char* errorMsg)
214  {
215  ls->bSucceed = false;
216  throw errorMsg;
217  }
218 
219  static const char* FormatString(const char * zFormat, ...)
220  {
221  static char buf_[MAX_DEBUG_STRING_LENGTH + 1];
222  va_list args;
223  va_start(args, zFormat);
224  vsnprintf(buf_, MAX_DEBUG_STRING_LENGTH, zFormat, args);
225  va_end(args);
226  return buf_;
227  }
228 
229 
230  static const char * luaX_token2str(LexState *ls, int token)
231  {
232  if (token < FIRST_RESERVED) {
233  assert(token == (unsigned char)token);
234  return FormatString("%c", token);
235  }
236  else
237  return token2string[token - FIRST_RESERVED];
238  }
239 
240 
241  static void luaX_lexerror(LexState *ls, const char *s, int token)
242  {
243  if (token == TK_EOS)
244  luaX_error(ls, s, luaX_token2str(ls, token));
245  else
246  luaX_error(ls, s, &(ls->buff[0]));
247  }
248 
249  static void luaX_errorline(LexState *ls, const char *s, const char *token, int line)
250  {
251  ThrowError(ls, FormatString("%d: %s near `%s'", line, s, token));
252  }
253 
254  static void luaX_error(LexState *ls, const char *s, const char *token)
255  {
256  luaX_errorline(ls, s, token, ls->linenumber);
257  }
258 
259  static void luaX_syntaxerror(LexState *ls, const char *msg)
260  {
261  const char *lasttoken;
262  switch (ls->t.token) {
263  case TK_NAME:
264  lasttoken = ls->t.seminfo.ts.c_str();
265  break;
266  case TK_STRING:
267  case TK_NUMBER:
268  lasttoken = &(ls->buff[0]);
269  break;
270  default:
271  lasttoken = luaX_token2str(ls, ls->t.token);
272  break;
273  }
274  luaX_error(ls, msg, lasttoken);
275  }
276 
277  static void luaX_checklimit(LexState *ls, int val, int limit, const char *msg)
278  {
279  if (val > limit) {
280  msg = FormatString("too many %s (limit=%d)", msg, limit);
281  luaX_syntaxerror(ls, msg);
282  }
283  }
284 
285  static void inclinenumber(LexState *LS)
286  {
287  next(LS); /* skip `\n' */
288  ++LS->linenumber;
289  luaX_checklimit(LS, LS->linenumber, MAX_INT, "lines in a chunk");
290  }
291 
292  static void read_long_string(LexState *LS, SemInfo *seminfo) {
293  int cont = 0;
294  int l = 0;
295  checkbuffer(LS, l);
296  save(LS, '[', l); /* save first `[' */
297  save_and_next(LS, l); /* pass the second `[' */
298  if (currIsNewline(LS)) /* string starts with a newline? */
299  inclinenumber(LS); /* skip it */
300  bool bBreak = false;
301  for (; !bBreak;) {
302  checkbuffer(LS, l);
303  switch (LS->current) {
304  case EOZ:
305  save(LS, '\0', l);
306  luaX_lexerror(LS, (seminfo) ? "unfinished long string" :
307  "unfinished long comment", TK_EOS);
308  break; /* to avoid warnings */
309  case '[':
310  save_and_next(LS, l);
311  if (LS->current == '[') {
312  cont++;
313  save_and_next(LS, l);
314  }
315  continue;
316  case ']':
317  save_and_next(LS, l);
318  if (LS->current == ']') {
319  if (cont == 0) bBreak = true;
320  cont--;
321  save_and_next(LS, l);
322  }
323  continue;
324  case '\n':
325  case '\r': // lua 5.1 syntax fix
326  save(LS, '\n', l);
327  inclinenumber(LS);
328  if (!seminfo) l = 0; /* reset buffer to avoid wasting space */
329  continue;
330  default:
331  save_and_next(LS, l);
332  break;
333  }
334  }
335  save_and_next(LS, l); /* skip the second `]' */
336  save(LS, '\0', l);
337  if (seminfo)
338  {
339  seminfo->ts.clear();
340  seminfo->ts.append(&(LS->buff[2]), l - 5);
341  }
342  }
343 
344  static void read_string(LexState *LS, int del, SemInfo *seminfo)
345  {
346  int l = 0;
347  checkbuffer(LS, l);
348  save_and_next(LS, l);
349  while (LS->current != del) {
350  checkbuffer(LS, l);
351  switch (LS->current) {
352  case EOZ:
353  save(LS, '\0', l);
354  luaX_lexerror(LS, "unfinished string", TK_EOS);
355  break; /* to avoid warnings */
356  case '\n':
357  case '\r': // lua 5.1 syntax fix
358  save(LS, '\0', l);
359  luaX_lexerror(LS, "unfinished string", TK_STRING);
360  break; /* to avoid warnings */
361  case '\\':
362  next(LS); /* do not save the `\' */
363  switch (LS->current) {
364  case 'a': save(LS, '\a', l); next(LS); break;
365  case 'b': save(LS, '\b', l); next(LS); break;
366  case 'f': save(LS, '\f', l); next(LS); break;
367  case 'n': save(LS, '\n', l); next(LS); break;
368  case 'r': save(LS, '\r', l); next(LS); break;
369  case 't': save(LS, '\t', l); next(LS); break;
370  case 'v': save(LS, '\v', l); next(LS); break;
371  case '\n':
372  case '\r': // lua 5.1 syntax fix
373  save(LS, '\n', l); inclinenumber(LS); break;
374  case EOZ: break; /* will raise an error next loop */
375  default: {
376  if (!isdigit(LS->current))
377  save_and_next(LS, l); /* handles \\, \", \', and \? */
378  else { /* \xxx */
379  int c = 0;
380  int i = 0;
381  do {
382  c = 10 * c + (LS->current - '0');
383  next(LS);
384  } while (++i<3 && isdigit(LS->current));
385  if (c > UCHAR_MAX) {
386  save(LS, '\0', l);
387  luaX_lexerror(LS, "escape sequence too large", TK_STRING);
388  }
389  save(LS, c, l);
390  }
391  break;
392  }
393  }
394  break;
395  default:
396  save_and_next(LS, l);
397  break;
398  }
399  }
400  save_and_next(LS, l); /* skip delimiter */
401  save(LS, '\0', l);
402  if (seminfo)
403  {
404  seminfo->ts.clear();
405  seminfo->ts.append(&(LS->buff[1]), l - 3);
406  }
407  }
408 
409  static int readname(LexState *LS)
410  {
411  int l = 0;
412  checkbuffer(LS, l);
413  do {
414  checkbuffer(LS, l);
415  save_and_next(LS, l);
416  } while (isalnum(LS->current) || LS->current == '_');
417  save(LS, '\0', l);
418  return l - 1;
419  }
420 
421  static void read_numeral(LexState *LS, int comma, SemInfo *seminfo)
422  {
423  int l = 0;
424  checkbuffer(LS, l);
425  if (comma) save(LS, '.', l);
426  while (isdigit(LS->current)) {
427  checkbuffer(LS, l);
428  save_and_next(LS, l);
429  }
430  if (LS->current == '.') {
431  save_and_next(LS, l);
432  if (LS->current == '.') {
433  save_and_next(LS, l);
434  save(LS, '\0', l);
435  luaX_lexerror(LS,
436  "ambiguous syntax (decimal point x string concatenation)",
437  TK_NUMBER);
438  }
439  }
440  while (isdigit(LS->current)) {
441  checkbuffer(LS, l);
442  save_and_next(LS, l);
443  }
444  if (LS->current == 'e' || LS->current == 'E') {
445  save_and_next(LS, l); /* read `E' */
446  if (LS->current == '+' || LS->current == '-')
447  save_and_next(LS, l); /* optional exponent sign */
448  while (isdigit(LS->current)) {
449  checkbuffer(LS, l);
450  save_and_next(LS, l);
451  }
452  }
453  save(LS, '\0', l);
454  try
455  {
456  seminfo->r = atof(&LS->buff[0]);
457  }
458  catch (...)
459  {
460  luaX_lexerror(LS, "malformed number", TK_NUMBER);
461  }
462  }
463 
464  static int luaX_lex(LexState *LS, SemInfo *seminfo)
465  {
466  for (;;) {
467  switch (LS->current) {
468 
469  case '\n':
470  case '\r': {
471  inclinenumber(LS);
472  continue;
473  }
474  case '-': {
475  next(LS);
476  if (LS->current != '-') return '-';
477  /* else is a comment */
478  next(LS);
479  if (LS->current == '[' && (next(LS) == '['))
480  read_long_string(LS, NULL); /* long comment */
481  else /* short comment */
482  while (!currIsNewline(LS) && LS->current != EOZ)
483  next(LS);
484  continue;
485  }
486  case '[': {
487  next(LS);
488  if (LS->current != '[') return '[';
489  else {
490  read_long_string(LS, seminfo);
491  return TK_STRING;
492  }
493  }
494  case '=': {
495  next(LS);
496  if (LS->current != '=') return '=';
497  else { next(LS); return TK_EQ; }
498  }
499  case '<': {
500  next(LS);
501  if (LS->current != '=') return '<';
502  else { next(LS); return TK_LE; }
503  }
504  case '>': {
505  next(LS);
506  if (LS->current != '=') return '>';
507  else { next(LS); return TK_GE; }
508  }
509  case '~': {
510  next(LS);
511  if (LS->current != '=') return '~';
512  else { next(LS); return TK_NE; }
513  }
514  case '"':
515  case '\'': {
516  read_string(LS, LS->current, seminfo);
517  return TK_STRING;
518  }
519  case '.': {
520  next(LS);
521  if (LS->current == '.') {
522  next(LS);
523  if (LS->current == '.') {
524  next(LS);
525  return TK_DOTS; /* ... */
526  }
527  else return TK_CONCAT; /* .. */
528  }
529  else if (!isdigit(LS->current)) return '.';
530  else {
531  read_numeral(LS, 1, seminfo);
532  return TK_NUMBER;
533  }
534  }
535  case EOZ: {
536  return TK_EOS;
537  }
538  default: {
539  if (isspace(LS->current)) {
540  next(LS);
541  continue;
542  }
543  else if (isdigit(LS->current)) {
544  read_numeral(LS, 0, seminfo);
545  return TK_NUMBER;
546  }
547  else if (isalpha(LS->current) || LS->current == '_') {
548  /* identifier or reserved word */
549  int l = readname(LS);
550  string ts;
551  ts.append(&(LS->buff[0]), l);
552 
553  // The following code implemented below
554  //if (ts->tsv.reserved > 0) /* reserved word? */
555  // return ts->tsv.reserved - 1 + FIRST_RESERVED;
556 
557  /* reserved word? */
558  for (int i = 0; i < NUM_RESERVED; ++i)
559  {
560  if (ts == token2string[i])
561  return i + FIRST_RESERVED;
562  }
563 
564  seminfo->ts = ts;
565  return TK_NAME;
566  }
567  else {
568  int c = LS->current;
569  if (iscntrl(c))
570  luaX_error(LS, "invalid control char",
571  FormatString("char(%d)", c));
572  next(LS);
573  return c; /* single-char tokens (+ - / ...) */
574  }
575  break;
576  }
577  }
578  }//for (;;) {
579  }
580 
581  private:
583  // static lex functions
585  static inline int next(LexState *LS)
586  {
587  return LS->current = ((LS->z)->n--) > 0 ? (int)((unsigned char)(*(LS->z)->p++)) : EOZ;
588  }
589 
590  static void checkbuffer(LexState *LS, int len)
591  {
592  if (((len)+MAXNOCHECK)*sizeof(char) > LS->buff.size())
593  {
594  if (len < 500)
595  LS->buff.resize(len + EXTRABUFF);
596  else
597  {
598  // added by LiXizhi 2007.6.20: in case the file contains super large string, such as a base64 encoded file of 2MB, we will double the size instead using a fixed length.
599  LS->buff.resize(len * 2);
600  }
601  }
602  }
603  static void save(LexState *LS, char c, int& l)
604  {
605  LS->buff[l++] = (char)c;
606  }
607  static void save_and_next(LexState *LS, int& l)
608  {
609  save(LS, LS->current, l);
610  next(LS);
611  }
612 
613 
614  };
615 
619  class NPLParser
620  {
621  public:
622  /*
623  ** maximum number of syntactical nested non-terminals: Not too big,
624  ** or may overflow the C stack...
625  */
626  static const int LUA_MAXPARSERLEVEL = 200;
627 
628  public:
629 
630  NPLParser(void)
631  {
632  }
633 
634  ~NPLParser(void)
635  {
636  }
637 
638  static void next(LexState *ls)
639  {
640  ls->lastline = ls->linenumber;
641  if (ls->lookahead.token != NPLLex::TK_EOS) { /* is there a look-ahead token? */
642  ls->t = ls->lookahead; /* use this one */
643  ls->lookahead.token = NPLLex::TK_EOS; /* and discharge it */
644  }
645  else
646  ls->t.token = NPLLex::luaX_lex(ls, &ls->t.seminfo); /* read next token */
647  }
648 
649  static void lookahead(LexState *ls)
650  {
651  assert(ls->lookahead.token == NPLLex::TK_EOS);
652  ls->lookahead.token = NPLLex::luaX_lex(ls, &ls->lookahead.seminfo);
653  }
654 
655  static void error_expected(LexState *ls, int token)
656  {
657  NPLLex::luaX_syntaxerror(ls,
658  NPLLex::FormatString("`%s' expected", NPLLex::luaX_token2str(ls, token)));
659  }
660 
661  static int testnext(LexState *ls, int c)
662  {
663  if (ls->t.token == c) {
664  next(ls);
665  return 1;
666  }
667  else return 0;
668  }
669 
670  static void check_condition(LexState *ls, void* c, const char* msg)
671  {
672  if (!(c))
673  NPLLex::luaX_syntaxerror(ls, msg);
674  }
675 
676  static void check_match(LexState *ls, int what, int who, int where)
677  {
678  if (!testnext(ls, what)) {
679  if (where == ls->linenumber)
680  error_expected(ls, what);
681  else {
682  NPLLex::luaX_syntaxerror(ls, NPLLex::FormatString("`%s' expected (to close `%s' at line %d)",
683  NPLLex::luaX_token2str(ls, what), NPLLex::luaX_token2str(ls, who), where));
684  }
685  }
686  }
687 
688  static void check(LexState *ls, int c)
689  {
690  if (!testnext(ls, c))
691  error_expected(ls, c);
692  }
693 
694  static bool CheckPureDataBlock(LexState *ls)
695  {
696  // data
697  int c = ls->t.token;
698  if (c == NPLLex::TK_TRUE || c == NPLLex::TK_NIL || c == NPLLex::TK_FALSE || c == NPLLex::TK_NUMBER || c == NPLLex::TK_STRING)
699  {
700  next(ls);
701  return true;
702  }
703  else if (c == '-')
704  {
705  // negative number
706  next(ls);
707  if (ls->t.token == NPLLex::TK_NUMBER)
708  {
709  next(ls);
710  return true;
711  }
712  else
713  return false;
714  }
715  else if (c == '{')
716  {
717  enterlevel(ls);
718  bool bBreak = false;
719  next(ls);
720  while (!bBreak)
721  {
722  c = ls->t.token;
723  if (c == '}')
724  {
725  // end of table
726  leavelevel(ls);
727  next(ls);
728  bBreak = true;
729  }
730  else if (c == NPLLex::TK_NAME)
731  {
732  // by name assignment, such as name = data|table
733  next(ls);
734  if (ls->t.token == '=')
735  {
736  next(ls);
737  if (!CheckPureDataBlock(ls))
738  return false;
739  testnext(ls, ',');
740  }
741  else
742  return false;
743  }
744  else if (c == '[')
745  {
746  // by integer or string key assignment, such as [number|string] = data|table
747  next(ls);
748  if (ls->t.token == NPLLex::TK_NUMBER)
749  {
750  // TODO: verify that it is an integer, instead of a floating value.
751  }
752  else if (ls->t.token == NPLLex::TK_STRING)
753  {
754  // verify that the string is a value key(non-empty);
755  if (ls->t.seminfo.ts.empty())
756  return false;
757  }
758  else
759  return false;
760  next(ls);
761  if (ls->t.token == ']')
762  {
763  next(ls);
764  if (ls->t.token == '=')
765  {
766  next(ls);
767  if (!CheckPureDataBlock(ls))
768  return false;
769  testnext(ls, ',');
770  }
771  else
772  return false;
773  }
774  }
777  else if (c == NPLLex::TK_STRING || c == NPLLex::TK_NUMBER || c == NPLLex::TK_NIL || c == NPLLex::TK_FALSE || c == NPLLex::TK_TRUE)
778  {
779  next(ls);
780  testnext(ls, ',');
781  }
782  else if (c == '{')
783  {
784  if (!CheckPureDataBlock(ls))
785  return false;
786  testnext(ls, ',');
787  }
788  else
789  {
790  return false;
791  }
792  };
793  return true;
794  }
795  else
796  {
797  return false;
798  }
799  }
800 
801  static bool IsPureData(const char* input, int nLen)
802  {
803  NPLLex lex;
804  LexState* ls = lex.SetInput(input, nLen);
805  ls->nestlevel = 0;
806  try
807  {
808  next(ls); /* read first token */
809  if (CheckPureDataBlock(ls))
810  {
811  testnext(ls, ';');
812  if (ls->t.token == NPLLex::TK_EOS)
813  {
814  return true;
815  }
816  }
817  }
818  catch (const char* err)
819  {
820  OUTPUT_LOG(err);
821  OUTPUT_LOG("\r\n");
822  return false;
823  }
824  catch (...)
825  {
826  OUTPUT_LOG("error: unknown error in NPLParser::IsPureData()\r\n");
827  return false;
828  }
829  return false;
830  }
831 
832  static bool IsPureTable(const char* input, int nLen)
833  {
834  NPLLex lex;
835  LexState* ls = lex.SetInput(input, nLen);
836  ls->nestlevel = 0;
837  try
838  {
839  next(ls); /* read first token */
840  int c = ls->t.token;
841  if (c == '{')
842  {
843  if (CheckPureDataBlock(ls))
844  {
845  testnext(ls, ';');
846  if (ls->t.token == NPLLex::TK_EOS)
847  {
848  return true;
849  }
850  }
851  }
852  }
853  catch (const char* err)
854  {
855  OUTPUT_LOG(err);
856  OUTPUT_LOG("\r\n");
857  return false;
858  }
859  catch (...)
860  {
861  OUTPUT_LOG("error: unknown error in NPLParser::IsPureTable()\r\n");
862  return false;
863  }
864  return false;
865  }
866 
867  static bool IsMsgData(const char* input, int nLen)
868  {
869  NPLLex lex;
870  LexState* ls = lex.SetInput(input, nLen);
871  ls->nestlevel = 0;
872 
873  try
874  {
875  next(ls); /* read first token */
876 
877  if (ls->t.token == NPLLex::TK_NAME && ls->t.seminfo.ts == "msg")
878  {
879  next(ls);
880  if (ls->t.token == '=')
881  {
882  next(ls);
883  if (CheckPureDataBlock(ls))
884  {
885  testnext(ls, ';');
886  if (ls->t.token == NPLLex::TK_EOS)
887  {
888  return true;
889  }
890  }
891  }
892  }
893  }
894  catch (const char* err)
895  {
896  OUTPUT_LOG(err);
897  OUTPUT_LOG("\r\n");
898  return false;
899  }
900  catch (...)
901  {
902  OUTPUT_LOG("error: unknown error in NPLParser::IsMsgData()\r\n");
903  return false;
904  }
905  return false;
906  }
907 
908  static bool IsIdentifier(const char* str, int nLength)
909  {
910  bool bIsIdentifier = !isdigit(str[0]);
911  for (int i = 0; i < nLength && bIsIdentifier; ++i)
912  {
913  char c = str[i];
914  bIsIdentifier = (isalnum(c) || c == '_');
915  }
916  return bIsIdentifier;
917  }
918 
919  static void enterlevel(LexState *ls)
920  {
921  if (++(ls)->nestlevel > LUA_MAXPARSERLEVEL)
922  NPLLex::luaX_syntaxerror(ls, "too many syntax levels");
923  }
924 
925  static void leavelevel(LexState *ls)
926  {
927  ((ls)->nestlevel--);
928  }
929 
930 
931  };
932 
933 
934 
935 #pragma endregion NPLParser
936 
937 #pragma region NPLObjectProxy
938 
941  {
942  public:
943  enum NPLObjectType
944  {
945  NPLObjectType_Nil,
946  NPLObjectType_Table,
947  NPLObjectType_Number,
948  NPLObjectType_String,
949  NPLObjectType_Bool,
950  };
951  NPLObjectBase() :m_type(NPLObjectType_Nil){};
952  virtual ~NPLObjectBase(){};
953 
954  inline NPLObjectType GetType(){ return m_type; }
955 
956  protected:
957  NPLObjectType m_type;
958  };
959 
962  {
963  public:
964  NPLNumberObject() :m_value(0){ m_type = NPLObjectType_Number; };
966  NPLNumberObject(double value) :m_value(value){ m_type = NPLObjectType_Number; };
967 
968  virtual ~NPLNumberObject(){};
969 
970  void SetValue(double value){ m_value = value; }
971  double GetValue() { return m_value; }
972 
973  NPLNumberObject& operator = (double value) { SetValue(value); return *this; }
974 
975  private:
976  double m_value;
977  };
978 
979 
982  {
983  public:
984  NPLBoolObject() :m_value(false){ m_type = NPLObjectType_Bool; };
986  NPLBoolObject(bool value) :m_value(value){ m_type = NPLObjectType_Bool; };
987 
988  virtual ~NPLBoolObject(){};
989 
990  inline void SetValue(bool value){ m_value = value; }
991  bool GetValue() { return m_value; }
992 
993  NPLBoolObject& operator = (bool value) { SetValue(value); return *this; }
994 
995  private:
996  bool m_value;
997  };
998 
999 
1002  {
1003  public:
1004  NPLStringObject(){ m_type = NPLObjectType_String; };
1006  NPLStringObject(const std::string& value) :m_value(value){ m_type = NPLObjectType_String; };
1007 
1008  virtual ~NPLStringObject(){};
1009 
1010  void SetValue(const std::string& value){ m_value = value; }
1011  std::string& GetValue(){ return m_value; }
1012 
1013  NPLStringObject& operator = (const std::string& value) { SetValue(value); return *this; }
1014 
1015  private:
1016  std::string m_value;
1017  };
1018 
1034  template <class T>
1035  class NPLObjectProxyT : public NPLObjectBase_ptr
1036  {
1037  public:
1038  NPLObjectProxyT() {};
1039  NPLObjectProxyT(NPLObjectBase* pObject) : NPLObjectBase_ptr(pObject)
1040  {
1041 
1042  }
1043 
1044  operator double()
1045  {
1046  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Number)
1047  *this = NPLObjectProxyT(new NPLNumberObject());
1048  return ((NPLNumberObject*)get())->GetValue();
1049  }
1050 
1051  void operator = (double value)
1052  {
1053  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Number)
1054  *this = NPLObjectProxyT(new NPLNumberObject());
1055  ((NPLNumberObject*)get())->SetValue(value);
1056  }
1057 
1058  operator bool()
1059  {
1060  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Bool)
1061  *this = NPLObjectProxyT(new NPLBoolObject());
1062  return (((NPLBoolObject*)get())->GetValue());
1063  }
1064 
1065  void operator = (bool value)
1066  {
1067  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Bool)
1068  *this = NPLObjectProxyT(new NPLBoolObject());
1069  ((NPLBoolObject*)get())->SetValue(value);
1070 
1071  }
1072 
1073  operator const string& ()
1074  {
1075  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_String)
1076  *this = NPLObjectProxyT(new NPLStringObject());
1077  return (((NPLStringObject*)get())->GetValue());
1078  }
1079 
1080  bool operator == (const string& value)
1081  {
1082  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_String)
1083  return false;
1084  else
1085  return ((NPLStringObject*)get())->GetValue() == value;
1086  }
1087  bool operator == (const char* value)
1088  {
1089  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_String)
1090  return false;
1091  else
1092  return ((NPLStringObject*)get())->GetValue() == value;
1093  }
1094 
1095  void operator = (const std::string& value)
1096  {
1097  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_String)
1098  *this = NPLObjectProxyT(new NPLStringObject());
1099  ((NPLStringObject*)get())->SetValue(value);
1100  }
1101 
1102  void operator = (const char* value)
1103  {
1104  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_String)
1105  *this = NPLObjectProxyT(new NPLStringObject());
1106  ((NPLStringObject*)get())->SetValue(value);
1107  }
1108 
1109  NPLObjectProxyT& operator [] (const string& sName)
1110  {
1111  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1112  *this = NPLObjectProxyT(new T());
1113  return ((T*)get())->CreateGetField(sName);
1114  };
1115 
1116  NPLObjectProxyT& operator [] (const char* sName)
1117  {
1118  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1119  *this = NPLObjectProxyT(new T());
1120  return ((T*)get())->CreateGetField(sName);
1121 
1122  };
1123 
1124  NPLObjectProxyT& operator [](int nIndex)
1125  {
1126  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1127  *this = NPLObjectProxyT(new T());
1128  return ((T*)get())->CreateGetField(nIndex);
1129  };
1130 
1131  NPLObjectProxyT GetField(const string& sName)
1132  {
1133  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1134  {
1135  return NPLObjectProxyT();
1136  }
1137  else
1138  {
1139  return ((T*)get())->GetField(sName);
1140  }
1141  }
1142 
1143 
1144  NPLObjectProxyT GetField(const char* sName)
1145  {
1146  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1147  {
1148  return NPLObjectProxyT();
1149  }
1150  else
1151  {
1152  return ((T*)get())->GetField(sName);
1153  }
1154  }
1155 
1156  void MakeNil()
1157  {
1158  reset();
1159  }
1160 
1161  typename T::Iterator_Type begin()
1162  {
1163  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1164  *this = NPLObjectProxyT(new T());
1165  return ((T*)get())->begin();
1166  };
1167 
1168  typename T::Iterator_Type end()
1169  {
1170  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1171  *this = NPLObjectProxyT(new T());
1172  return ((T*)get())->end();
1173  };
1174 
1175  typename T::IndexIterator_Type index_begin()
1176  {
1177  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1178  *this = NPLObjectProxyT(new T());
1179  return ((T*)get())->index_begin();
1180  };
1181 
1182  typename T::IndexIterator_Type index_end()
1183  {
1184  if (get() == 0 || GetType() != NPLObjectBase::NPLObjectType_Table)
1185  *this = NPLObjectProxyT(new T());
1186  return ((T*)get())->index_end();
1187  };
1188 
1190  NPLObjectBase::NPLObjectType GetType()
1191  {
1192  return (get() != 0) ? get()->GetType() : NPLObjectBase::NPLObjectType_Nil;
1193  }
1194  };
1195 
1196 
1198 
1219  class NPLTable : public NPLObjectBase
1220  {
1221  public:
1222  typedef std::map<std::string, NPLObjectProxy> TableFieldMap_Type;
1223  typedef std::map<int, NPLObjectProxy> TableIntFieldMap_Type;
1224  typedef TableFieldMap_Type::iterator Iterator_Type;
1225  typedef TableIntFieldMap_Type::iterator IndexIterator_Type;
1226 
1227 
1229  NPLTable(){ m_type = NPLObjectType_Table; };
1230 
1231  virtual ~NPLTable(){
1232  Clear();
1233  }
1234 
1235  public:
1236 
1237  void ToString(std::string& str)
1238  {
1239  }
1240 
1241  void Clear()
1242  {
1243  m_fields.clear();
1244  }
1245 
1246  void SetField(const string& sName, const NPLObjectProxy& pObject)
1247  {
1248  TableFieldMap_Type::iterator iter = m_fields.find(sName);
1249  if (iter == m_fields.end())
1250  {
1251  if (pObject.get() != 0)
1252  {
1253  m_fields[sName] = pObject;
1254  }
1255  }
1256  else
1257  {
1258  if (pObject.get() != 0)
1259  {
1260  iter->second = pObject;
1261  }
1262  else
1263  {
1264  m_fields.erase(iter);
1265  }
1266  }
1267  }
1268 
1269  void SetField(int nIndex, const NPLObjectProxy& pObject)
1270  {
1271  TableIntFieldMap_Type::iterator iter = m_index_fields.find(nIndex);
1272  if (iter == m_index_fields.end())
1273  {
1274  if (pObject.get() != 0)
1275  {
1276  m_index_fields[nIndex] = pObject;
1277  }
1278  }
1279  else
1280  {
1281  if (pObject.get() != 0)
1282  {
1283  iter->second = pObject;
1284  }
1285  else
1286  {
1287  m_index_fields.erase(iter);
1288  }
1289  }
1290  }
1291 
1292  NPLObjectProxy GetField(int nIndex)
1293  {
1294  TableIntFieldMap_Type::iterator iter = m_index_fields.find(nIndex);
1295  return (iter != m_index_fields.end()) ? iter->second : NPLObjectProxy();
1296  }
1297 
1298  NPLObjectProxy GetField(const string& sName)
1299  {
1300  TableFieldMap_Type::iterator iter = m_fields.find(sName);
1301  return (iter != m_fields.end()) ? iter->second : NPLObjectProxy();
1302  }
1303 
1304  NPLObjectProxy& CreateGetField(int nIndex)
1305  {
1306  return m_index_fields[nIndex];
1307  }
1308 
1309  NPLObjectProxy& CreateGetField(const string& sName)
1310  {
1311  return m_fields[sName];
1312  }
1313 
1314  Iterator_Type begin() { return m_fields.begin(); };
1315  Iterator_Type end() { return m_fields.end(); };
1316 
1317  IndexIterator_Type index_begin() { return m_index_fields.begin(); };
1318  IndexIterator_Type index_end() { return m_index_fields.end(); };
1319 
1321  NPLObjectProxy& operator [](const string& sName) { return CreateGetField(sName); };
1322  NPLObjectProxy& operator [](const char* sName) { return CreateGetField(sName); };
1323  NPLObjectProxy& operator [](int nIndex) { return CreateGetField(nIndex); };
1324  private:
1325  TableFieldMap_Type m_fields;
1326  TableIntFieldMap_Type m_index_fields;
1327  };
1328 
1329 
1330 #pragma endregion NPLObjectProxy
1331 
1332 #pragma region NPLHelper
1333 
1336  {
1337  public:
1338  static bool DeserializePureNPLDataBlock(LexState *ls, NPLObjectProxy& objProxy)
1339  {
1340  // data
1341  int c = ls->t.token;
1342  switch (c)
1343  {
1344  case NPLLex::TK_TRUE:
1345  objProxy = true;
1346  NPLParser::next(ls);
1347  return true;
1348  case NPLLex::TK_FALSE:
1349  objProxy = false;
1350  NPLParser::next(ls);
1351  return true;
1352  case NPLLex::TK_NIL:
1353  objProxy.MakeNil();
1354  NPLParser::next(ls);
1355  return true;
1356  case NPLLex::TK_NUMBER:
1357  objProxy = ls->t.seminfo.r;
1358  NPLParser::next(ls);
1359  return true;
1360  case NPLLex::TK_STRING:
1361  objProxy = ls->t.seminfo.ts;
1362  NPLParser::next(ls);
1363  return true;
1364  case '-':
1365  {
1366  // negative number
1367  NPLParser::next(ls);
1368  if (ls->t.token == NPLLex::TK_NUMBER)
1369  {
1370  objProxy = -ls->t.seminfo.r;
1371  NPLParser::next(ls);
1372  return true;
1373  }
1374  else
1375  return false;
1376  }
1377  case '{':
1378  {
1379  // table object
1380  NPLObjectProxy tabMsg;
1381 
1382  NPLParser::enterlevel(ls);
1383  bool bBreak = false;
1384  NPLParser::next(ls);
1385  // auto table index that begins with 1.
1386  int nTableAutoIndex = 1;
1387  while (!bBreak)
1388  {
1389  c = ls->t.token;
1390  if (c == '}')
1391  {
1392  // end of table
1393  NPLParser::leavelevel(ls);
1394  NPLParser::next(ls);
1395  bBreak = true;
1396  objProxy = tabMsg;
1397  }
1398  else if (c == NPLLex::TK_NAME)
1399  {
1400  NPLObjectProxy& proxy_ = tabMsg[ls->t.seminfo.ts];
1401  // by name assignment, such as name = data|table
1402  NPLParser::next(ls);
1403  if (ls->t.token == '=')
1404  {
1405  NPLParser::next(ls);
1406  if (!DeserializePureNPLDataBlock(ls, proxy_))
1407  return false;
1408  NPLParser::testnext(ls, ',');
1409  }
1410  else
1411  return false;
1412  }
1413  else if (c == '[')
1414  {
1415  // by integer or string key assignment, such as [number|string] = data|table
1416  NPLParser::next(ls);
1417  if (ls->t.token == NPLLex::TK_NUMBER)
1418  {
1419  // verify that it is an integer, instead of a floating value.
1420  NPLObjectProxy& proxy_ = tabMsg[(int)(ls->t.seminfo.r)];
1421 
1422  NPLParser::next(ls);
1423  if (ls->t.token == ']')
1424  {
1425  NPLParser::next(ls);
1426  if (ls->t.token == '=')
1427  {
1428  NPLParser::next(ls);
1429  if (!DeserializePureNPLDataBlock(ls, proxy_))
1430  return false;
1431  NPLParser::testnext(ls, ',');
1432  }
1433  else
1434  return false;
1435  }
1436  }
1437  else if (ls->t.token == NPLLex::TK_STRING)
1438  {
1439  // verify that the string is a value key(non-empty);
1440  if (ls->t.seminfo.ts.empty())
1441  return false;
1442 
1443  NPLObjectProxy& proxy_ = tabMsg[ls->t.seminfo.ts];
1444  NPLParser::next(ls);
1445  if (ls->t.token == ']')
1446  {
1447  NPLParser::next(ls);
1448  if (ls->t.token == '=')
1449  {
1450  NPLParser::next(ls);
1451  if (!DeserializePureNPLDataBlock(ls, proxy_))
1452  return false;
1453  NPLParser::testnext(ls, ',');
1454  }
1455  else
1456  return false;
1457  }
1458  }
1459  else
1460  return false;
1461  }
1464  else
1465  {
1466  NPLObjectProxy& proxy_ = tabMsg[nTableAutoIndex++];
1467  if (!DeserializePureNPLDataBlock(ls, proxy_))
1468  return false;
1469  NPLParser::testnext(ls, ',');
1470  }
1471  }
1472  return true;
1473  }
1474  default:
1475  break;
1476  }
1477  return false;
1478  }
1479 
1485  static bool IsSCodePureData(const char * sCode, int nCodeSize = -1)
1486  {
1487  if (nCodeSize < 0)
1488  nCodeSize = strlen(sCode);
1489  return NPLParser::IsMsgData(sCode, nCodeSize);
1490  }
1491 
1496  static bool IsPureData(const char * sCode, int nCodeSize = -1){
1497  if (nCodeSize < 0)
1498  nCodeSize = strlen(sCode);
1499  return NPLParser::IsPureData(sCode, nCodeSize);
1500  }
1501 
1506  static bool IsPureTable(const char * sCode, int nCodeSize = -1){
1507  if (nCodeSize < 0)
1508  nCodeSize = strlen(sCode);
1509  return NPLParser::IsPureTable(sCode, nCodeSize);
1510  }
1511 
1515  static NPLObjectProxy StringToNPLTable(const char* input, int nLen = -1){
1516  NPLLex lex;
1517  LexState* ls = lex.SetInput(input, nLen);
1518  ls->nestlevel = 0;
1519 
1520  try
1521  {
1522  NPLParser::next(ls); /* read first token */
1523 
1524  if (ls->t.token == '{')
1525  {
1526  NPLObjectProxy output;
1527  if (DeserializePureNPLDataBlock(ls, output))
1528  {
1529  NPLParser::testnext(ls, ';');
1530  if (ls->t.token == NPLLex::TK_EOS)
1531  {
1532  return output;
1533  }
1534  }
1535  }
1536  }
1537  catch (const char* err)
1538  {
1539  OUTPUT_LOG("error: %s in NPLHelper::StringToNPLTable()\n", err);
1540  return NPLObjectProxy();
1541  }
1542  catch (...)
1543  {
1544  OUTPUT_LOG("error: unknown error in NPLHelper::StringToNPLTable()\n");
1545  return NPLObjectProxy();
1546  }
1547  return NPLObjectProxy();
1548  }
1549 
1553  static NPLObjectProxy MsgStringToNPLTable(const char* input, int nLen = -1){
1554  NPLLex lex;
1555  LexState* ls = lex.SetInput(input, nLen);
1556  ls->nestlevel = 0;
1557 
1558  try
1559  {
1560  NPLParser::next(ls); /* read first token */
1561 
1562  if (ls->t.token == NPLLex::TK_NAME && ls->t.seminfo.ts == "msg")
1563  {
1564  NPLParser::next(ls);
1565  if (ls->t.token == '=')
1566  {
1567  NPLParser::next(ls);
1568  NPLObjectProxy output;
1569  if (DeserializePureNPLDataBlock(ls, output))
1570  {
1571  NPLParser::testnext(ls, ';');
1572  if (ls->t.token == NPLLex::TK_EOS)
1573  {
1574  return output;
1575  }
1576  }
1577  }
1578  }
1579  }
1580  catch (const char* err)
1581  {
1582  OUTPUT_LOG("error: %s in NPLHelper::StringToNPLTable()\n", err);
1583  return NPLObjectProxy();
1584  }
1585  catch (...)
1586  {
1587  OUTPUT_LOG("error: unknown error in NPLHelper::StringToNPLTable()\n");
1588  return NPLObjectProxy();
1589  }
1590  return NPLObjectProxy();
1591  }
1592 
1593  template <typename StringType>
1594  static bool SerializeNPLTableToString(const char* sStorageVar, NPLObjectProxy& input, StringType& sCode, int nCodeOffset)
1595  {
1596  sCode.resize(nCodeOffset);
1597 
1598  int nStorageVarLen = 0;
1599  if (sStorageVar != NULL)
1600  {
1601  nStorageVarLen = strlen(sStorageVar);
1602  if (nStorageVarLen > 0)
1603  {
1604  sCode.append(sStorageVar, nStorageVarLen);
1605  sCode.append("=");
1606  }
1607  }
1608 
1609  NPLObjectBase::NPLObjectType nType = input.GetType();
1610  switch (nType)
1611  {
1612  case NPLObjectBase::NPLObjectType_Number:
1613  {
1614  double value = input;
1615  char buff[40];
1616  int nLen = snprintf(buff, 40, LUA_NUMBER_FMT, value);
1617  sCode.append(buff, nLen);
1618  break;
1619  }
1620  case NPLObjectBase::NPLObjectType_Bool:
1621  {
1622  bool bValue = input;
1623  sCode.append(bValue ? "true" : "false");
1624  break;
1625  }
1626  case NPLObjectBase::NPLObjectType_String:
1627  {
1628  // this is something like string.format("%q") in NPL.
1629  const string& str = input;
1630  EncodeStringInQuotation(sCode, (int)(sCode.size()), str.c_str(), (int)(str.size()));
1631  break;
1632  }
1633  case NPLObjectBase::NPLObjectType_Table:
1634  {
1635  sCode.append("{");
1636  int nNextIndex = 1;
1637  // serialize item with number key
1638  for (NPLTable::IndexIterator_Type itCur = input.index_begin(), itEnd = input.index_end(); itCur != itEnd; ++itCur)
1639  {
1640  int nIndex = itCur->first;
1641  NPLObjectProxy& value = itCur->second;
1642  int nOldSize = (int)(sCode.size());
1643  if (nIndex != nNextIndex)
1644  {
1645  sCode.append("[");
1646  char buff[40];
1647  int nLen = snprintf(buff, 40, "%d", nIndex);
1648  sCode.append(buff, nLen);
1649  sCode.append("]=");
1650  }
1651  if (SerializeNPLTableToString(NULL, value, sCode, (int)(sCode.size())))
1652  sCode.append(",");
1653  else
1654  sCode.resize(nOldSize);
1655  nNextIndex = nIndex + 1;
1656  }
1657  // serialize item with a string key
1658  for (NPLTable::Iterator_Type itCur = input.begin(), itEnd = input.end(); itCur != itEnd; ++itCur)
1659  {
1660  const string& key = itCur->first;
1661  const char* sKey = key.c_str();
1662  NPLObjectProxy& value = itCur->second;
1663  int nOldSize = (int)(sCode.size());
1664  // if sKey contains only alphabetic letters, we will use sKey=data,otherwise, we go the safer one ["sKey"]=data.
1665  // the first is more efficient in disk space.
1666  int nSKeyCount = (int)(key.size());
1667  bool bIsIdentifier = NPLParser::IsIdentifier(sKey, nSKeyCount);
1668  if (bIsIdentifier)
1669  {
1670  sCode.append(sKey, nSKeyCount);
1671  sCode.append("=");
1672  }
1673  else
1674  {
1675  sCode.append("[");
1676  EncodeStringInQuotation(sCode, (int)(sCode.size()), sKey, nSKeyCount);
1677  sCode.append("]=");
1678  }
1679  if (SerializeNPLTableToString(NULL, value, sCode, (int)(sCode.size())))
1680  {
1681  sCode.append(",");
1682  }
1683  else
1684  {
1685  sCode.resize(nOldSize);
1686  }
1687  }
1688  sCode.append("}");
1689  break;
1690  }
1691  default:
1692  // we will escape any functions or nil, etc.
1693  if (nStorageVarLen > 0)
1694  {
1695  sCode.resize(nCodeOffset);
1696  }
1697  return false;
1698  break;
1699  }
1700  return true;
1701  }
1702 
1703  // Thread-safe version
1704  template <typename StringType>
1705  static void EncodeStringInQuotation(StringType& buff, int nOutputOffset, const char* str, int nSize)
1706  {
1707  // this is something like string.format("%q") in NPL.
1708  // estimate the size.
1709  if (nSize < 0)
1710  nSize = strlen(str);
1711  int nFinalSize = nOutputOffset + nSize + 2;
1712  buff.resize(nFinalSize);
1713 
1714  // replace quotation mark in string.
1715  int nPos = nOutputOffset;
1716  buff[nPos++] = '"';
1717  for (int i = 0; i < nSize; ++i)
1718  {
1719  char c = str[i];
1720  switch (c)
1721  {
1722  case '"': case '\\': {
1723  nFinalSize += 1;
1724  buff.resize(nFinalSize);
1725 
1726  buff[nPos++] = '\\';
1727  buff[nPos++] = c;
1728 
1729  break;
1730  }
1731  case '\n': {
1732  nFinalSize += 1;
1733  buff.resize(nFinalSize);
1734 
1735  buff[nPos++] = '\\';
1736  buff[nPos++] = 'n';
1737  break;
1738  }
1739  case '\r': {
1740  nFinalSize += 1;
1741  buff.resize(nFinalSize);
1742 
1743  buff[nPos++] = '\\';
1744  buff[nPos++] = 'r';
1745  break;
1746  }
1747  case '\0': {
1748  nFinalSize += 3;
1749  buff.resize(nFinalSize);
1750 
1751  buff[nPos++] = '\\';
1752  buff[nPos++] = '0';
1753  buff[nPos++] = '0';
1754  buff[nPos++] = '0';
1755  break;
1756  }
1757  default: {
1758  buff[nPos++] = c;
1759  break;
1760  }
1761  }
1762  }
1763  buff[nPos++] = '"';
1764  assert(nPos == nFinalSize && (int)(buff.size()) == nFinalSize);
1765  }
1766 
1768  static bool NPLTableToString(const char* sStorageVar, NPLObjectProxy& input, std::string& sCode, int nCodeOffset = 0){
1769  return SerializeNPLTableToString(sStorageVar, input, sCode, nCodeOffset);
1770  }
1771 
1772  template <typename StringType>
1773  static void EncodeStringInQuotation(StringType& output, int nOutputOffset, const std::string& input)
1774  {
1775  EncodeStringInQuotation(output, nOutputOffset, input.c_str(), (int)input.size());
1776  }
1777  template <typename StringType>
1778  static void EncodeStringInQuotation(StringType& output, int nOutputOffset, const char* input)
1779  {
1780  EncodeStringInQuotation(output, nOutputOffset, input, strlen(input));
1781  }
1782  };
1783 
1784  // Note: Instantiate function with the explicitly specified template.
1785  // This allows us to put template implementation code in cpp file.
1786  template void NPLHelper::EncodeStringInQuotation(std::string& output, int nOutputOffset, const char* input, int nInputSize);
1787  template bool NPLHelper::SerializeNPLTableToString(const char* sStorageVar, NPLObjectProxy& input, std::string& sCode, int nCodeOffset);
1788 
1789 #pragma endregion NPLHelper
1790 
1791 #pragma region NPLWriter
1792  template <typename StringBufferType = std::string>
1794 
1797 
1821  template <typename StringBufferType>
1822  class CNPLWriterT
1823  {
1824  public:
1825  typedef StringBufferType Buffer_Type;
1826 
1828  CNPLWriterT(int nReservedSize = -1);
1830  CNPLWriterT(Buffer_Type& buff_, int nReservedSize = -1);
1831  ~CNPLWriterT();
1832 
1834  void Reset(int nReservedSize = -1);
1835 
1837  void BeginTable();
1840  void WriteName(const char* name, bool bUseBrackets = false);
1841 
1843  void WriteValue(const char* value, bool bInQuotation = true);
1845  void WriteValue(const char* buffer, int nSize, bool bInQuotation = true);
1846  void WriteValue(const string& sStr, bool bInQuotation = true);
1848  void WriteValue(double value);
1850  void WriteNil();
1851 
1853  void EndTable();
1854 
1856  void Append(const char* text);
1857  void Append(const char* pData, int nSize);
1858  void Append(const string& sText);
1863  char* AddMemBlock(int nSize);
1864 
1866  void WriteParamDelimiter(){ Append(";"); };
1867 
1869  const Buffer_Type& ToString() { return m_sCode; };
1870  public:
1871  // static strings.
1872 
1873 #ifndef DISABLE_STATIC_VARIABLE
1874 
1875  static const Buffer_Type& GetNilMessage();
1876 
1878  static const Buffer_Type& GetEmptyMessage();
1879 #endif
1880 
1881  private:
1882  Buffer_Type& m_sCode;
1883  Buffer_Type m_buf;
1884  bool m_bBeginAssignment;
1885  int m_nTableLevel;
1886  };
1887 
1888  template <typename StringBufferType>
1890  : m_sCode(m_buf), m_bBeginAssignment(false), m_nTableLevel(0)
1891  {
1892  if (nReservedSize > 0)
1893  m_sCode.reserve(nReservedSize);
1894  }
1895 
1896  template <typename StringBufferType>
1897  CNPLWriterT<StringBufferType>::CNPLWriterT(StringBufferType& buff_, int nReservedSize)
1898  : m_sCode(buff_), m_bBeginAssignment(false), m_nTableLevel(0)
1899  {
1900  if (nReservedSize > 0)
1901  m_sCode.reserve(nReservedSize);
1902  }
1903 
1904  template <typename StringBufferType>
1906  {
1907  }
1908 
1909 
1910  template <typename StringBufferType>
1911  void CNPLWriterT<StringBufferType>::Reset(int nReservedSize /*= -1*/)
1912  {
1913  m_sCode.clear();
1914  if (nReservedSize > 0)
1915  m_sCode.reserve(nReservedSize);
1916  }
1917 
1918  template <typename StringBufferType>
1919  void CNPLWriterT<StringBufferType>::WriteName(const char* name, bool bUseBrackets /*= false*/)
1920  {
1921  if (name)
1922  {
1923  m_bBeginAssignment = true;
1924  if (!bUseBrackets)
1925  {
1926  m_sCode += name;
1927  }
1928  else
1929  {
1930  m_sCode += "[\"";
1931  m_sCode += name;
1932  m_sCode += "\"]";
1933  }
1934  }
1935  }
1936 
1937  template <typename StringBufferType>
1938  void CNPLWriterT<StringBufferType>::WriteValue(const char* value, bool bInQuotation/*=true*/)
1939  {
1940  if (value == NULL)
1941  {
1942  return WriteNil();
1943  }
1944  if (m_bBeginAssignment)
1945  {
1946  m_sCode += "=";
1947  }
1948  if (bInQuotation)
1949  {
1950  NPLHelper::EncodeStringInQuotation(m_sCode, (int)m_sCode.size(), value);
1951  }
1952  else
1953  {
1954  m_sCode += value;
1955  }
1956  if (m_nTableLevel > 0)
1957  m_sCode += ",";
1958  m_bBeginAssignment = false;
1959  }
1960 
1961  template <typename StringBufferType>
1962  void CNPLWriterT<StringBufferType>::WriteValue(const char* buffer, int nSize, bool bInQuotation/*=true*/)
1963  {
1964  if (buffer == NULL)
1965  {
1966  return WriteNil();
1967  }
1968  if (m_bBeginAssignment)
1969  {
1970  m_sCode += "=";
1971  }
1972  if (bInQuotation)
1973  {
1974  NPLHelper::EncodeStringInQuotation(m_sCode, (int)m_sCode.size(), buffer, nSize);
1975  }
1976  else
1977  {
1978  size_t nOldSize = m_sCode.size();
1979  m_sCode.resize(nOldSize + nSize);
1980  memcpy((void*)(m_sCode.c_str() + nOldSize), buffer, nSize);
1981  }
1982  if (m_nTableLevel > 0)
1983  m_sCode += ",";
1984  m_bBeginAssignment = false;
1985  }
1986 
1987  template <typename StringBufferType>
1989  {
1990  char buff[40];
1991  snprintf(buff, 40, LUA_NUMBER_FMT, value);
1992  WriteValue(buff, false);
1993  }
1994 
1995  template <typename StringBufferType>
1996  void CNPLWriterT<StringBufferType>::WriteValue(const string& sStr, bool bInQuotation/*=true*/)
1997  {
1998  WriteValue(sStr.c_str(), (int)sStr.size(), bInQuotation);
1999  }
2000  template <typename StringBufferType>
2002  {
2003  WriteValue("nil", false);
2004  }
2005 
2006  template <typename StringBufferType>
2008  {
2009  m_sCode += m_bBeginAssignment ? "={" : "{";
2010  m_nTableLevel++;
2011  m_bBeginAssignment = false;
2012  }
2013 
2014  template <typename StringBufferType>
2016  {
2017  m_sCode += "}";
2018  if ((--m_nTableLevel) > 0)
2019  m_sCode += ",";
2020  }
2021 
2022  template <typename StringBufferType>
2024  {
2025  if (text)
2026  m_sCode += text;
2027  }
2028 
2029  template <typename StringBufferType>
2030  void CNPLWriterT<StringBufferType>::Append(const char* pData, int nSize)
2031  {
2032  m_sCode.append(pData, nSize);
2033  }
2034 
2035  template <typename StringBufferType>
2036  void CNPLWriterT<StringBufferType>::Append(const string& text)
2037  {
2038  m_sCode += text;
2039  }
2040 
2041  template <typename StringBufferType>
2043  {
2044  if (nSize > 0)
2045  {
2046  m_sCode.resize(m_sCode.size() + nSize);
2047  return &(m_sCode[m_sCode.size() - nSize]);
2048  }
2049  else
2050  return NULL;
2051  }
2052 
2053 #ifndef DISABLE_STATIC_VARIABLE
2054  template <typename StringBufferType>
2056  {
2057  static const StringBufferType g_str = "msg=nil;";
2058  return g_str;
2059  }
2060 
2061  template <typename StringBufferType>
2063  {
2064  static const StringBufferType g_str = "msg={};";
2065  return g_str;
2066  }
2067 #endif
2068 
2069  // instantiate class so that no link errors when separating template implementation to hpp file.
2070  template class CNPLWriterT < std::string > ;
2071 #pragma endregion NPLWriter
2072 }
NPLNumberObject(double value)
create the table from a serialized string.
Definition: NPLInterface.hpp:966
NPL object proxy.
Definition: NPLInterface.hpp:1035
Definition: NPLInterface.hpp:110
static bool DeserializePureNPLDataBlock(LexState *ls, NPLObjectProxy &objProxy)
Definition: NPLInterface.hpp:1338
for lexer for NPL files
Definition: NPLInterface.hpp:152
NPL helper class.
Definition: NPLInterface.hpp:1335
Definition: NPLInterface.hpp:116
NPLObjectBase::NPLObjectType GetType()
get the type
Definition: NPLInterface.hpp:1190
static const Buffer_Type & GetNilMessage()
return "msg=nil;"
Definition: NPLInterface.hpp:2055
base class for all NPL date members.
Definition: NPLInterface.hpp:940
bool bSucceed
true if no error
Definition: NPLInterface.hpp:136
void WriteName(const char *name, bool bUseBrackets=false)
write a parameter name
Definition: NPLInterface.hpp:1919
Definition: NPLInterface.hpp:43
static const Buffer_Type & GetEmptyMessage()
return "msg={};"
Definition: NPLInterface.hpp:2062
parser for NPL files.
Definition: NPLInterface.hpp:619
lex state
Definition: NPLInterface.hpp:125
a floating point number
Definition: NPLInterface.hpp:961
NPLTable()
this is an empty table
Definition: NPLInterface.hpp:1229
static bool CheckPureDataBlock(LexState *ls)
Definition: NPLInterface.hpp:694
void WriteNil()
write a parameter nil
Definition: NPLInterface.hpp:2001
CNPLWriterT(int nReservedSize=-1)
the internal buffer reserved size.
Definition: NPLInterface.hpp:1889
static bool IsPureTable(const char *sCode, int nCodeSize=-1)
it will return true if input string is a {table} containing only "false", "true", NUMBER...
Definition: NPLInterface.hpp:1506
void WriteValue(const char *value, bool bInQuotation=true)
if bInQuotation is true, it writes a parameter text value.
Definition: NPLInterface.hpp:1938
a boolean
Definition: NPLInterface.hpp:981
const Buffer_Type & ToString()
get the current NPL code.
Definition: NPLInterface.hpp:1869
a simple class for creating NPL script code, especially data table code.
Definition: NPLInterface.hpp:1793
static NPLObjectProxy StringToNPLTable(const char *input, int nLen=-1)
converting string to NPL table object
Definition: NPLInterface.hpp:1515
static NPLObjectProxy MsgStringToNPLTable(const char *input, int nLen=-1)
same as StringToNPLTable(), except that it begins with "msg={...}"
Definition: NPLInterface.hpp:1553
NPLStringObject(const std::string &value)
create the table from a serialized string.
Definition: NPLInterface.hpp:1006
Definition: NPLInterface.hpp:103
static bool IsSCodePureData(const char *sCode, int nCodeSize=-1)
verify the script code.
Definition: NPLInterface.hpp:1485
NPLBoolObject(bool value)
create the table from a serialized string.
Definition: NPLInterface.hpp:986
void BeginTable()
write begin table "{"
Definition: NPLInterface.hpp:2007
void WriteParamDelimiter()
write ";"
Definition: NPLInterface.hpp:1866
void EndTable()
write end table "}"
Definition: NPLInterface.hpp:2015
void Append(const char *text)
append any text
Definition: NPLInterface.hpp:2023
char * AddMemBlock(int nSize)
add a mem block of size nSize and return the address of the block.
Definition: NPLInterface.hpp:2042
a floating point number
Definition: NPLInterface.hpp:1001
single-threaded reference counted base class for boost::intrusive_ptr all boost::intrusive_ptr<T>, should derive from this class.
Definition: NPLInterface.hpp:56
this is a pure c++ implementation of lua table.
Definition: NPLInterface.hpp:1219
static bool NPLTableToString(const char *sStorageVar, NPLObjectProxy &input, std::string &sCode, int nCodeOffset=0)
same as SerializeNPLTableToString.
Definition: NPLInterface.hpp:1768
void Reset(int nReservedSize=-1)
reset the writer
Definition: NPLInterface.hpp:1911
static bool IsPureData(const char *sCode, int nCodeSize=-1)
it will return true if input string is "false", "true", NUMBER, STRING, and {table} ...
Definition: NPLInterface.hpp:1496