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