OpenClonk
C4AulParse.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // parses scripts
17 
18 #include "C4Include.h"
19 #include "script/C4AulParse.h"
20 
21 #include "object/C4Def.h"
22 #include "script/C4AulDebug.h"
23 #include "script/C4AulExec.h"
24 
25 #ifndef DEBUG_BYTECODE_DUMP
26 #define DEBUG_BYTECODE_DUMP 0
27 #endif
28 #include <iomanip>
29 
30 #define C4AUL_Include "#include"
31 #define C4AUL_Append "#appendto"
32 #define C4AUL_Warning "#warning"
33 
34 #define C4Aul_Warning_enable "enable"
35 #define C4Aul_Warning_disable "disable"
36 
37 #define C4AUL_Func "func"
38 
39 #define C4AUL_Private "private"
40 #define C4AUL_Protected "protected"
41 #define C4AUL_Public "public"
42 #define C4AUL_Global "global"
43 #define C4AUL_Const "const"
44 
45 #define C4AUL_If "if"
46 #define C4AUL_Else "else"
47 #define C4AUL_Do "do"
48 #define C4AUL_While "while"
49 #define C4AUL_For "for"
50 #define C4AUL_In "in"
51 #define C4AUL_Return "return"
52 #define C4AUL_Var "Var"
53 #define C4AUL_Par "Par"
54 #define C4AUL_Break "break"
55 #define C4AUL_Continue "continue"
56 #define C4AUL_this "this"
57 
58 #define C4AUL_GlobalNamed "static"
59 #define C4AUL_LocalNamed "local"
60 #define C4AUL_VarNamed "var"
61 
62 #define C4AUL_TypeInt "int"
63 #define C4AUL_TypeBool "bool"
64 #define C4AUL_TypeC4ID "id"
65 #define C4AUL_TypeDef "def"
66 #define C4AUL_TypeEffect "effect"
67 #define C4AUL_TypeC4Object "object"
68 #define C4AUL_TypePropList "proplist"
69 #define C4AUL_TypeString "string"
70 #define C4AUL_TypeArray "array"
71 #define C4AUL_TypeFunction "func"
72 
73 #define C4AUL_True "true"
74 #define C4AUL_False "false"
75 #define C4AUL_Nil "nil"
76 #define C4AUL_New "new"
77 
78 // script token type
79 enum C4AulTokenType : int
80 {
81  ATT_INVALID,// invalid token
82  ATT_DIR, // directive
83  ATT_IDTF, // identifier
84  ATT_INT, // integer constant
85  ATT_STRING, // string constant
86  ATT_DOT, // "."
87  ATT_COMMA, // ","
88  ATT_COLON, // ":"
89  ATT_SCOLON, // ";"
90  ATT_BOPEN, // "("
91  ATT_BCLOSE, // ")"
92  ATT_BOPEN2, // "["
93  ATT_BCLOSE2,// "]"
94  ATT_BLOPEN, // "{"
95  ATT_BLCLOSE,// "}"
96  ATT_CALL, // "->"
97  ATT_CALLFS, // "->~"
98  ATT_LDOTS, // '...'
99  ATT_SET, // '='
100  ATT_OPERATOR,// operator
101  ATT_EOF // end of file
102 };
103 
105  Fn(nullptr), Host(a), pOrgScript(a), Engine(a->Engine),
106  SPos(a->Script.getData()), TokenSPos(SPos),
107  TokenType(ATT_INVALID),
108  ContextToExecIn(nullptr)
109 { }
110 
112  Fn(Fn), Host(nullptr), pOrgScript(nullptr), Engine(Engine),
113  SPos(Fn->Script), TokenSPos(SPos),
114  TokenType(ATT_INVALID),
115  ContextToExecIn(context)
116 { }
117 
119 {
120  ClearToken();
121 }
122 
123 void C4ScriptHost::Warn(const char *pMsg, ...)
124 {
125  va_list args; va_start(args, pMsg);
126  StdStrBuf Buf = FormatStringV(pMsg, args);
127  Buf.AppendFormat(" (%s)", ScriptName.getData());
129  va_end(args);
130 }
131 
132 void C4AulParse::Warn(C4AulWarningId warning, ...)
133 {
134  if (!IsWarningEnabled(TokenSPos, warning))
135  return;
136  va_list args; va_start(args, warning);
137  StdStrBuf Buf = FormatStringV(C4AulWarningMessages[static_cast<size_t>(warning)], args);
138  AppendPosition(Buf);
139  Buf.AppendFormat(" [%s]", C4AulWarningIDs[static_cast<size_t>(warning)]);
141  va_end(args);
142 }
143 
144 bool C4AulParse::IsWarningEnabled(const char *pos, C4AulWarningId warning) const
145 {
146  if (pOrgScript) return pOrgScript->IsWarningEnabled(pos, warning);
147  // In DirectExec, the default warnings are always active.
148  switch (warning)
149  {
150 #define DIAG(id, text, enabled) case C4AulWarningId::id: return enabled;
151 #include "C4AulWarnings.h"
152 #undef DIAG
153  default: return false;
154  }
155 }
156 
157 void C4AulParse::Error(const char *pMsg, ...)
158 {
159  va_list args; va_start(args, pMsg);
160  StdStrBuf Buf;
161  Buf.FormatV(pMsg, args);
162 
163  throw C4AulParseError(this, Buf.getData());
164 }
165 
166 void C4AulParse::AppendPosition(StdStrBuf & Buf)
167 {
168  if (Fn && Fn->GetName())
169  {
170  // Show function name
171  Buf.AppendFormat(" (in %s", Fn->GetName());
172 
173  // Exact position
174  if (Fn->pOrgScript && TokenSPos)
175  Buf.AppendFormat(", %s:%d:%d)",
177  SGetLine(Fn->pOrgScript->GetScript(), TokenSPos),
178  SLineGetCharacters(Fn->pOrgScript->GetScript(), TokenSPos));
179  else
180  Buf.AppendChar(')');
181  }
182  else if (pOrgScript)
183  {
184  // Script name
185  Buf.AppendFormat(" (%s:%d:%d)",
186  pOrgScript->ScriptName.getData(),
187  SGetLine(pOrgScript->GetScript(), TokenSPos),
188  SLineGetCharacters(pOrgScript->GetScript(), TokenSPos));
189  }
190  // show a warning if the error is in a remote script
191  if (pOrgScript != Host && Host)
192  Buf.AppendFormat(" (as #appendto/#include to %s)", Host->ScriptName.getData());
193 }
194 
195 C4AulParseError::C4AulParseError(C4AulParse * state, const char *pMsg)
196 {
197  // compose error string
198  sMessage.Copy(pMsg);
199  state->AppendPosition(sMessage);
200 }
201 
202 C4AulParseError::C4AulParseError(C4ScriptHost *pScript, const char *pMsg)
203 {
204  // compose error string
205  sMessage.Copy(pMsg);
206  if (pScript)
207  {
208  // Script name
209  sMessage.AppendFormat(" (%s)",
210  pScript->ScriptName.getData());
211  }
212 }
213 
214 C4AulParseError::C4AulParseError(C4AulScriptFunc * Fn, const char *SPos, const char *pMsg)
215 {
216  // compose error string
217  sMessage.Copy(pMsg);
218  if (!Fn) return;
219  sMessage.Append(" (");
220  // Show function name
221  if (Fn->GetName())
222  sMessage.AppendFormat("in %s", Fn->GetName());
223  if (Fn->GetName() && Fn->pOrgScript && SPos)
224  sMessage.Append(", ");
225  // Exact position
226  if (Fn->pOrgScript && SPos)
227  sMessage.AppendFormat("%s:%d:%d)",
229  SGetLine(Fn->pOrgScript->GetScript(), SPos),
230  SLineGetCharacters(Fn->pOrgScript->GetScript(), SPos));
231  else
232  sMessage.AppendChar(')');
233 }
234 
235 bool C4AulParse::AdvanceSpaces()
236 {
237  if (!SPos)
238  return false;
239  while(*SPos)
240  {
241  if (*SPos == '/')
242  {
243  // // comment
244  if (SPos[1] == '/')
245  {
246  SPos += 2;
247  while (*SPos && *SPos != 13 && *SPos != 10)
248  ++SPos;
249  }
250  // /* comment */
251  else if (SPos[1] == '*')
252  {
253  SPos += 2;
254  while (*SPos && (*SPos != '*' || SPos[1] != '/'))
255  ++SPos;
256  SPos += 2;
257  }
258  else
259  return true;
260  }
261  // Skip any "zero width no-break spaces" (also known as Byte Order Marks)
262  else if (*SPos == '\xEF' && SPos[1] == '\xBB' && SPos[2] == '\xBF')
263  SPos += 3;
264  else if ((unsigned)*SPos > 32)
265  return true;
266  else
267  ++SPos;
268  }
269  // end of script reached
270  return false;
271 }
272 
273 //=========================== C4Script Operator Map ===================================
275 {
276  // priority postfix
277  // | identifier | changer
278  // | | Bytecode | | no second id
279  // | | | | | | RetType ParType1 ParType2
280  // prefix
281  { 15, "++", AB_Inc, false, true, false, C4V_Int, C4V_Int, C4V_Any},
282  { 15, "--", AB_Dec, false, true, false, C4V_Int, C4V_Int, C4V_Any},
283  { 15, "~", AB_BitNot, false, false, false, C4V_Int, C4V_Int, C4V_Any},
284  { 15, "!", AB_Not, false, false, false, C4V_Bool, C4V_Bool, C4V_Any},
285  { 15, "+", AB_ERR, false, false, false, C4V_Int, C4V_Int, C4V_Any},
286  { 15, "-", AB_Neg, false, false, false, C4V_Int, C4V_Int, C4V_Any},
287 
288  // postfix (whithout second statement)
289  { 16, "++", AB_Inc, true, true, true, C4V_Int, C4V_Int, C4V_Any},
290  { 16, "--", AB_Dec, true, true, true, C4V_Int, C4V_Int, C4V_Any},
291 
292  // postfix
293  { 14, "**", AB_Pow, true, false, false, C4V_Int, C4V_Int, C4V_Int},
294  { 13, "/", AB_Div, true, false, false, C4V_Int, C4V_Int, C4V_Int},
295  { 13, "*", AB_Mul, true, false, false, C4V_Int, C4V_Int, C4V_Int},
296  { 13, "%", AB_Mod, true, false, false, C4V_Int, C4V_Int, C4V_Int},
297  { 12, "-", AB_Sub, true, false, false, C4V_Int, C4V_Int, C4V_Int},
298  { 12, "+", AB_Sum, true, false, false, C4V_Int, C4V_Int, C4V_Int},
299  { 11, "<<", AB_LeftShift, true, false, false, C4V_Int, C4V_Int, C4V_Int},
300  { 11, ">>", AB_RightShift, true, false, false, C4V_Int, C4V_Int, C4V_Int},
301  { 10, "<", AB_LessThan, true, false, false, C4V_Bool, C4V_Int, C4V_Int},
302  { 10, "<=", AB_LessThanEqual, true, false, false, C4V_Bool, C4V_Int, C4V_Int},
303  { 10, ">", AB_GreaterThan, true, false, false, C4V_Bool, C4V_Int, C4V_Int},
304  { 10, ">=", AB_GreaterThanEqual, true, false, false, C4V_Bool, C4V_Int, C4V_Int},
305  { 9, "==", AB_Equal, true, false, false, C4V_Bool, C4V_Any, C4V_Any},
306  { 9, "!=", AB_NotEqual, true, false, false, C4V_Bool, C4V_Any, C4V_Any},
307  { 8, "&", AB_BitAnd, true, false, false, C4V_Int, C4V_Int, C4V_Int},
308  { 6, "^", AB_BitXOr, true, false, false, C4V_Int, C4V_Int, C4V_Int},
309  { 6, "|", AB_BitOr, true, false, false, C4V_Int, C4V_Int, C4V_Int},
310  { 5, "&&", AB_JUMPAND, true, false, false, C4V_Bool, C4V_Bool, C4V_Bool},
311  { 4, "||", AB_JUMPOR, true, false, false, C4V_Bool, C4V_Bool, C4V_Bool},
312  { 3, "??", AB_JUMPNNIL, true, false, false, C4V_Any, C4V_Any, C4V_Any},
313 
314  // changers
315  { 2, "*=", AB_Mul, true, true, false, C4V_Int, C4V_Int, C4V_Int},
316  { 2, "/=", AB_Div, true, true, false, C4V_Int, C4V_Int, C4V_Int},
317  { 2, "%=", AB_Mod, true, true, false, C4V_Int, C4V_Int, C4V_Int},
318  { 2, "+=", AB_Sum, true, true, false, C4V_Int, C4V_Int, C4V_Int},
319  { 2, "-=", AB_Sub, true, true, false, C4V_Int, C4V_Int, C4V_Int},
320  { 2, "&=", AB_BitAnd, true, true, false, C4V_Int, C4V_Int, C4V_Int},
321  { 2, "|=", AB_BitOr, true, true, false, C4V_Int, C4V_Int, C4V_Int},
322  { 2, "^=", AB_BitXOr, true, true, false, C4V_Int, C4V_Int, C4V_Int},
323 
324  { 0, nullptr, AB_ERR, false, false, false, C4V_Nil, C4V_Nil, C4V_Nil}
325 };
326 
327 int C4AulParse::GetOperator(const char* pScript)
328 {
329  // return value:
330  // >= 0: operator found. could be found in C4ScriptOfDef
331  // -1: isn't an operator
332 
333  unsigned int i;
334 
335  if (!*pScript) return 0;
336  // operators are not alphabetical
337  if ((*pScript >= 'a' && *pScript <= 'z') ||
338  (*pScript >= 'A' && *pScript <= 'Z'))
339  {
340  return -1;
341  }
342 
343  // find the longest operator
344  int len = 0; int maxfound = -1;
345  for (i=0; C4ScriptOpMap[i].Identifier; i++)
346  {
347  if (SEqual2(pScript, C4ScriptOpMap[i].Identifier))
348  {
349  int oplen = SLen(C4ScriptOpMap[i].Identifier);
350  if (oplen > len)
351  {
352  len = oplen;
353  maxfound = i;
354  }
355  }
356  }
357  return maxfound;
358 }
359 
360 void C4AulParse::ClearToken()
361 {
362  // if last token was a string, make sure its ref is deleted
363  if (TokenType == ATT_STRING && cStr)
364  {
365  cStr->DecRef();
366  TokenType = ATT_INVALID;
367  }
368 }
369 
370 C4AulTokenType C4AulParse::GetNextToken()
371 {
372  // clear mem of prev token
373  ClearToken();
374  // move to start of token
375  if (!AdvanceSpaces()) return ATT_EOF;
376  // store offset
377  TokenSPos = SPos;
378 
379  // get char
380  char C = *(SPos++);
381  // Mostly sorted by frequency, except that tokens that have
382  // other tokens as prefixes need to be checked for first.
383  if (Inside(C, 'a', 'z') || Inside(C, 'A', 'Z') || C == '_' || C == '#')
384  {
385  // identifier or directive
386  bool dir = C == '#';
387  int Len = 1;
388  C = *SPos;
389  while (Inside(C, '0', '9') || Inside(C, 'a', 'z') || Inside(C, 'A', 'Z') || C == '_')
390  {
391  ++Len;
392  C = *(++SPos);
393  }
394 
395  // Special case for #warning because we don't want to give it to the parser
396  if (dir && SEqual2(TokenSPos, C4AUL_Warning))
397  {
398  // Look for end of line or end of file
399  while (*SPos != '\n' && *SPos != '\0') ++SPos;
400  Parse_WarningPragma();
401  // And actually return the next token.
402  return GetNextToken();
403  }
404 
405  Len = std::min(Len, C4AUL_MAX_Identifier);
406  SCopy(TokenSPos, Idtf, Len);
407  return dir ? ATT_DIR : ATT_IDTF;
408  }
409  else if (C == '(') return ATT_BOPEN; // "("
410  else if (C == ')') return ATT_BCLOSE; // ")"
411  else if (C == ',') return ATT_COMMA; // ","
412  else if (C == ';') return ATT_SCOLON; // ";"
413  else if (Inside(C, '0', '9'))
414  {
415  // integer
416  if (C == '0' && *SPos == 'x')
417  {
418  // hexadecimal
419  cInt = StrToI32(SPos + 1, 16, &SPos);
420  return ATT_INT;
421  }
422  else
423  {
424  // decimal
425  cInt = StrToI32(TokenSPos, 10, &SPos);
426  return ATT_INT;
427  }
428  }
429  else if (C == '-' && *SPos == '>' && *(SPos + 1) == '~')
430  { SPos+=2; return ATT_CALLFS;}// "->~"
431  else if (C == '-' && *SPos == '>')
432  { ++SPos; return ATT_CALL; } // "->"
433  else if ((cInt = GetOperator(SPos - 1)) != -1)
434  {
435  SPos += SLen(C4ScriptOpMap[cInt].Identifier) - 1;
436  return ATT_OPERATOR;
437  }
438  else if (C == '=') return ATT_SET; // "="
439  else if (C == '{') return ATT_BLOPEN; // "{"
440  else if (C == '}') return ATT_BLCLOSE;// "}"
441  else if (C == '"')
442  {
443  // string
444  std::string strbuf;
445  strbuf.reserve(512); // assume most strings to be smaller than this
446  // string end
447  while (*SPos != '"')
448  {
449  C = *SPos;
450  ++SPos;
451  if (C == '\\') // escape
452  switch (*SPos)
453  {
454  case '"': ++SPos; strbuf.push_back('"'); break;
455  case '\\': ++SPos; strbuf.push_back('\\'); break;
456  case 'n': ++SPos; strbuf.push_back('\n'); break;
457  case 't': ++SPos; strbuf.push_back('\t'); break;
458  case 'x':
459  {
460  ++SPos;
461  // hexadecimal escape: \xAD.
462  // First char must be a hexdigit
463  if (!std::isxdigit(*SPos))
464  {
465  Warn(C4AulWarningId::invalid_hex_escape);
466  strbuf.push_back('\\'); strbuf.push_back('x');
467  }
468  else
469  {
470  char ch = 0;
471  while (std::isxdigit(*SPos))
472  {
473  ch *= 16;
474  if (*SPos >= '0' && *SPos <= '9')
475  ch += *SPos - '0';
476  else if (*SPos >= 'a' && *SPos <= 'f')
477  ch += *SPos - 'a' + 10;
478  else if (*SPos >= 'A' && *SPos <= 'F')
479  ch += *SPos - 'A' + 10;
480  ++SPos;
481  };
482  strbuf.push_back(ch);
483  }
484  break;
485  }
486  case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
487  {
488  // Octal escape: \142
489  char ch = 0;
490  while (SPos[0] >= '0' && SPos[0] <= '7')
491  {
492  ch *= 8;
493  ch += *SPos++ -'0';
494  }
495  strbuf.push_back(ch);
496  break;
497  }
498  default:
499  {
500  // just insert "\"
501  strbuf.push_back('\\');
502  // show warning
503  Warn(C4AulWarningId::invalid_escape_sequence, *(SPos + 1));
504  }
505  }
506  else if (C == 0 || C == 10 || C == 13) // line break / feed
507  throw C4AulParseError(this, "string not closed");
508  else
509  // copy character
510  strbuf.push_back(C);
511  }
512  ++SPos;
513  cStr = Strings.RegString(StdStrBuf(strbuf.data(),strbuf.size()));
514  // hold onto string, ClearToken will deref it
515  cStr->IncRef();
516  return ATT_STRING;
517  }
518  else if (C == '[') return ATT_BOPEN2; // "["
519  else if (C == ']') return ATT_BCLOSE2;// "]"
520  else if (C == '.' && *SPos == '.' && *(SPos + 1) == '.')
521  { SPos+=2; return ATT_LDOTS; } // "..."
522  else if (C == '.') return ATT_DOT; // "."
523  else if (C == ':') return ATT_COLON; // ":"
524  else
525  {
526  // show appropriate error message
527  if (C >= '!' && C <= '~')
528  throw C4AulParseError(this, FormatString("unexpected character '%c' found", C).getData());
529  else
530  throw C4AulParseError(this, FormatString(R"(unexpected character \x%x found)", (int)(unsigned char) C).getData());
531  }
532 }
533 
534 static const char * GetTTName(C4AulBCCType e)
535 {
536  switch (e)
537  {
538  case AB_ARRAYA: return "ARRAYA"; // array access
539  case AB_ARRAYA_SET: return "ARRAYA_SET"; // setter
540  case AB_PROP: return "PROP";
541  case AB_PROP_SET: return "PROP_SET";
542  case AB_ARRAY_SLICE: return "ARRAY_SLICE";
543  case AB_ARRAY_SLICE_SET: return "ARRAY_SLICE_SET";
544  case AB_STACK_SET: return "STACK_SET";
545  case AB_LOCALN: return "LOCALN"; // a named local
546  case AB_LOCALN_SET: return "LOCALN_SET";
547  case AB_GLOBALN: return "GLOBALN"; // a named global
548  case AB_GLOBALN_SET: return "GLOBALN_SET";
549  case AB_PAR: return "PAR"; // Par statement
550  case AB_THIS: return "THIS";
551  case AB_FUNC: return "FUNC"; // function
552 
553 // prefix
554  case AB_Inc: return "Inc"; // ++
555  case AB_Dec: return "Dec"; // --
556  case AB_BitNot: return "BitNot"; // ~
557  case AB_Not: return "Not"; // !
558  case AB_Neg: return "Neg"; // -
559 
560 // postfix
561  case AB_Pow: return "Pow"; // **
562  case AB_Div: return "Div"; // /
563  case AB_Mul: return "Mul"; // *
564  case AB_Mod: return "Mod"; // %
565  case AB_Sub: return "Sub"; // -
566  case AB_Sum: return "Sum"; // +
567  case AB_LeftShift: return "LeftShift"; // <<
568  case AB_RightShift: return "RightShift"; // >>
569  case AB_LessThan: return "LessThan"; // <
570  case AB_LessThanEqual: return "LessThanEqual"; // <=
571  case AB_GreaterThan: return "GreaterThan"; // >
572  case AB_GreaterThanEqual: return "GreaterThanEqual"; // >=
573  case AB_Equal: return "Equal"; // ==
574  case AB_NotEqual: return "NotEqual"; // !=
575  case AB_BitAnd: return "BitAnd"; // &
576  case AB_BitXOr: return "BitXOr"; // ^
577  case AB_BitOr: return "BitOr"; // |
578 
579  case AB_CALL: return "CALL"; // direct object call
580  case AB_CALLFS: return "CALLFS"; // failsafe direct call
581  case AB_STACK: return "STACK"; // push nulls / pop
582  case AB_INT: return "INT"; // constant: int
583  case AB_BOOL: return "BOOL"; // constant: bool
584  case AB_STRING: return "STRING"; // constant: string
585  case AB_CPROPLIST: return "CPROPLIST"; // constant: proplist
586  case AB_CARRAY: return "CARRAY"; // constant: array
587  case AB_CFUNCTION: return "CFUNCTION"; // constant: function
588  case AB_NIL: return "NIL"; // constant: nil
589  case AB_NEW_ARRAY: return "NEW_ARRAY"; // semi-constant: array
590  case AB_DUP: return "DUP"; // duplicate value from stack
591  case AB_DUP_CONTEXT: return "AB_DUP_CONTEXT"; // duplicate value from stack of parent function
592  case AB_NEW_PROPLIST: return "NEW_PROPLIST"; // create a new proplist
593  case AB_POP_TO: return "POP_TO"; // initialization of named var
594  case AB_JUMP: return "JUMP"; // jump
595  case AB_JUMPAND: return "JUMPAND";
596  case AB_JUMPOR: return "JUMPOR";
597  case AB_JUMPNNIL: return "JUMPNNIL"; // nil-coalescing operator ("??")
598  case AB_CONDN: return "CONDN"; // conditional jump (negated, pops stack)
599  case AB_COND: return "COND"; // conditional jump (pops stack)
600  case AB_FOREACH_NEXT: return "FOREACH_NEXT"; // foreach: next element
601  case AB_RETURN: return "RETURN"; // return statement
602  case AB_ERR: return "ERR"; // parse error at this position
603  case AB_DEBUG: return "DEBUG"; // debug break
604  case AB_EOFN: return "EOFN"; // end of function
605  }
606  assert(false); return "UNKNOWN";
607 }
608 
610 {
612  {
613  fprintf(stderr, "%s:\n", GetName());
614  std::map<C4AulBCC *, int> labels;
615  int labeln = 0;
616  for (auto & bcc: Code)
617  {
618  switch (bcc.bccType)
619  {
620  case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_JUMPNNIL: case AB_CONDN: case AB_COND:
621  labels[&bcc + bcc.Par.i] = ++labeln; break;
622  default: break;
623  }
624  }
625  for (auto & bcc: Code)
626  {
627  C4AulBCCType eType = bcc.bccType;
628  if (labels.find(&bcc) != labels.end())
629  fprintf(stderr, "%d:\n", labels[&bcc]);
630  fprintf(stderr, "\t%d\t%-20s", GetLineOfCode(&bcc), GetTTName(eType));
631  switch (eType)
632  {
633  case AB_FUNC:
634  fprintf(stderr, "\t%s\n", bcc.Par.f->GetFullName().getData()); break;
635  case AB_ERR:
636  if (bcc.Par.s)
637  case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET: case AB_PROP: case AB_PROP_SET:
638  fprintf(stderr, "\t%s\n", bcc.Par.s->GetCStr()); break;
639  case AB_STRING:
640  {
641  const StdStrBuf &s = bcc.Par.s->GetData();
642  std::string es;
643  std::for_each(s.getData(), s.getData() + s.getLength(), [&es](char c) {
644  if (std::isgraph((unsigned char)c))
645  {
646  es += c;
647  }
648  else
649  {
650  switch (c)
651  {
652  case '\'': es.append(R"(\')"); break;
653  case '\"': es.append(R"(\")"); break;
654  case '\\': es.append(R"(\\)"); break;
655  case '\a': es.append(R"(\a)"); break;
656  case '\b': es.append(R"(\b)"); break;
657  case '\f': es.append(R"(\f)"); break;
658  case '\n': es.append(R"(\n)"); break;
659  case '\r': es.append(R"(\r)"); break;
660  case '\t': es.append(R"(\t)"); break;
661  case '\v': es.append(R"(\v)"); break;
662  default:
663  {
664  std::stringstream hex;
665  hex << R"(\x)" << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>((unsigned char)c);
666  es.append(hex.str());
667  break;
668  }
669  }
670  }
671  });
672  fprintf(stderr, "\t\"%s\"\n", es.c_str()); break;
673  }
674  case AB_DEBUG: case AB_NIL: case AB_RETURN:
675  case AB_PAR: case AB_THIS:
677  case AB_EOFN:
678  assert(!bcc.Par.X); fprintf(stderr, "\n"); break;
679  case AB_CARRAY:
680  fprintf(stderr, "\t%s\n", C4VArray(bcc.Par.a).GetDataString().getData()); break;
681  case AB_CPROPLIST:
682  fprintf(stderr, "\t%s\n", C4VPropList(bcc.Par.p).GetDataString().getData()); break;
683  case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_JUMPNNIL: case AB_CONDN: case AB_COND:
684  fprintf(stderr, "\t% -d\n", labels[&bcc + bcc.Par.i]); break;
685  default:
686  fprintf(stderr, "\t% -d\n", bcc.Par.i); break;
687  }
688  }
689  }
690 }
691 
693 {
694  // handle easiest case first
695  if (State < ASS_NONE) return false;
696 
697  // clear stuff
698  Includes.clear(); Appends.clear();
699 
700  GetPropList()->C4PropList::Clear();
702  LocalValues.Clear();
703 
704  // Add any engine functions specific to this script
706 
707  // Insert default warnings
708  assert(enabledWarnings.empty());
709  auto &warnings = enabledWarnings[Script.getData()];
710 #define DIAG(id, text, enabled) warnings.set(static_cast<size_t>(C4AulWarningId::id), enabled);
711 #include "C4AulWarnings.h"
712 #undef DIAG
713 
714  C4AulParse parser(this);
715  ast = parser.Parse_Script(this);
716 
717  C4AulCompiler::Preparse(this, this, ast.get());
718 
719  // #include will have to be resolved now...
720  IncludesResolved = false;
721 
722  // Parse will write the properties back after the ones from included scripts
723  GetPropList()->Properties.Swap(&LocalValues);
724 
725  // return success
726  this->State = ASS_PREPARSED;
727  return true;
728 }
729 
730 static const char * GetTokenName(C4AulTokenType TokenType)
731 {
732  switch (TokenType)
733  {
734  case ATT_INVALID: return "invalid token";
735  case ATT_DIR: return "directive";
736  case ATT_IDTF: return "identifier";
737  case ATT_INT: return "integer constant";
738  case ATT_STRING: return "string constant";
739  case ATT_DOT: return "'.'";
740  case ATT_COMMA: return "','";
741  case ATT_COLON: return "':'";
742  case ATT_SCOLON: return "';'";
743  case ATT_BOPEN: return "'('";
744  case ATT_BCLOSE: return "')'";
745  case ATT_BOPEN2: return "'['";
746  case ATT_BCLOSE2: return "']'";
747  case ATT_BLOPEN: return "'{'";
748  case ATT_BLCLOSE: return "'}'";
749  case ATT_CALL: return "'->'";
750  case ATT_CALLFS: return "'->~'";
751  case ATT_LDOTS: return "'...'";
752  case ATT_SET: return "'='";
753  case ATT_OPERATOR: return "operator";
754  case ATT_EOF: return "end of file";
755  default: return "unrecognized token";
756  }
757 }
758 
760 {
761  TokenType = GetNextToken();
762 }
763 void C4AulParse::Check(C4AulTokenType RefTokenType, const char * Expected)
764 {
765  if (TokenType != RefTokenType)
766  UnexpectedToken(Expected ? Expected : GetTokenName(RefTokenType));
767 }
768 void C4AulParse::Match(C4AulTokenType RefTokenType, const char * Expected)
769 {
770  Check(RefTokenType, Expected);
771  Shift();
772 }
773 void C4AulParse::UnexpectedToken(const char * Expected)
774 {
775  throw C4AulParseError(this, FormatString("%s expected, but found %s", Expected, GetTokenName(TokenType)).getData());
776 }
777 
778 void C4AulParse::Parse_WarningPragma()
779 {
780  assert(SEqual2(TokenSPos, C4AUL_Warning));
781  assert(std::isspace(TokenSPos[sizeof(C4AUL_Warning) - 1]));
782 
783 
784  // Read parameters in to string buffer. The sizeof() includes the terminating \0, but
785  // that's okay because we need to skip (at least) one whitespace character anyway.
786  std::string line(TokenSPos + sizeof(C4AUL_Warning), SPos);
787  auto end = line.end();
788  auto cursor = std::find_if_not(begin(line), end, IsWhiteSpace);
789 
790  if (cursor == end)
791  throw C4AulParseError(this, "'" C4Aul_Warning_enable "' or '" C4Aul_Warning_disable "' expected, but found end of line");
792 
793  // Split directive on whitespace
794  auto start = cursor;
795  cursor = std::find_if(start, end, IsWhiteSpace);
796  bool enable_warning = false;
797  if (std::equal(start, cursor, C4Aul_Warning_enable))
798  {
799  enable_warning = true;
800  }
801  else if (std::equal(start, cursor, C4Aul_Warning_disable))
802  {
803  enable_warning = false;
804  }
805  else
806  {
807  throw C4AulParseError(this, FormatString("'" C4Aul_Warning_enable "' or '" C4Aul_Warning_disable "' expected, but found '%s'", std::string(start, cursor).c_str()).getData());
808  }
809 
810  cursor = std::find_if_not(cursor, end, IsWhiteSpace);
811  if (cursor == end)
812  {
813  // enable or disable all warnings
814 #define DIAG(id, text, enabled) pOrgScript->EnableWarning(TokenSPos, C4AulWarningId::id, enable_warning);
815 #include "C4AulWarnings.h"
816 #undef DIAG
817  return;
818  }
819 
820  // enable or disable specific warnings
821  static const std::map<std::string, C4AulWarningId> warnings{
822 #define DIAG(id, text, enabled) std::make_pair(#id, C4AulWarningId::id),
823 #include "C4AulWarnings.h"
824 #undef DIAG
825  };
826  while (cursor != end)
827  {
828  start = std::find_if_not(cursor, end, IsWhiteSpace);
829  cursor = std::find_if(start, end, IsWhiteSpace);
830  auto entry = warnings.find(std::string(start, cursor));
831  if (entry != warnings.end())
832  {
833  pOrgScript->EnableWarning(TokenSPos, entry->second, enable_warning);
834  }
835  }
836 }
837 
839 {
840  ClearCode();
841  // parse
842  C4AulParse state(this, context, Engine);
843  auto func = state.Parse_DirectExec(Script, true);
844  C4AulCompiler::Compile(this, func.get());
845 }
846 
848 {
849  ClearCode();
850  // parse
851  C4AulParse state(this, context, Engine);
852  auto func = state.Parse_DirectExec(Script, false);
853  C4AulCompiler::Compile(this, func.get());
854 }
855 
856 std::unique_ptr<::aul::ast::FunctionDecl> C4AulParse::Parse_DirectExec(const char *code, bool whole_function)
857 {
858  // get first token
859  Shift();
860  // Synthesize a wrapping function which we can call
861  std::unique_ptr<::aul::ast::FunctionDecl> func;
862  if (whole_function)
863  {
865  }
866  else
867  {
868  auto expr = Parse_Expression();
869  func = std::make_unique<::aul::ast::FunctionDecl>("$internal$eval");
870  func->body = std::make_unique<::aul::ast::Block>();
871  func->body->children.push_back(std::make_unique<::aul::ast::Return>(std::move(expr)));
872  }
873  Match(ATT_EOF);
874  return func;
875 }
876 
877 std::unique_ptr<::aul::ast::Script> C4AulParse::Parse_Script(C4ScriptHost * scripthost)
878 {
879  pOrgScript = scripthost;
880  SPos = pOrgScript->Script.getData();
881  const char * SPos0 = SPos;
882  bool first_error = true;
883  auto script = ::aul::ast::Script::New(SPos0);
884  while (true) try
885  {
886  // Go to the next token if the current token could not be processed or no token has yet been parsed
887  if (SPos == SPos0)
888  {
889  Shift();
890  }
891  SPos0 = SPos;
892  switch (TokenType)
893  {
894  case ATT_DIR:
895  // check for include statement
896  if (SEqual(Idtf, C4AUL_Include))
897  {
898  Shift();
899  // get id of script to include
900  Check(ATT_IDTF, "script name");
901  script->declarations.push_back(::aul::ast::IncludePragma::New(TokenSPos, Idtf));
902  Shift();
903  }
904  else if (SEqual(Idtf, C4AUL_Append))
905  {
906  if (pOrgScript->GetPropList()->GetDef())
907  throw C4AulParseError(this, "#appendto in a Definition");
908  Shift();
909  // get id of script to include/append
910  switch (TokenType)
911  {
912  case ATT_IDTF:
913  script->declarations.push_back(::aul::ast::AppendtoPragma::New(TokenSPos, Idtf));
914  break;
915  case ATT_OPERATOR:
916  if (SEqual(C4ScriptOpMap[cInt].Identifier, "*"))
917  {
918  script->declarations.push_back(::aul::ast::AppendtoPragma::New(TokenSPos));
919  break;
920  }
921  //fallthrough
922  default:
923  // -> ID expected
924  UnexpectedToken("identifier or '*'");
925  }
926  Shift();
927  }
928  else
929  // -> unknown directive
930  Error("unknown directive: %s", Idtf);
931  break;
932  case ATT_IDTF:
933  // need a keyword here to avoid parsing random function contents
934  // after a syntax error in a function
935  // check for object-local variable definition (local)
936  if (SEqual(Idtf, C4AUL_LocalNamed) || SEqual(Idtf, C4AUL_GlobalNamed))
937  {
938  script->declarations.push_back(Parse_Var());
939  Match(ATT_SCOLON);
940  }
941  // check for variable definition (static)
942  else
943  script->declarations.push_back(Parse_ToplevelFunctionDecl());
944  break;
945  case ATT_EOF:
946  return script;
947  default:
948  UnexpectedToken("declaration");
949  }
950  first_error = true;
951  }
952  catch (C4AulError &err)
953  {
954  if (first_error)
955  {
956  ++Engine->errCnt;
958  }
959  first_error = false;
960  }
961 }
962 
963 std::unique_ptr<::aul::ast::FunctionDecl> C4AulParse::Parse_ToplevelFunctionDecl()
964 {
965  const char *NodeStart = TokenSPos;
966  bool is_global = SEqual(Idtf, C4AUL_Global);
967  // skip access modifier
968  if (SEqual(Idtf, C4AUL_Private) ||
969  SEqual(Idtf, C4AUL_Protected) ||
970  SEqual(Idtf, C4AUL_Public) ||
971  SEqual(Idtf, C4AUL_Global))
972  {
973  Shift();
974  }
975 
976  // check for func declaration
977  if (!SEqual(Idtf, C4AUL_Func))
978  Error("Declaration expected, but found identifier: %s", Idtf);
979  Shift();
980  // get next token, must be func name
981  Check(ATT_IDTF, "function name");
982 
983  auto func = ::aul::ast::FunctionDecl::New(NodeStart, Idtf);
984  func->is_global = is_global;
985  Shift();
986  Parse_Function(func.get());
987  return func;
988 }
989 
990 void C4AulParse::Parse_Function(::aul::ast::Function *func)
991 {
992  Match(ATT_BOPEN);
993  // get pars
994  while (TokenType != ATT_BCLOSE)
995  {
996  // too many parameters?
997  if (func->params.size() >= C4AUL_MAX_Par)
998  throw C4AulParseError(this, "'func' parameter list: too many parameters (max 10)");
999  if (TokenType == ATT_LDOTS)
1000  {
1001  func->has_unnamed_params = true;
1002  Shift();
1003  // don't allow any more parameters after ellipsis
1004  break;
1005  }
1006  // must be a name or type now
1007  Check(ATT_IDTF, "parameter, '...', or ')'");
1008  // type identifier?
1009  C4V_Type t = C4V_Any;
1010  if (SEqual(Idtf, C4AUL_TypeInt)) { t = C4V_Int; Shift(); }
1011  else if (SEqual(Idtf, C4AUL_TypeBool)) { t = C4V_Bool; Shift(); }
1012  else if (SEqual(Idtf, C4AUL_TypeC4ID)) { t = C4V_Def; Shift(); }
1013  else if (SEqual(Idtf, C4AUL_TypeDef)) { t = C4V_Def; Shift(); }
1014  else if (SEqual(Idtf, C4AUL_TypeEffect)) { t = C4V_Effect; Shift(); }
1015  else if (SEqual(Idtf, C4AUL_TypeC4Object)) { t = C4V_Object; Shift(); }
1016  else if (SEqual(Idtf, C4AUL_TypePropList)) { t = C4V_PropList; Shift(); }
1017  else if (SEqual(Idtf, C4AUL_TypeString)) { t = C4V_String; Shift(); }
1018  else if (SEqual(Idtf, C4AUL_TypeArray)) { t = C4V_Array; Shift(); }
1019  else if (SEqual(Idtf, C4AUL_TypeFunction)) { t = C4V_Function; Shift(); }
1020  // a parameter name which matched a type name?
1021  std::string par_name;
1022  if (TokenType == ATT_BCLOSE || TokenType == ATT_COMMA)
1023  {
1024  par_name = Idtf;
1025  Warn(C4AulWarningId::type_name_used_as_par_name, Idtf);
1026  }
1027  else
1028  {
1029  Check(ATT_IDTF, "parameter name");
1030  par_name = Idtf;
1031  Shift();
1032  }
1033  func->params.emplace_back(par_name, t);
1034  // end of params?
1035  if (TokenType == ATT_BCLOSE)
1036  {
1037  break;
1038  }
1039  // must be a comma now
1040  Match(ATT_COMMA, "',' or ')'");
1041  }
1042  Match(ATT_BCLOSE);
1043  func->body = Parse_Block();
1044 }
1045 
1046 std::unique_ptr<::aul::ast::Block> C4AulParse::Parse_Block()
1047 {
1048  auto block = ::aul::ast::Block::New(TokenSPos);
1049  Match(ATT_BLOPEN);
1050  while (TokenType != ATT_BLCLOSE)
1051  {
1052  block->children.push_back(Parse_Statement());
1053  }
1054  Shift();
1055  return block;
1056 }
1057 
1058 std::unique_ptr<::aul::ast::Stmt> C4AulParse::Parse_Statement()
1059 {
1060  const char *NodeStart = TokenSPos;
1061  std::unique_ptr<::aul::ast::Stmt> stmt;
1062  switch (TokenType)
1063  {
1064  // do we have a block start?
1065  case ATT_BLOPEN:
1066  return Parse_Block();
1067  case ATT_BOPEN:
1068  case ATT_BOPEN2:
1069  case ATT_SET:
1070  case ATT_OPERATOR:
1071  case ATT_INT:
1072  case ATT_STRING:
1073  {
1074  stmt = Parse_Expression();
1075  Match(ATT_SCOLON);
1076  return stmt;
1077  }
1078  // additional function separator
1079  case ATT_SCOLON:
1080  Shift();
1081  return ::aul::ast::Noop::New(NodeStart);
1082  case ATT_IDTF:
1083  // check for variable definition
1084  if (SEqual(Idtf, C4AUL_VarNamed) || SEqual(Idtf, C4AUL_LocalNamed) || SEqual(Idtf, C4AUL_GlobalNamed))
1085  stmt = Parse_Var();
1086  // check new-form func begin
1087  else if (SEqual(Idtf, C4AUL_Func) ||
1088  SEqual(Idtf, C4AUL_Private) ||
1089  SEqual(Idtf, C4AUL_Protected) ||
1090  SEqual(Idtf, C4AUL_Public) ||
1091  SEqual(Idtf, C4AUL_Global))
1092  {
1093  throw C4AulParseError(this, "unexpected end of function");
1094  }
1095  // get function by identifier: first check special functions
1096  else if (SEqual(Idtf, C4AUL_If)) // if
1097  {
1098  return Parse_If();
1099  }
1100  else if (SEqual(Idtf, C4AUL_Else)) // else
1101  {
1102  throw C4AulParseError(this, "misplaced 'else'");
1103  }
1104  else if (SEqual(Idtf, C4AUL_Do)) // while
1105  {
1106  stmt = Parse_DoWhile();
1107  }
1108  else if (SEqual(Idtf, C4AUL_While)) // while
1109  {
1110  return Parse_While();
1111  }
1112  else if (SEqual(Idtf, C4AUL_For)) // for
1113  {
1114  PushParsePos();
1115  Shift();
1116  // Look if it's the for([var] foo in array)-form
1117  // must be followed by a bracket
1118  Match(ATT_BOPEN);
1119  // optional var
1120  if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_VarNamed))
1121  Shift();
1122  // variable and "in"
1123  if (TokenType == ATT_IDTF
1124  && GetNextToken() == ATT_IDTF
1125  && SEqual(Idtf, C4AUL_In))
1126  {
1127  // reparse the stuff in the brackets like normal statements
1128  PopParsePos();
1129  return Parse_ForEach();
1130  }
1131  else
1132  {
1133  // reparse the stuff in the brackets like normal statements
1134  PopParsePos();
1135  return Parse_For();
1136  }
1137  }
1138  else if (SEqual(Idtf, C4AUL_Return)) // return
1139  {
1140  Shift();
1141  if (TokenType == ATT_SCOLON)
1142  {
1143  // allow return; without return value (implies nil)
1144  stmt = ::aul::ast::Return::New(NodeStart, ::aul::ast::NilLit::New(NodeStart));
1145  }
1146  else
1147  {
1148  // return retval;
1149  stmt = ::aul::ast::Return::New(NodeStart, Parse_Expression());
1150  }
1151  }
1152  else if (SEqual(Idtf, C4AUL_Break)) // break
1153  {
1154  Shift();
1155  stmt = ::aul::ast::Break::New(NodeStart);
1156  }
1157  else if (SEqual(Idtf, C4AUL_Continue)) // continue
1158  {
1159  Shift();
1160  stmt = ::aul::ast::Continue::New(NodeStart);
1161  }
1162  else
1163  {
1164  stmt = Parse_Expression();
1165  }
1166  Match(ATT_SCOLON);
1167  assert(stmt);
1168  return stmt;
1169  default:
1170  UnexpectedToken("statement");
1171  }
1172 }
1173 
1174 void C4AulParse::Parse_CallParams(::aul::ast::CallExpr *call)
1175 {
1176  assert(call != nullptr);
1177  assert(call->args.empty());
1178 
1179  // so it's a regular function; force "("
1180  Match(ATT_BOPEN);
1181  while(TokenType != ATT_BCLOSE) switch(TokenType)
1182  {
1183  case ATT_COMMA:
1184  // got no parameter before a ","
1185  Warn(C4AulWarningId::empty_parameter_in_call, (unsigned)call->args.size(), call->callee.c_str());
1186  call->args.push_back(::aul::ast::NilLit::New(TokenSPos));
1187  Shift();
1188  break;
1189  case ATT_LDOTS:
1190  // functions using ... always take as many parameters as possible
1191  Shift();
1192  call->append_unnamed_pars = true;
1193  // Do not allow more parameters even if there is space left
1194  Check(ATT_BCLOSE);
1195  break;
1196  default:
1197  // get a parameter
1198  call->args.push_back(Parse_Expression());
1199  // end of parameter list?
1200  if (TokenType != ATT_BCLOSE)
1201  Match(ATT_COMMA, "',' or ')'");
1202  break;
1203  }
1204  Match(ATT_BCLOSE);
1205 }
1206 
1207 std::unique_ptr<::aul::ast::ArrayLit> C4AulParse::Parse_Array()
1208 {
1209  auto arr = ::aul::ast::ArrayLit::New(TokenSPos);
1210  // force "["
1211  Match(ATT_BOPEN2);
1212  // Create an array
1213  while (TokenType != ATT_BCLOSE2)
1214  {
1215  // got no parameter before a ","? then push nil
1216  if (TokenType == ATT_COMMA)
1217  {
1218  Warn(C4AulWarningId::empty_parameter_in_array, (unsigned)arr->values.size());
1219  arr->values.emplace_back(::aul::ast::NilLit::New(TokenSPos));
1220  }
1221  else
1222  arr->values.emplace_back(Parse_Expression());
1223  if (TokenType == ATT_BCLOSE2)
1224  break;
1225  Match(ATT_COMMA, "',' or ']'");
1226  // [] -> size 0, [*,] -> size 2, [*,*,] -> size 3
1227  if (TokenType == ATT_BCLOSE2)
1228  {
1229  Warn(C4AulWarningId::empty_parameter_in_array, (unsigned)arr->values.size());
1230  arr->values.emplace_back(::aul::ast::NilLit::New(TokenSPos));
1231  }
1232  }
1233  Shift();
1234  return arr;
1235 }
1236 
1237 std::unique_ptr<::aul::ast::ProplistLit> C4AulParse::Parse_PropList()
1238 {
1239  auto proplist = ::aul::ast::ProplistLit::New(TokenSPos);
1240  if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_New))
1241  {
1242  Shift();
1243  proplist->values.emplace_back(Strings.P[P_Prototype].GetCStr(), Parse_Expression());
1244  }
1245  Match(ATT_BLOPEN);
1246  while (TokenType != ATT_BLCLOSE)
1247  {
1248  std::string key;
1249  if (TokenType == ATT_IDTF)
1250  {
1251  key = Idtf;
1252  Shift();
1253  }
1254  else if (TokenType == ATT_STRING)
1255  {
1256  key = cStr->GetCStr();
1257  Shift();
1258  }
1259  else UnexpectedToken("string or identifier");
1260  if (TokenType != ATT_COLON && TokenType != ATT_SET)
1261  UnexpectedToken("':' or '='");
1262  Shift();
1263  proplist->values.emplace_back(key, Parse_Expression());
1264  if (TokenType == ATT_COMMA)
1265  Shift();
1266  else if (TokenType != ATT_BLCLOSE)
1267  UnexpectedToken("'}' or ','");
1268  }
1269  Shift();
1270  return proplist;
1271 }
1272 
1273 std::unique_ptr<::aul::ast::DoLoop> C4AulParse::Parse_DoWhile()
1274 {
1275  auto loop = ::aul::ast::DoLoop::New(TokenSPos);
1276  Shift();
1277  loop->body = Parse_Statement();
1278  // Execute condition
1279  if (TokenType != ATT_IDTF || !SEqual(Idtf, C4AUL_While))
1280  UnexpectedToken("'while'");
1281  Shift();
1282  Match(ATT_BOPEN);
1283  loop->cond = Parse_Expression();
1284  Match(ATT_BCLOSE);
1285  return loop;
1286 }
1287 
1288 std::unique_ptr<::aul::ast::WhileLoop> C4AulParse::Parse_While()
1289 {
1290  auto loop = ::aul::ast::WhileLoop::New(TokenSPos);
1291  Shift();
1292  // Execute condition
1293  Match(ATT_BOPEN);
1294  loop->cond = Parse_Expression();
1295  Match(ATT_BCLOSE);
1296  // Execute body
1297  loop->body = Parse_Statement();
1298  return loop;
1299 }
1300 
1301 std::unique_ptr<::aul::ast::If> C4AulParse::Parse_If()
1302 {
1303  auto stmt = ::aul::ast::If::New(TokenSPos);
1304  Shift();
1305  Match(ATT_BOPEN);
1306  stmt->cond = Parse_Expression();
1307  Match(ATT_BCLOSE);
1308  // parse controlled statement
1309  stmt->iftrue = Parse_Statement();
1310  if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_Else))
1311  {
1312  Shift();
1313  // expect a command now
1314  stmt->iffalse = Parse_Statement();
1315  }
1316  return stmt;
1317 }
1318 
1319 std::unique_ptr<::aul::ast::ForLoop> C4AulParse::Parse_For()
1320 {
1321  auto loop = ::aul::ast::ForLoop::New(TokenSPos);
1322  Match(ATT_IDTF); Match(ATT_BOPEN);
1323  // Initialization
1324  if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_VarNamed))
1325  {
1326  loop->init = Parse_Var();
1327  }
1328  else if (TokenType != ATT_SCOLON)
1329  {
1330  loop->init = Parse_Expression();
1331  }
1332  // Consume first semicolon
1333  Match(ATT_SCOLON);
1334  // Condition
1335  if (TokenType != ATT_SCOLON)
1336  {
1337  loop->cond = Parse_Expression();
1338  }
1339  // Consume second semicolon
1340  Match(ATT_SCOLON);
1341  // Incrementor
1342  if (TokenType != ATT_BCLOSE)
1343  {
1344  loop->incr = Parse_Expression();
1345  }
1346  // Consume closing bracket
1347  Match(ATT_BCLOSE);
1348  loop->body = Parse_Statement();
1349  return loop;
1350 }
1351 
1352 std::unique_ptr<::aul::ast::RangeLoop> C4AulParse::Parse_ForEach()
1353 {
1354  auto loop = ::aul::ast::RangeLoop::New(TokenSPos);
1355  Match(ATT_IDTF); Match(ATT_BOPEN);
1356  if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_VarNamed))
1357  {
1358  loop->scoped_var = true;
1359  Shift();
1360  }
1361  // get variable name
1362  Check(ATT_IDTF, "variable name");
1363  loop->var = Idtf;
1364  Shift();
1365  if (TokenType != ATT_IDTF || !SEqual(Idtf, C4AUL_In))
1366  UnexpectedToken("'in'");
1367  Shift();
1368  // get expression for array
1369  loop->cond = Parse_Expression();
1370  Match(ATT_BCLOSE);
1371  // loop body
1372  loop->body = Parse_Statement();
1373  return loop;
1374 }
1375 
1376 static bool GetPropertyByS(const C4PropList * p, const char * s, C4Value & v)
1377 {
1378  C4String * k = Strings.FindString(s);
1379  if (!k) return false;
1380  return p->GetPropertyByS(k, &v);
1381 }
1382 
1383 std::unique_ptr<::aul::ast::Expr> C4AulParse::Parse_Expression(int iParentPrio)
1384 {
1385  const char *NodeStart = TokenSPos;
1386  std::unique_ptr<::aul::ast::Expr> expr;
1387  const C4ScriptOpDef * op;
1388  C4AulFunc *FoundFn = nullptr;
1389  C4Value val;
1390  switch (TokenType)
1391  {
1392  case ATT_IDTF:
1393  // XXX: Resolving literals here means that you can't create a variable
1394  // with the names "true", "false", "nil" or "this" anymore. I don't
1395  // consider this too much of a problem, because there is never a reason
1396  // to do this and it makes my job a lot easier
1397  if (SEqual(Idtf, C4AUL_True))
1398  {
1399  Shift();
1400  expr = ::aul::ast::BoolLit::New(NodeStart, true);
1401  }
1402  else if (SEqual(Idtf, C4AUL_False))
1403  {
1404  Shift();
1405  expr = ::aul::ast::BoolLit::New(NodeStart, false);
1406  }
1407  else if (SEqual(Idtf, C4AUL_Nil))
1408  {
1409  Shift();
1410  expr = ::aul::ast::NilLit::New(NodeStart);
1411  }
1412  else if (SEqual(Idtf, C4AUL_this))
1413  {
1414  Shift();
1415  if (TokenType == ATT_BOPEN)
1416  {
1417  Shift();
1418  Match(ATT_BCLOSE);
1419  // TODO: maybe warn about "this" with parentheses?
1420  }
1421  expr = ::aul::ast::ThisLit::New(NodeStart);
1422  }
1423  // XXX: Other things that people aren't allowed to do anymore: name their variables or functions any of:
1424  // "if", "else", "for", "while", "do", "return", or "Par".
1425  // We could allow variables with these names and disambiguate based on the syntax, but no.
1426  else if (SEqual(Idtf, C4AUL_If) || SEqual(Idtf, C4AUL_Else) || SEqual(Idtf, C4AUL_For) || SEqual(Idtf, C4AUL_While) || SEqual(Idtf, C4AUL_Do) || SEqual(Idtf, C4AUL_Return))
1427  {
1428  Error("reserved identifier not allowed in expressions: %s", Idtf);
1429  }
1430  else if (SEqual(Idtf, C4AUL_Par))
1431  {
1432  Shift();
1433  // "Par" is special in that it isn't a function and thus doesn't accept an arbitrary number of parameters
1434  Match(ATT_BOPEN);
1435  expr = ::aul::ast::ParExpr::New(NodeStart, Parse_Expression());
1436  Match(ATT_BCLOSE);
1437  }
1438  else if (SEqual(Idtf, C4AUL_New))
1439  {
1440  // Because people might call a variables or functions "new", we need to look ahead and guess whether it's a proplist constructor.
1441  PushParsePos();
1442  Shift();
1443  if (TokenType == ATT_IDTF)
1444  {
1445  // this must be a proplist because two identifiers can't immediately follow each other
1446  PopParsePos();
1447  expr = Parse_PropList();
1448  }
1449  else
1450  {
1451  // Some non-identifier means this is either a variable, a function, or a syntax error. Which one exactly is something we'll figure out later.
1452  PopParsePos();
1453  }
1454  }
1455  else if (SEqual(Idtf, C4AUL_Func))
1456  {
1457  PushParsePos();
1458  Shift();
1459  if (TokenType == ATT_BOPEN)
1460  {
1461  auto func = ::aul::ast::FunctionExpr::New(NodeStart);
1462  Parse_Function(func.get());
1463  expr = std::move(func);
1464  DiscardParsePos();
1465  }
1466  else
1467  {
1468  PopParsePos();
1469  }
1470  }
1471  if (!expr)
1472  {
1473  // If we end up here, it must be a proper identifier (or a reserved word that's used as an identifier).
1474  // Time to look ahead and see whether it's a function call.
1475  std::string identifier = Idtf;
1476  Shift();
1477  if (TokenType == ATT_BOPEN)
1478  {
1479  // Well, it looks like one, at least
1480  auto func = ::aul::ast::CallExpr::New(NodeStart);
1481  func->callee = identifier;
1482  Parse_CallParams(func.get());
1483  expr = std::move(func);
1484  }
1485  else
1486  {
1487  // It's most certainly not a function call.
1488  expr = ::aul::ast::VarExpr::New(NodeStart, identifier);
1489  }
1490  }
1491  break;
1492  case ATT_INT: // constant in cInt
1493  expr = ::aul::ast::IntLit::New(NodeStart, cInt);
1494  Shift();
1495  break;
1496  case ATT_STRING: // reference in cStr
1497  expr = ::aul::ast::StringLit::New(NodeStart, cStr->GetCStr());
1498  Shift();
1499  break;
1500  case ATT_OPERATOR:
1501  // -> must be a prefix operator
1502  op = &C4ScriptOpMap[cInt];
1503  // postfix?
1504  if (op->Postfix)
1505  // oops. that's wrong
1506  throw C4AulParseError(this, "postfix operator without first expression");
1507  Shift();
1508  // generate code for the following expression
1509  expr = Parse_Expression(op->Priority);
1510  if (SEqual(op->Identifier, "+"))
1511  {
1512  // This is a no-op.
1513  }
1514  else
1515  {
1516  expr = ::aul::ast::UnOpExpr::New(NodeStart, op - C4ScriptOpMap, std::move(expr));
1517  }
1518  break;
1519  case ATT_BOPEN:
1520  Shift();
1521  expr = Parse_Expression();
1522  Match(ATT_BCLOSE);
1523  break;
1524  case ATT_BOPEN2:
1525  expr = Parse_Array();
1526  break;
1527  case ATT_BLOPEN:
1528  expr = Parse_PropList();
1529  break;
1530  default:
1531  UnexpectedToken("expression");
1532  }
1533 
1534  assert(expr);
1535 
1536  while (true)
1537  {
1538  NodeStart = TokenSPos;
1539  switch (TokenType)
1540  {
1541  case ATT_SET:
1542  {
1543  // back out of any kind of parent operator
1544  // (except other setters, as those are right-associative)
1545  if (iParentPrio > 1)
1546  return expr;
1547  Shift();
1548  expr = ::aul::ast::AssignmentExpr::New(NodeStart, std::move(expr), Parse_Expression(1));
1549  break;
1550  }
1551  case ATT_OPERATOR:
1552  {
1553  // expect postfix operator
1554  const C4ScriptOpDef * op = &C4ScriptOpMap[cInt];
1555  if (!op->Postfix)
1556  {
1557  // does an operator with the same name exist?
1558  // when it's a postfix-operator, it can be used instead.
1559  const C4ScriptOpDef * postfixop;
1560  for (postfixop = op + 1; postfixop->Identifier; ++postfixop)
1561  if (SEqual(op->Identifier, postfixop->Identifier))
1562  if (postfixop->Postfix)
1563  break;
1564  // not found?
1565  if (!postfixop->Identifier)
1566  {
1567  Error("unexpected prefix operator: %s", op->Identifier);
1568  }
1569  // otherwise use the new-found correct postfix operator
1570  op = postfixop;
1571  }
1572 
1573  if (iParentPrio + !op->Changer > op->Priority)
1574  return expr;
1575 
1576  Shift();
1577  if (op->NoSecondStatement)
1578  {
1579  // Postfix unary op
1580  expr = ::aul::ast::UnOpExpr::New(NodeStart, op - C4ScriptOpMap, std::move(expr));
1581  }
1582  else
1583  {
1584  expr = ::aul::ast::BinOpExpr::New(NodeStart, op - C4ScriptOpMap, std::move(expr), Parse_Expression(op->Priority));
1585  }
1586  break;
1587  }
1588  case ATT_BOPEN2:
1589  {
1590  // parse either [index], or [start:end] in which case either index is optional
1591  Shift();
1592  ::aul::ast::ExprPtr start;
1593  if (TokenType == ATT_COLON)
1594  start = ::aul::ast::IntLit::New(TokenSPos, 0); // slice with first index missing -> implicit start index zero
1595  else
1596  start = Parse_Expression();
1597 
1598  if (TokenType == ATT_BCLOSE2)
1599  {
1600  expr = ::aul::ast::SubscriptExpr::New(NodeStart, std::move(expr), std::move(start));
1601  }
1602  else if (TokenType == ATT_COLON)
1603  {
1604  Shift();
1605  ::aul::ast::ExprPtr end;
1606  if (TokenType == ATT_BCLOSE2)
1607  {
1608  end = ::aul::ast::IntLit::New(TokenSPos, std::numeric_limits<int32_t>::max());
1609  }
1610  else
1611  {
1612  end = Parse_Expression();
1613  }
1614  expr = ::aul::ast::SliceExpr::New(NodeStart, std::move(expr), std::move(start), std::move(end));
1615  }
1616  else
1617  {
1618  UnexpectedToken("']' or ':'");
1619  }
1620  Match(ATT_BCLOSE2);
1621  break;
1622  }
1623  case ATT_DOT:
1624  Shift();
1625  Check(ATT_IDTF, "property name");
1626  expr = ::aul::ast::SubscriptExpr::New(NodeStart, std::move(expr), ::aul::ast::StringLit::New(TokenSPos, Idtf));
1627  Shift();
1628  break;
1629  case ATT_CALL: case ATT_CALLFS:
1630  {
1631  auto call = ::aul::ast::CallExpr::New(NodeStart);
1632  call->context = std::move(expr);
1633  call->safe_call = TokenType == ATT_CALLFS;
1634  Shift();
1635  Check(ATT_IDTF, "function name after '->'");
1636  call->callee = Idtf;
1637  Shift();
1638  Parse_CallParams(call.get());
1639  expr = std::move(call);
1640  break;
1641  }
1642  default:
1643  return expr;
1644  }
1645  }
1646 }
1647 
1648 std::unique_ptr<::aul::ast::VarDecl> C4AulParse::Parse_Var()
1649 {
1650  auto decl = ::aul::ast::VarDecl::New(TokenSPos);
1651  if (SEqual(Idtf, C4AUL_VarNamed))
1652  {
1653  decl->scope = ::aul::ast::VarDecl::Scope::Func;
1654  }
1655  else if (SEqual(Idtf, C4AUL_LocalNamed))
1656  {
1657  decl->scope = ::aul::ast::VarDecl::Scope::Object;
1658  }
1659  else if (SEqual(Idtf, C4AUL_GlobalNamed))
1660  {
1661  decl->scope = ::aul::ast::VarDecl::Scope::Global;
1662  }
1663  else
1664  {
1665  assert(0 && "C4AulParse::Parse_Var called with invalid parse state (current token should be scope of variable)");
1666  // Uh this shouldn't happen, ever
1667  Error("internal error: C4AulParse::Parse_Var called with invalid parse state (current token should be scope of variable, but is '%s')", Idtf);
1668  }
1669  Shift();
1670  if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_Const))
1671  {
1672  decl->constant = true;
1673  Shift();
1674  }
1675  while (true)
1676  {
1677  // get desired variable name
1678  Check(ATT_IDTF, "variable name");
1679  std::string identifier = Idtf;
1680  Shift();
1681  ::aul::ast::ExprPtr init;
1682  if (TokenType == ATT_SET)
1683  {
1684  Shift();
1685  init = Parse_Expression();
1686  }
1687  decl->decls.push_back({ identifier, std::move(init) });
1688  if (TokenType == ATT_SCOLON)
1689  return decl;
1690  Match(ATT_COMMA, "',' or ';'");
1691  }
1692 }
1693 
1695 {
1696  // append all funcs and local variable initializations
1697  const C4Property * prop = from.First();
1698  while (prop)
1699  {
1700  switch(prop->Value.GetType())
1701  {
1702  case C4V_Function:
1703  {
1704  C4AulScriptFunc * sf = prop->Value.getFunction()->SFunc();
1705  if (sf)
1706  {
1707  C4AulScriptFunc *sfc;
1708  if (sf->pOrgScript != this)
1709  sfc = new C4AulScriptFunc(to, *sf);
1710  else
1711  sfc = sf;
1712  sfc->SetOverloaded(to->GetFunc(sf->Name));
1713  to->SetPropertyByS(prop->Key, C4VFunction(sfc));
1714  }
1715  else
1716  {
1717  // engine function
1718  to->SetPropertyByS(prop->Key, prop->Value);
1719  }
1720  }
1721  break;
1722  case C4V_PropList:
1723  {
1724  C4PropListStatic * p = prop->Value._getPropList()->IsStatic();
1725  assert(p);
1726  if (prop->Key != &::Strings.P[P_Prototype])
1727  if (!p || p->GetParent() != to)
1728  {
1729  p = C4PropList::NewStatic(nullptr, to, prop->Key);
1730  CopyPropList(prop->Value._getPropList()->Properties, p);
1731  }
1732  to->SetPropertyByS(prop->Key, C4VPropList(p));
1733  }
1734  break;
1735  case C4V_Array: // FIXME: copy the array if necessary
1736  default:
1737  to->SetPropertyByS(prop->Key, prop->Value);
1738  }
1739  prop = from.Next(prop);
1740  }
1741 }
1742 
1744 {
1745  // check state
1746  if (State != ASS_LINKED) return false;
1747 
1748  if (!Appends.empty())
1749  {
1750  // #appendto scripts are not allowed to contain global functions or belong to definitions
1751  // so their contents are not reachable
1752  return true;
1753  }
1754 
1755  C4PropListStatic * p = GetPropList();
1756 
1757  for (auto & SourceScript : SourceScripts)
1758  {
1759  CopyPropList(SourceScript->LocalValues, p);
1760  if (SourceScript == this)
1761  continue;
1762  // definition appends
1763  if (GetPropList() && GetPropList()->GetDef() && SourceScript->GetPropList() && SourceScript->GetPropList()->GetDef())
1764  GetPropList()->GetDef()->IncludeDefinition(SourceScript->GetPropList()->GetDef());
1765  }
1766 
1767  // generate bytecode
1768  for (auto &s : SourceScripts)
1769  C4AulCompiler::Compile(this, s, s->ast.get());
1770 
1771  // save line count
1773 
1774  // finished
1775  State = ASS_PARSED;
1776 
1777  return true;
1778 }
1779 
1780 void C4ScriptHost::EnableWarning(const char *pos, C4AulWarningId warning, bool enable)
1781 {
1782  auto entry = enabledWarnings.emplace(pos, decltype(enabledWarnings)::mapped_type{});
1783  if (entry.second)
1784  {
1785  // If there was no earlier entry for this position, copy the previous
1786  // warning state
1787  assert(entry.first != enabledWarnings.begin());
1788  auto previous = entry.first;
1789  --previous;
1790  entry.first->second = previous->second;
1791  }
1792  entry.first->second.set(static_cast<size_t>(warning), enable);
1793 }
1794 
1795 bool C4ScriptHost::IsWarningEnabled(const char *pos, C4AulWarningId warning) const
1796 {
1797  assert(!enabledWarnings.empty());
1798  if (enabledWarnings.empty())
1799  return false;
1800 
1801  // find nearest set of warnings at or before the current position
1802  auto entry = enabledWarnings.upper_bound(pos);
1803  assert(entry != enabledWarnings.begin());
1804  if (entry != enabledWarnings.begin())
1805  {
1806  --entry;
1807  }
1808 
1809  return entry->second.test(static_cast<size_t>(warning));
1810 }
1811 
1812 void C4AulParse::PushParsePos()
1813 {
1814  parse_pos_stack.push(TokenSPos);
1815 }
1816 
1817 void C4AulParse::PopParsePos()
1818 {
1819  assert(!parse_pos_stack.empty());
1820  SPos = parse_pos_stack.top();
1821  DiscardParsePos();
1822  Shift();
1823 }
1824 
1825 void C4AulParse::DiscardParsePos()
1826 {
1827  assert(!parse_pos_stack.empty());
1828  parse_pos_stack.pop();
1829 }
#define C4AUL_Const
Definition: C4AulParse.cpp:43
void ParseDirectExecStatement(C4AulScriptEngine *Engine, C4AulScriptContext *context=nullptr)
Definition: C4AulParse.cpp:847
C4String P[P_LAST]
C4Value Value
Definition: C4PropList.h:55
#define C4AUL_For
Definition: C4AulParse.cpp:49
#define C4AUL_TypeBool
Definition: C4AulParse.cpp:63
bool IncludesResolved
Definition: C4ScriptHost.h:88
#define C4AUL_Public
Definition: C4AulParse.cpp:41
C4V_Type
Definition: C4Value.h:23
const char * GetScript() const
Definition: C4ScriptHost.h:52
void EnableWarning(const char *pos, C4AulWarningId warning, bool enable=true)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
const C4ScriptOpDef C4ScriptOpMap[]
Definition: C4AulParse.cpp:274
#define C4AUL_Continue
Definition: C4AulParse.cpp:55
C4PropListStatic * GetPropList()
Definition: C4Aul.h:151
C4AulWarningId
Definition: C4Aul.h:29
void IncRef()
Definition: C4StringTable.h:27
void ParseDirectExecFunc(C4AulScriptEngine *Engine, C4AulScriptContext *context=nullptr)
Definition: C4AulParse.cpp:838
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
int SGetLine(const char *szText, const char *cpPosition)
Definition: Standard.cpp:473
std::list< StdCopyStrBuf > Appends
Definition: C4ScriptHost.h:80
#define DEBUG_BYTECODE_DUMP
Definition: C4AulParse.cpp:26
bool NoSecondStatement
Definition: C4AulParse.h:37
#define C4AUL_Func
Definition: C4AulParse.cpp:37
static void Preparse(C4ScriptHost *out, C4ScriptHost *source, const ::aul::ast::Script *s)
C4String * RegString(StdStrBuf String)
#define C4AUL_Return
Definition: C4AulParse.cpp:51
#define C4AUL_Private
Definition: C4AulParse.cpp:39
StdStrBuf GetDataString(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
Definition: C4Value.cpp:131
bool append_unnamed_pars
Definition: C4AulAST.h:255
C4AulBCCType
void SetProperty(C4PropertyName k, const C4Value &to)
Definition: C4PropList.h:120
T const * Next(T const *p) const
std::unique_ptr<::aul::ast::FunctionDecl > Parse_DirectExec(const char *code, bool whole_function)
Definition: C4AulParse.cpp:856
bool IsWhiteSpace(char cChar)
Definition: Standard.h:72
std::string callee
Definition: C4AulAST.h:258
C4ScriptHost * pOrgScript
#define C4AUL_VarNamed
Definition: C4AulParse.cpp:60
virtual class C4PropListStatic * IsStatic()
Definition: C4PropList.h:85
#define C4AUL_Append
Definition: C4AulParse.cpp:31
int32_t StrToI32(const char *str, int base, const char **scan_end)
Definition: Standard.cpp:120
C4AulScriptEngine * Engine
Definition: C4ScriptHost.h:76
void Clear()
virtual C4PropListStatic * GetPropList()
Definition: C4ScriptHost.h:50
#define a
size_t SLen(const char *sptr)
Definition: Standard.h:74
std::vector< ExprPtr > args
Definition: C4AulAST.h:257
#define C4AUL_If
Definition: C4AulParse.cpp:45
void IncludeDefinition(C4Def *pIncludeDef)
Definition: C4Def.cpp:631
std::vector< Parameter > params
Definition: C4AulAST.h:377
#define C4AUL_TypeInt
Definition: C4AulParse.cpp:62
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
#define C4AUL_Global
Definition: C4AulParse.cpp:42
const char * GetName() const
Definition: C4AulFunc.h:56
int SLineGetCharacters(const char *szText, const char *cpPosition)
Definition: Standard.cpp:485
C4PropList * _getPropList() const
Definition: C4Value.h:129
C4AulErrorHandler * GetErrorHandler() const
Definition: C4Aul.h:173
std::unique_ptr<::aul::ast::FunctionDecl > Parse_ToplevelFunctionDecl()
Definition: C4AulParse.cpp:963
C4V_Type GetType() const
Definition: C4Value.h:161
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:190
#define C4AUL_Do
Definition: C4AulParse.cpp:47
#define C4AUL_In
Definition: C4AulParse.cpp:50
void AppendChar(char cChar)
Definition: StdBuf.h:588
std::unique_ptr<::aul::ast::ArrayLit > Parse_Array()
#define C4AUL_Warning
Definition: C4AulParse.cpp:32
#define C4AUL_TypeEffect
Definition: C4AulParse.cpp:66
C4AulFunc * getFunction() const
Definition: C4Value.h:119
void CopyPropList(C4Set< C4Property > &from, C4PropListStatic *to)
std::unique_ptr< Block > body
Definition: C4AulAST.h:379
#define C4AUL_MAX_Par
Definition: C4AulFunc.h:26
std::unique_ptr< Expr > ExprPtr
Definition: C4AulAST.h:147
#define C4AUL_this
Definition: C4AulParse.cpp:56
const char * GetCStr() const
Definition: C4StringTable.h:49
virtual void AddEngineFunctions()
Definition: C4ScriptHost.h:82
#define C4AUL_Break
Definition: C4AulParse.cpp:54
std::unique_ptr<::aul::ast::ForLoop > Parse_For()
const char * what() const noexcept override
Definition: C4Aul.cpp:59
std::unique_ptr<::aul::ast::VarDecl > Parse_Var()
std::unique_ptr<::aul::ast::WhileLoop > Parse_While()
bool IsWarningEnabled(const char *pos, C4AulWarningId warning) const
C4StringTable Strings
Definition: C4Globals.cpp:42
bool has_unnamed_params
Definition: C4AulAST.h:378
std::unique_ptr<::aul::ast::Block > Parse_Block()
C4Value C4VPropList(C4PropList *p)
Definition: C4Value.h:245
friend class C4AulScriptFunc
Definition: C4ScriptHost.h:110
std::unique_ptr<::aul::ast::If > Parse_If()
C4AulParse(class C4ScriptHost *host)
Definition: C4AulParse.cpp:104
#define C4AUL_TypeC4Object
Definition: C4AulParse.cpp:67
#define C4AUL_New
Definition: C4AulParse.cpp:76
std::list< StdCopyStrBuf > Includes
Definition: C4ScriptHost.h:79
#define C4AUL_MAX_Identifier
Definition: C4Aul.h:26
#define C4AUL_Include
Definition: C4AulParse.cpp:30
#define C4AUL_GlobalNamed
Definition: C4AulParse.cpp:58
friend class C4AulParseError
Definition: C4AulParse.h:102
#define C4AUL_TypeC4ID
Definition: C4AulParse.cpp:64
#define C4AUL_False
Definition: C4AulParse.cpp:74
static void Compile(C4AulScriptFunc *out, const ::aul::ast::Function *f)
virtual bool Parse()
T const * First() const
StdStrBuf FormatStringV(const char *szFmt, va_list args)
Definition: StdBuf.cpp:276
C4Value C4VArray(C4ValueArray *pArray)
Definition: C4Value.h:249
const char * C4AulWarningMessages[]
Definition: C4Aul.cpp:28
static C4PropListStatic * NewStatic(C4PropList *prototype, const C4PropListStatic *parent, C4String *key)
Definition: C4PropList.cpp:70
unsigned short Priority
Definition: C4AulParse.h:32
const char * getData() const
Definition: StdBuf.h:442
virtual void OnError(const char *msg)=0
std::unique_ptr<::aul::ast::DoLoop > Parse_DoWhile()
std::unique_ptr<::aul::ast::RangeLoop > Parse_ForEach()
#define C4AUL_Else
Definition: C4AulParse.cpp:46
C4Set< C4Property > LocalValues
Definition: C4ScriptHost.h:92
void Warn(const char *pMsg,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: C4AulParse.cpp:123
C4String * Key
Definition: C4PropList.h:54
std::unique_ptr<::aul::ast::Expr > Parse_Expression(int iParentPrio=-1)
virtual void OnWarning(const char *msg)=0
#define C4AUL_TypePropList
Definition: C4AulParse.cpp:68
const char * getPtr(size_t i) const
Definition: StdBuf.h:448
bool Preparse()
Definition: C4AulParse.cpp:692
virtual C4Def const * GetDef() const
Definition: C4PropList.cpp:685
#define C4AUL_TypeString
Definition: C4AulParse.cpp:69
void SetOverloaded(C4AulFunc *)
#define C4Aul_Warning_enable
Definition: C4AulParse.cpp:34
#define C4AUL_TypeFunction
Definition: C4AulParse.cpp:71
#define C4AUL_While
Definition: C4AulParse.cpp:48
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:198
void FormatV(const char *szFmt, va_list args)
Definition: StdBuf.cpp:182
#define C4AUL_LocalNamed
Definition: C4AulParse.cpp:59
C4AulFunc * GetFunc(C4PropertyName k) const
Definition: C4PropList.h:105
const C4PropListStatic * GetParent() const
Definition: C4PropList.h:270
C4String * FindString(const char *strString) const
C4Value C4VFunction(C4AulFunc *pFn)
Definition: C4Value.h:250
virtual void SetPropertyByS(C4String *k, const C4Value &to)
Definition: C4PropList.cpp:971
std::list< C4ScriptHost * > SourceScripts
Definition: C4ScriptHost.h:56
std::unique_ptr<::aul::ast::Stmt > Parse_Statement()
C4RefCntPointer< C4String > Name
Definition: C4AulFunc.h:60
C4AulErrorHandler * ErrorHandler
Definition: C4Aul.h:128
void DecRef()
Definition: C4StringTable.h:28
#define C4AUL_Par
Definition: C4AulParse.cpp:53
const char * C4AulWarningIDs[]
Definition: C4Aul.cpp:34
const char * Identifier
Definition: C4AulParse.h:33
#define C4AUL_Nil
Definition: C4AulParse.cpp:75
void Shift()
Definition: C4AulParse.cpp:759
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const
Definition: C4PropList.cpp:757
#define C4AUL_Protected
Definition: C4AulParse.cpp:40
virtual C4AulScriptFunc * SFunc()
Definition: C4AulFunc.h:65
std::unique_ptr<::aul::ast::ProplistLit > Parse_PropList()
#define C4AUL_True
Definition: C4AulParse.cpp:73
C4AulTokenType
Definition: C4AulParse.cpp:79
#define C4AUL_TypeArray
Definition: C4AulParse.cpp:70
size_t getLength() const
Definition: StdBuf.h:445
StdCopyStrBuf ScriptName
Definition: C4ScriptHost.h:57
#define s
C4AulScriptState State
Definition: C4ScriptHost.h:93
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
StdStrBuf Script
Definition: C4ScriptHost.h:90
std::unique_ptr<::aul::ast::Script > Parse_Script(C4ScriptHost *)
Definition: C4AulParse.cpp:877
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
#define C4Aul_Warning_disable
Definition: C4AulParse.cpp:35
#define C4AUL_TypeDef
Definition: C4AulParse.cpp:65