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