GDevelop Core
Core library for developing platforms and tools compatible with GDevelop.
ExpressionParser2.h
1 /*
2  * GDevelop Core
3  * Copyright 2008-present Florian Rival ([email protected]). All rights
4  * reserved. This project is released under the MIT License.
5  */
6 #ifndef GDCORE_EXPRESSIONPARSER2_H
7 #define GDCORE_EXPRESSIONPARSER2_H
8 
9 #include <memory>
10 #include <utility>
11 #include <vector>
12 
13 #include "ExpressionParser2Node.h"
14 #include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
15 #include "GDCore/Extensions/Metadata/MetadataProvider.h"
16 #include "GDCore/Extensions/Metadata/ObjectMetadata.h"
17 #include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
18 #include "GDCore/String.h"
20 #include "GDCore/Tools/MakeUnique.h"
21 #include "GrammarTerminals.h"
22 namespace gd {
23 class Expression;
24 class ObjectsContainer;
25 class Platform;
26 class ParameterMetadata;
27 class ExpressionMetadata;
28 } // namespace gd
29 
30 namespace gd {
31 
32 using namespace gd::GrammarTerminals;
33 
44 class GD_CORE_API ExpressionParser2 {
45  public:
47  virtual ~ExpressionParser2(){};
48 
56  std::unique_ptr<ExpressionNode> ParseExpression(
57  const gd::String &expression) {
58  // Parse over a UTF-32 (fixed-width) copy of the expression: gd::String is
59  // UTF-8, so indexing it by character position (operator[]) and computing
60  // its size() are both O(position)/O(length). Doing that for every
61  // character would make parsing O(N^2) in the expression length (which is
62  // catastrophic for very large expressions). std::u32string gives O(1)
63  // random access and size, while keeping the same character indices (and so
64  // the same node locations) as gd::String.
65  expressionUtf32 = expression.ToUTF32();
66 
67  currentPosition = 0;
68  return Start();
69  }
70 
80  static size_t WrittenParametersFirstIndex(const gd::String &objectName,
81  const gd::String &behaviorName) {
82  // By convention, object is always the first parameter, and behavior the
83  // second one.
84  return !behaviorName.empty() ? 2 : (!objectName.empty() ? 1 : 0);
85  }
86 
87  private:
92  std::unique_ptr<ExpressionNode> Start() {
93  size_t expressionStartPosition = GetCurrentPosition();
94  auto expression = Expression();
95 
96  // Check for extra characters at the end of the expression
97  if (!IsEndReached()) {
98  auto op = gd::make_unique<OperatorNode>(' ');
99  op->leftHandSide = std::move(expression);
100  op->rightHandSide = ReadUntilEnd();
101  op->rightHandSide->parent = op.get();
102 
103  op->rightHandSide->diagnostic = RaiseSyntaxError(
104  _("The expression has extra character at the end that should be "
105  "removed (or completed if your expression is not finished)."));
106 
107  op->location = ExpressionParserLocation(expressionStartPosition,
108  GetCurrentPosition());
109  return std::move(op);
110  }
111 
112  return expression;
113  }
114 
115  std::unique_ptr<ExpressionNode> Expression() {
116  SkipAllWhitespaces();
117 
118  size_t expressionStartPosition = GetCurrentPosition();
119  std::unique_ptr<ExpressionNode> leftHandSide = Term();
120 
121  SkipAllWhitespaces();
122 
123  if (IsEndReached()) return leftHandSide;
124  if (CheckIfChar(IsExpressionEndingChar)) return leftHandSide;
125  if (CheckIfChar(IsExpressionOperator)) {
126  auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
127  op->leftHandSide = std::move(leftHandSide);
128  op->leftHandSide->parent = op.get();
129  op->diagnostic = ValidateOperator(GetCurrentChar());
130  SkipChar();
131  op->rightHandSide = Expression();
132  op->rightHandSide->parent = op.get();
133 
134  op->location = ExpressionParserLocation(expressionStartPosition,
135  GetCurrentPosition());
136  return std::move(op);
137  }
138 
139  leftHandSide->diagnostic = RaiseSyntaxError(
140  "More than one term was found. Verify that your expression is "
141  "properly written.");
142 
143  auto op = gd::make_unique<OperatorNode>(' ');
144  op->leftHandSide = std::move(leftHandSide);
145  op->leftHandSide->parent = op.get();
146  op->rightHandSide = Expression();
147  op->rightHandSide->parent = op.get();
148  op->location =
149  ExpressionParserLocation(expressionStartPosition, GetCurrentPosition());
150  return std::move(op);
151  }
152 
153  std::unique_ptr<ExpressionNode> Term() {
154  SkipAllWhitespaces();
155 
156  size_t expressionStartPosition = GetCurrentPosition();
157  std::unique_ptr<ExpressionNode> factor = Factor();
158 
159  SkipAllWhitespaces();
160 
161  // This while loop is used instead of a recursion (like in Expression)
162  // to guarantee the proper operator precedence. (Expression could also
163  // be reworked to use a while loop).
164  while (CheckIfChar(IsTermOperator)) {
165  auto op = gd::make_unique<OperatorNode>(GetCurrentChar());
166  op->leftHandSide = std::move(factor);
167  op->leftHandSide->parent = op.get();
168  op->diagnostic = ValidateOperator(GetCurrentChar());
169  SkipChar();
170  op->rightHandSide = Factor();
171  op->rightHandSide->parent = op.get();
172  op->location = ExpressionParserLocation(expressionStartPosition,
173  GetCurrentPosition());
174  SkipAllWhitespaces();
175 
176  factor = std::move(op);
177  }
178 
179  return factor;
180  };
181 
182  std::unique_ptr<ExpressionNode> Factor() {
183  SkipAllWhitespaces();
184  size_t expressionStartPosition = GetCurrentPosition();
185 
186  if (CheckIfChar(IsQuote)) {
187  std::unique_ptr<ExpressionNode> factor = ReadText();
188  return factor;
189  } else if (CheckIfChar(IsUnaryOperator)) {
190  auto unaryOperatorCharacter = GetCurrentChar();
191 
192  bool isNumberSign = CheckIfChar(IsNumberSign);
193  SkipChar();
194  if (isNumberSign && CheckIfChar(IsNumberFirstChar)) {
195  std::unique_ptr<ExpressionNode> numberNode =
196  ReadNumber(expressionStartPosition);
197  return numberNode;
198  }
199  auto operatorOperand = Factor();
200 
201  auto unaryOperator = gd::make_unique<UnaryOperatorNode>(
202  unaryOperatorCharacter);
203  unaryOperator->diagnostic = ValidateUnaryOperator(
204  unaryOperatorCharacter, expressionStartPosition);
205  unaryOperator->factor = std::move(operatorOperand);
206  unaryOperator->factor->parent = unaryOperator.get();
207  unaryOperator->location = ExpressionParserLocation(
208  expressionStartPosition, GetCurrentPosition());
209 
210  return std::move(unaryOperator);
211  } else if (CheckIfChar(IsNumberFirstChar)) {
212  std::unique_ptr<ExpressionNode> factor = ReadNumber();
213  return factor;
214  } else if (CheckIfChar(IsOpeningParenthesis)) {
215  size_t expressionStartPosition = GetCurrentPosition();
216  SkipChar();
217 
218  // The expression inside the parentheses excluding them.
219  auto expression = Expression();
220 
221  // The expression and its parentheses.
222  auto factor = gd::make_unique<SubExpressionNode>(std::move(expression));
223 
224  if (!CheckIfChar(IsClosingParenthesis)) {
225  factor->diagnostic =
226  RaiseSyntaxError(_("Missing a closing parenthesis. Add a closing "
227  "parenthesis for each opening parenthesis."));
228  }
229  SkipIfChar(IsClosingParenthesis);
230  factor->location = ExpressionParserLocation(expressionStartPosition,
231  GetCurrentPosition());
232 
233  return std::move(factor);
234  } else if (CheckIfChar(IsAllowedInIdentifier)) {
235  return Identifier();
236  }
237 
238  std::unique_ptr<ExpressionNode> factor = ReadUntilWhitespace();
239  return factor;
240  }
241 
242  std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
243  Identifier() {
244  auto identifierAndLocation = ReadIdentifierName();
245  gd::String name = identifierAndLocation.name;
246  auto nameLocation = identifierAndLocation.location;
247 
248  SkipAllWhitespaces();
249 
250  // We consider a namespace separator to be allowed here and be part of the
251  // function name (or object name, but object names are not allowed to
252  // contain a ":"). This is because functions from extensions have their
253  // extension name prefix, and separated by the namespace separator. This
254  // could maybe be refactored to create different nodes in the future.
255  if (IsNamespaceSeparator()) {
256  SkipNamespaceSeparator();
257  SkipAllWhitespaces();
258 
259  auto postNamespaceIdentifierAndLocation = ReadIdentifierName();
260  name += NAMESPACE_SEPARATOR;
261  name += postNamespaceIdentifierAndLocation.name;
262  ExpressionParserLocation completeNameLocation(
263  nameLocation.GetStartPosition(),
264  postNamespaceIdentifierAndLocation.location.GetEndPosition());
265  nameLocation = completeNameLocation;
266  }
267 
268  if (CheckIfChar(IsOpeningParenthesis)) {
269  ExpressionParserLocation openingParenthesisLocation = SkipChar();
270  return FreeFunction(name, nameLocation, openingParenthesisLocation);
271  } else if (CheckIfChar(IsDot)) {
272  ExpressionParserLocation dotLocation = SkipChar();
273  SkipAllWhitespaces();
274  return ObjectFunctionOrBehaviorFunctionOrVariable(
275  name, nameLocation, dotLocation);
276  } else if (CheckIfChar(IsOpeningSquareBracket)) {
277  return Variable(name, nameLocation);
278  } else {
279  auto identifier = gd::make_unique<IdentifierNode>(name);
280  identifier->location = ExpressionParserLocation(
281  nameLocation.GetStartPosition(), GetCurrentPosition());
282  identifier->identifierNameLocation = identifier->location;
283  return std::move(identifier);
284  }
285  }
286 
287  std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
288  auto variable = gd::make_unique<VariableNode>(name);
289 
290  if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
291  variable->child = VariableAccessorOrVariableBracketAccessor();
292  variable->child->parent = variable.get();
293  }
294 
295  variable->location = ExpressionParserLocation(
296  nameLocation.GetStartPosition(), GetCurrentPosition());
297  variable->nameLocation = nameLocation;
298  return std::move(variable);
299  }
300 
301  std::unique_ptr<VariableAccessorOrVariableBracketAccessorNode>
302  VariableAccessorOrVariableBracketAccessor() {
303  size_t childStartPosition = GetCurrentPosition();
304 
305  SkipAllWhitespaces();
306  if (CheckIfChar(IsOpeningSquareBracket)) {
307  SkipChar();
308 
309  SkipAllWhitespaces();
310  bool isEmptyBrackets = CheckIfChar(IsClosingSquareBracket);
311 
312  auto child = gd::make_unique<VariableBracketAccessorNode>(Expression());
313  child->expression->parent = child.get();
314 
315  if (!CheckIfChar(IsClosingSquareBracket) && !isEmptyBrackets) {
316  child->diagnostic =
317  RaiseSyntaxError(_("Missing a closing bracket. Add a closing "
318  "bracket for each opening bracket."));
319  }
320  SkipIfChar(IsClosingSquareBracket);
321 
322  SkipAllWhitespaces();
323  if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
324  child->child = VariableAccessorOrVariableBracketAccessor();
325  child->child->parent = child.get();
326  }
327  child->location =
328  ExpressionParserLocation(childStartPosition, GetCurrentPosition());
329 
330  return std::move(child);
331  } else if (CheckIfChar(IsDot)) {
332  auto dotLocation = SkipChar();
333  SkipAllWhitespaces();
334 
335  auto identifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
336  auto child =
337  gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
338  if (identifierAndLocation.name.empty()) {
339  child->diagnostic = RaiseSyntaxError(_("A name should be entered after the dot."));
340  }
341 
342  SkipAllWhitespaces();
343  if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
344  child->child = VariableAccessorOrVariableBracketAccessor();
345  child->child->parent = child.get();
346  }
347  child->nameLocation = identifierAndLocation.location;
348  child->dotLocation = dotLocation;
349  child->location =
350  ExpressionParserLocation(childStartPosition, GetCurrentPosition());
351 
352  return std::move(child);
353  }
354 
355  // Should never happen, unless a node called this function without checking if the current character
356  // was a dot or an opening bracket - this means there is an error in the grammar.
357  auto unrecognisedNode = gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>();
358  unrecognisedNode->diagnostic = RaiseSyntaxError(_("A dot or bracket was expected here."));
359  return std::move(unrecognisedNode);
360  }
361 
362  std::unique_ptr<FunctionCallNode> FreeFunction(
363  const gd::String &functionFullName,
364  const ExpressionParserLocation &identifierLocation,
365  const ExpressionParserLocation &openingParenthesisLocation) {
366  // TODO: error if trying to use function for type != "number" && != "string"
367  // + Test for it
368 
369  auto function =
370  gd::make_unique<FunctionCallNode>(functionFullName);
371  auto parametersNode = Parameters(function.get());
372  function->parameters = std::move(parametersNode.parameters);
373  function->diagnostic = std::move(parametersNode.diagnostic);
374 
375  function->location = ExpressionParserLocation(
376  identifierLocation.GetStartPosition(), GetCurrentPosition());
377  function->functionNameLocation = identifierLocation;
378  function->openingParenthesisLocation = openingParenthesisLocation;
379  function->closingParenthesisLocation =
380  parametersNode.closingParenthesisLocation;
381 
382  return std::move(function);
383  }
384 
385  std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
386  ObjectFunctionOrBehaviorFunctionOrVariable(
387  const gd::String &parentIdentifier,
388  const ExpressionParserLocation &parentIdentifierLocation,
389  const ExpressionParserLocation &parentIdentifierDotLocation) {
390  auto childIdentifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
391  const gd::String &childIdentifierName = childIdentifierAndLocation.name;
392  const auto &childIdentifierNameLocation =
393  childIdentifierAndLocation.location;
394 
395  std::unique_ptr<gd::ExpressionParserError> emptyNameError = childIdentifierName.empty() ?
396  RaiseSyntaxError(_("A name should be entered after the dot.")) : nullptr;
397 
398  SkipAllWhitespaces();
399 
400  if (IsNamespaceSeparator()) {
401  ExpressionParserLocation namespaceSeparatorLocation =
402  SkipNamespaceSeparator();
403  SkipAllWhitespaces();
404  auto behaviorFunction = BehaviorFunction(parentIdentifier,
405  childIdentifierName,
406  parentIdentifierLocation,
407  parentIdentifierDotLocation,
408  childIdentifierNameLocation,
409  namespaceSeparatorLocation);
410 
411  if (emptyNameError) behaviorFunction->diagnostic = std::move(emptyNameError);
412  return std::move(behaviorFunction);
413  } else if (CheckIfChar(IsOpeningParenthesis)) {
414  ExpressionParserLocation openingParenthesisLocation = SkipChar();
415 
416  auto function = gd::make_unique<FunctionCallNode>(
417  parentIdentifier,
418  childIdentifierName);
419  auto parametersNode = Parameters(function.get(), parentIdentifier);
420  function->parameters = std::move(parametersNode.parameters),
421  function->diagnostic = emptyNameError ? std::move(emptyNameError) : std::move(parametersNode.diagnostic);
422 
423  function->location = ExpressionParserLocation(
424  parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
425  function->objectNameLocation = parentIdentifierLocation;
426  function->objectNameDotLocation = parentIdentifierDotLocation;
427  function->functionNameLocation = childIdentifierNameLocation;
428  function->openingParenthesisLocation = openingParenthesisLocation;
429  function->closingParenthesisLocation =
430  parametersNode.closingParenthesisLocation;
431  return std::move(function);
432  } else if (CheckIfChar(IsDot) || CheckIfChar(IsOpeningSquareBracket)) {
433  auto variable = gd::make_unique<VariableNode>(parentIdentifier);
434  variable->diagnostic = std::move(emptyNameError);
435 
436  auto child =
437  gd::make_unique<VariableAccessorNode>(childIdentifierName);
438  child->child = VariableAccessorOrVariableBracketAccessor();
439  child->child->parent = child.get();
440  child->nameLocation = childIdentifierNameLocation;
441  child->dotLocation = parentIdentifierDotLocation;
442  child->location = ExpressionParserLocation(
443  parentIdentifierDotLocation.GetStartPosition(), GetCurrentPosition());
444  variable->child = std::move(child);
445  variable->child->parent = variable.get();
446 
447  variable->location = ExpressionParserLocation(
448  parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
449  variable->nameLocation = parentIdentifierLocation;
450 
451  return std::move(variable);
452  }
453 
454  auto node = gd::make_unique<IdentifierNode>(
455  parentIdentifier, childIdentifierName);
456  node->location = ExpressionParserLocation(
457  parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
458  node->identifierNameLocation = parentIdentifierLocation;
459  node->identifierNameDotLocation = parentIdentifierDotLocation;
460  node->childIdentifierNameLocation = childIdentifierNameLocation;
461  node->diagnostic = std::move(emptyNameError);
462  return std::move(node);
463  }
464 
465  std::unique_ptr<FunctionCallOrObjectFunctionNameOrEmptyNode> BehaviorFunction(
466  const gd::String &objectName,
467  const gd::String &behaviorName,
468  const ExpressionParserLocation &objectNameLocation,
469  const ExpressionParserLocation &objectNameDotLocation,
470  const ExpressionParserLocation &behaviorNameLocation,
471  const ExpressionParserLocation &behaviorNameNamespaceSeparatorLocation) {
472  auto identifierAndLocation = ReadIdentifierName();
473  const gd::String &functionName = identifierAndLocation.name;
474  const auto &functionNameLocation = identifierAndLocation.location;
475 
476  SkipAllWhitespaces();
477 
478  if (CheckIfChar(IsOpeningParenthesis)) {
479  ExpressionParserLocation openingParenthesisLocation = SkipChar();
480 
481  auto function = gd::make_unique<FunctionCallNode>(
482  objectName,
483  behaviorName,
484  functionName);
485  auto parametersNode =
486  Parameters(function.get(), objectName, behaviorName);
487  function->parameters = std::move(parametersNode.parameters);
488  function->diagnostic = std::move(parametersNode.diagnostic);
489 
490  function->location = ExpressionParserLocation(
491  objectNameLocation.GetStartPosition(), GetCurrentPosition());
492  function->objectNameLocation = objectNameLocation;
493  function->objectNameDotLocation = objectNameDotLocation;
494  function->behaviorNameLocation = behaviorNameLocation;
495  function->behaviorNameNamespaceSeparatorLocation =
496  behaviorNameNamespaceSeparatorLocation;
497  function->openingParenthesisLocation = openingParenthesisLocation;
498  function->closingParenthesisLocation =
499  parametersNode.closingParenthesisLocation;
500  function->functionNameLocation = functionNameLocation;
501  return std::move(function);
502  } else {
503  auto node = gd::make_unique<ObjectFunctionNameNode>(
504  objectName, behaviorName, functionName);
505  node->diagnostic = RaiseSyntaxError(
506  _("An opening parenthesis was expected here to call a function."));
507 
508  node->location = ExpressionParserLocation(
509  objectNameLocation.GetStartPosition(), GetCurrentPosition());
510  node->objectNameLocation = objectNameLocation;
511  node->objectNameDotLocation = objectNameDotLocation;
512  node->objectFunctionOrBehaviorNameLocation = behaviorNameLocation;
513  node->behaviorNameNamespaceSeparatorLocation =
514  behaviorNameNamespaceSeparatorLocation;
515  node->behaviorFunctionNameLocation = functionNameLocation;
516  return std::move(node);
517  }
518  }
519 
520  // A temporary node that will be integrated into function nodes.
521  struct ParametersNode {
522  std::vector<std::unique_ptr<ExpressionNode>> parameters;
523  std::unique_ptr<gd::ExpressionParserError> diagnostic;
524  ExpressionParserLocation closingParenthesisLocation;
525  };
526 
527  ParametersNode Parameters(
528  FunctionCallNode *functionCallNode,
529  const gd::String &objectName = "",
530  const gd::String &behaviorName = "") {
531  std::vector<std::unique_ptr<ExpressionNode>> parameters;
532  gd::String lastObjectName = "";
533 
534  bool previousCharacterIsParameterSeparator = false;
535  while (!IsEndReached()) {
536  SkipAllWhitespaces();
537 
538  if (CheckIfChar(IsClosingParenthesis) && !previousCharacterIsParameterSeparator) {
539  auto closingParenthesisLocation = SkipChar();
540  return ParametersNode{
541  std::move(parameters), nullptr, closingParenthesisLocation};
542  }
543  bool isEmptyParameter = CheckIfChar(IsParameterSeparator)
544  || (CheckIfChar(IsClosingParenthesis) && previousCharacterIsParameterSeparator);
545  auto parameter = isEmptyParameter ? gd::make_unique<EmptyNode>() : Expression();
546  parameter->parent = functionCallNode;
547  parameters.push_back(std::move(parameter));
548 
549  SkipAllWhitespaces();
550  previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
551  SkipIfChar(IsParameterSeparator);
552  }
553 
554  ExpressionParserLocation invalidClosingParenthesisLocation;
555  return ParametersNode{
556  std::move(parameters),
557  RaiseSyntaxError(_("The list of parameters is not terminated. Add a "
558  "closing parenthesis to end the parameters.")),
559  invalidClosingParenthesisLocation};
560  }
562 
563  std::unique_ptr<ExpressionParserError> ValidateOperator(
564  gd::String::value_type operatorChar) {
565  if (operatorChar == '+' || operatorChar == '-' || operatorChar == '/' ||
566  operatorChar == '*') {
567  return std::unique_ptr<ExpressionParserError>(nullptr);
568  }
569  return gd::make_unique<ExpressionParserError>(
570  gd::ExpressionParserError::ErrorType::InvalidOperator,
571  _("You've used an operator that is not supported. Operator should be "
572  "either +, -, / or *."),
573  GetCurrentPosition());
574  }
575 
576  std::unique_ptr<ExpressionParserError> ValidateUnaryOperator(
577  gd::String::value_type operatorChar,
578  size_t position) {
579  if (operatorChar == '+' || operatorChar == '-') {
580  return std::unique_ptr<ExpressionParserError>(nullptr);
581  }
582 
583  return gd::make_unique<ExpressionParserError>(
584  gd::ExpressionParserError::ErrorType::InvalidOperator,
585  _("You've used an \"unary\" operator that is not supported. Operator "
586  "should be "
587  "either + or -."),
588  position);
589  }
591 
596  ExpressionParserLocation SkipChar() {
597  size_t startPosition = currentPosition;
598  return ExpressionParserLocation(startPosition, ++currentPosition);
599  }
600 
601  void SkipAllWhitespaces() {
602  while (currentPosition < expressionUtf32.size() &&
603  IsWhitespace(expressionUtf32[currentPosition])) {
604  currentPosition++;
605  }
606  }
607 
608  void SkipIfChar(
609  const std::function<bool(gd::String::value_type)> &predicate) {
610  if (CheckIfChar(predicate)) {
611  currentPosition++;
612  }
613  }
614 
615  ExpressionParserLocation SkipNamespaceSeparator() {
616  size_t startPosition = currentPosition;
617  // Namespace separator is a special kind of delimiter as it is 2 characters
618  // long
619  if (IsNamespaceSeparator()) {
620  currentPosition += NAMESPACE_SEPARATOR.size();
621  }
622 
623  return ExpressionParserLocation(startPosition, currentPosition);
624  }
625 
626  bool CheckIfChar(
627  const std::function<bool(gd::String::value_type)> &predicate) {
628  if (currentPosition >= expressionUtf32.size()) return false;
629  gd::String::value_type character = expressionUtf32[currentPosition];
630 
631  return predicate(character);
632  }
633 
634  bool IsNamespaceSeparator() {
635  // Namespace separator is a special kind of delimiter as it is 2 characters
636  // long. Computed once as the separator is a compile-time constant ("::").
637  static const std::u32string separator = NAMESPACE_SEPARATOR.ToUTF32();
638  return (currentPosition + separator.size() <= expressionUtf32.size() &&
639  expressionUtf32.compare(
640  currentPosition, separator.size(), separator) == 0);
641  }
642 
643  bool IsEndReached() { return currentPosition >= expressionUtf32.size(); }
644 
645  // A temporary node used when reading an identifier
646  struct IdentifierAndLocation {
647  gd::String name;
648  ExpressionParserLocation location;
649  };
650 
651  IdentifierAndLocation ReadIdentifierName(bool allowDeprecatedSpacesInName = true) {
652  gd::String name;
653  size_t startPosition = currentPosition;
654  while (currentPosition < expressionUtf32.size() &&
655  (CheckIfChar(IsAllowedInIdentifier)
656  // Allow whitespace in identifier name for compatibility
657  || (allowDeprecatedSpacesInName && expressionUtf32[currentPosition] == ' '))) {
658  name += expressionUtf32[currentPosition];
659  currentPosition++;
660  }
661 
662  // Trim whitespace at the end (we allow them for compatibility inside
663  // the name, but after the last character that is not whitespace, they
664  // should be ignore again).
665  if (!name.empty() && IsWhitespace(name[name.size() - 1])) {
666  size_t lastCharacterPos = name.size() - 1;
667  while (lastCharacterPos < name.size() &&
668  IsWhitespace(name[lastCharacterPos])) {
669  lastCharacterPos--;
670  }
671  if ((lastCharacterPos + 1) < name.size()) {
672  name.erase(lastCharacterPos + 1);
673  }
674  }
675 
676  IdentifierAndLocation identifierAndLocation{
677  name,
678  // The location is ignoring the trailing whitespace (only whitespace
679  // inside the identifier are allowed for compatibility).
680  ExpressionParserLocation(startPosition, startPosition + name.size())};
681  return identifierAndLocation;
682  }
683 
684  std::unique_ptr<TextNode> ReadText();
685 
686  std::unique_ptr<NumberNode> ReadNumber(size_t minusSignPosition = -1);
687 
688  std::unique_ptr<EmptyNode> ReadUntilWhitespace() {
689  size_t startPosition = GetCurrentPosition();
690  gd::String text;
691  while (currentPosition < expressionUtf32.size() &&
692  !IsWhitespace(expressionUtf32[currentPosition])) {
693  text += expressionUtf32[currentPosition];
694  currentPosition++;
695  }
696 
697  auto node = gd::make_unique<EmptyNode>(text);
698  node->location =
699  ExpressionParserLocation(startPosition, GetCurrentPosition());
700  return node;
701  }
702 
703  std::unique_ptr<EmptyNode> ReadUntilEnd() {
704  size_t startPosition = GetCurrentPosition();
705  gd::String text;
706  while (currentPosition < expressionUtf32.size()) {
707  text += expressionUtf32[currentPosition];
708  currentPosition++;
709  }
710 
711  auto node = gd::make_unique<EmptyNode>(text);
712  node->location =
713  ExpressionParserLocation(startPosition, GetCurrentPosition());
714  return node;
715  }
716 
717  size_t GetCurrentPosition() { return currentPosition; }
718 
719  gd::String::value_type GetCurrentChar() {
720  if (currentPosition < expressionUtf32.size()) {
721  return expressionUtf32[currentPosition];
722  }
723 
724  return '\n'; // Should not arise, unless GetCurrentChar was called when
725  // IsEndReached() is true (which is a logical error).
726  }
728 
733  std::unique_ptr<ExpressionParserError> RaiseSyntaxError(
734  const gd::String &message) {
735  return std::move(gd::make_unique<ExpressionParserError>(
736  gd::ExpressionParserError::ErrorType::SyntaxError, message,
737  GetCurrentPosition()));
738  }
739 
740  std::unique_ptr<ExpressionParserError> RaiseTypeError(
741  const gd::String &message, size_t beginningPosition) {
742  return std::move(gd::make_unique<ExpressionParserError>(
743  gd::ExpressionParserError::ErrorType::MismatchedType, message,
744  beginningPosition, GetCurrentPosition()));
745  }
747 
748  // The expression being parsed, stored as UTF-32 (fixed-width) so that
749  // character access (operator[]) and size() are O(1). See ParseExpression for
750  // the rationale.
751  std::u32string expressionUtf32;
752  std::size_t currentPosition;
753 
754  static gd::String NAMESPACE_SEPARATOR;
755 };
756 
757 } // namespace gd
758 
759 #endif // GDCORE_EXPRESSIONPARSER2_H
Class representing an expression used as a parameter of a gd::Instruction. This class is nothing more...
Definition: Expression.h:30
Parse an expression, returning a tree of node corresponding to the parsed expression.
Definition: ExpressionParser2.h:44
static size_t WrittenParametersFirstIndex(const gd::String &objectName, const gd::String &behaviorName)
Definition: ExpressionParser2.h:80
std::unique_ptr< ExpressionNode > ParseExpression(const gd::String &expression)
Definition: ExpressionParser2.h:56
String represents an UTF8 encoded string.
Definition: String.h:33
std::u32string ToUTF32() const
Definition: String.cpp:154
bool empty() const
Returns true if the string is empty.
Definition: String.h:157
iterator erase(iterator first, iterator last)
Erase the characters between first and last (last not included).
Definition: String.cpp:333
size_type size() const
Returns the string's length.
Definition: String.cpp:63
Definition: GrammarTerminals.h:16
bool IsAllowedInIdentifier(gd::String::value_type character)
Definition: GrammarTerminals.h:110
Definition: CommonTools.h:24
Definition: ExpressionParser2Node.h:25