1-------------------------------------------------------------------------------
2-- SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com>
3--
4-- SPDX-License-Identifier: LGPL-2.0-or-later
5-----------------------------------------------------------
6
7
8-----------------------------------------------------------
9-- Grammar for PHP 5.2
10-- Modelled after the Zend Grammar shipped with PHP5.2
11-- source, the PHP Language Reference documentation,
12-- and parts taken from KDevelop Java Grammar
13-----------------------------------------------------------
14
15-- 4 first/first conflicts:
16--  - var_expression: variable vs. varExpressionNormal
17--    no problem because of ifs that allow always just one rule
18--  - classNameReference: STRING vs. staticMember (foo vs. foo::$bar)
19--    resolved by LA()
20--  - encapsVar: STRING_VARNAME LBRACKET vs. expr (expr allows STRING_VARNAME too - but not LBRACKET)
21--    resolved by LA()
22--  - constantOrClassConst: constant vs. class constant (FOO v.s Cls::FOO)
23--    resolved by LA() (could be avoided, but the Ast is much cleaner that way)
24-- 1 first/follow conflicts:
25--  - elseifList: dangling-else conflict - should be ok
26
27-- TODO: (post 1.0.0 release)
28--  1) decrease memory consumption
29--    1.1) use quint32 instead of qint64 for end/start tokens
30--    1.2) investigate whether using a map/hash for the ducontext member of all
31--         ast nodes gives a significant memory decrease while not hampering performance
32--    1.3) investigate how unions could be used for exclusive AST node members
33--    1.4) see whether we can always use the expression lists instead of both
34--         single member pointer and list of members, esp. in expressions
35--  2) better cope with invalid code, have at least a partial AST
36--  3) investigate whether expanding the visitor lookup to a
37--     (albeit huge) switch() in KDev-PG-Qt gives a significant performance gain
38--     I have the gut feeling that the current lookup takes unnecessary much time
39--
40
41------------------------------------------------------------
42-- Forward declaration in phpast.h
43------------------------------------------------------------
44
45[:
46
47#include <QtCore/QRegularExpression>
48
49namespace KDevelop
50{
51    class DUContext;
52}
53
54:]
55
56------------------------------------------------------------
57-- Additional includes for the parser
58------------------------------------------------------------
59
60%parser_declaration_header "parser/tokenstream.h"
61%parser_declaration_header "QtCore/QString"
62%parser_declaration_header "language/duchain/problem.h"
63%parser_declaration_header "parser/phplexer.h"
64
65%parser_bits_header "parserdebug.h"
66
67------------------------------------------------------------
68-- Export macro to use the parser in a shared lib
69------------------------------------------------------------
70%export_macro "KDEVPHPPARSER_EXPORT"
71%export_macro_header "parserexport.h"
72
73
74------------------------------------------------------------
75-- Enumeration types for additional AST members,
76-- in the global "Php" namespace
77------------------------------------------------------------
78%namespace
79[:
80    class Lexer;
81
82    enum ModifierFlags {
83        ModifierPrivate      = 1,
84        ModifierPublic       = 1 << 1,
85        ModifierProtected    = 1 << 2,
86        ModifierStatic       = 1 << 3,
87        ModifierFinal        = 1 << 4,
88        ModifierAbstract     = 1 << 5
89    };
90
91    enum ClassModifier {
92        NormalClass,
93        AbstractClass,
94        FinalClass
95    };
96
97    enum ScalarTypes {
98        ScalarTypeInt,
99        ScalarTypeFloat,
100        ScalarTypeString
101    };
102
103    enum CastType {
104        CastInt,
105        CastDouble,
106        CastString,
107        CastArray,
108        CastObject,
109        CastBool,
110        CastUnset
111    };
112
113    enum OperationType {
114        OperationPlus = 1,
115        OperationMinus,
116        OperationConcat,
117        OperationMul,
118        OperationDiv,
119        OperationExp,
120        OperationMod,
121        OperationAnd,
122        OperationOr,
123        OperationXor,
124        OperationSl,
125        OperationSr,
126        OperationSpaceship,
127    };
128:]
129
130------------------------------------------------------------
131-- Ast Node class members
132------------------------------------------------------------
133%ast_extra_members
134[:
135  KDevelop::DUContext* ducontext;
136:]
137
138------------------------------------------------------------
139-- Parser class members
140------------------------------------------------------------
141
142%parserclass (public declaration)
143[:
144  /**
145   * Transform the raw input into tokens.
146   * When this method returns, the parser's token stream has been filled
147   * and any parse*() method can be called.
148   */
149  void tokenize(const QString& contents, int initialState = Lexer::HtmlState);
150
151  enum ProblemType {
152      Error,
153      Warning,
154      Info,
155      Todo
156  };
157  KDevelop::ProblemPointer reportProblem( Parser::ProblemType type, const QString& message, int tokenOffset = -1 );
158  QList<KDevelop::ProblemPointer> problems() {
159      return m_problems;
160  }
161  QString tokenText(qint64 begin, qint64 end);
162  void setDebug(bool debug);
163  void setCurrentDocument(KDevelop::IndexedString url);
164  void setTodoMarkers(const QStringList& markers);
165  void extractTodosFromComment(const QString& comment, qint64 offset);
166
167    enum InitialLexerState {
168        HtmlState = 0,
169        DefaultState = 1
170    };
171
172:]
173
174%parserclass (private declaration)
175[:
176    enum VarExpressionState {
177        Normal,
178        OnlyVariable,
179        OnlyNewObject
180    };
181    QString m_contents;
182    bool m_debug;
183    KDevelop::IndexedString m_currentDocument;
184    QList<KDevelop::ProblemPointer> m_problems;
185
186    struct ParserState {
187        VarExpressionState varExpressionState;
188        bool varExpressionIsVariable;
189    };
190    ParserState m_state;
191
192    QRegularExpression m_todoMarkers;
193:]
194
195%parserclass (constructor)
196[:
197    m_state.varExpressionState = Normal;
198    m_state.varExpressionIsVariable = false;
199:]
200
201
202%token_stream TokenStream ;;
203
204-----------------------------------------------------------
205-- List of defined tokens
206-----------------------------------------------------------
207
208-- keywords:
209%token ABSTRACT ("abstract"), BREAK ("break"), CASE ("case"), CATCH ("catch"),
210       CLASS ("class"), CONST ("const"), CONTINUE ("continue"),
211       DEFAULT ("default"), DO ("do"), ELSE ("else"), EXTENDS ("extends"),
212       FINAL ("final"), FOR ("for"), IF ("if"),
213       IMPLEMENTS ("implements"), INSTANCEOF ("instanceof"), INTERFACE ("interface"),
214       NEW ("new"), PRIVATE ("private"), PROTECTED ("protected"), PUBLIC ("public"),
215       RETURN ("return"), STATIC ("static"), SWITCH ("switch"), THROW ("throw"),
216       TRY ("try"), WHILE ("while"), ECHO ("echo"), PRINT ("print"), FINALLY ("finally"),
217       CLONE ("clone"), EXIT ("exit"), ELSEIF ("elseif"), ENDIF ("endif"),
218       ENDWHILE ("endwhile"), ENDFOR ("endfor"), FOREACH ("foreach"), ENDFOREACH ("endforeach"),
219       DECLARE ("declare"), ENDDECLARE ("enddeclare"), AS ("as"), ENDSWITCH ("endswitch"),
220       FUNCTION ("function"), USE ("use"), GLOBAL ("global"), VAR ("var "), UNSET ("unset"),
221       ISSET ("isset"), EMPTY ("empty"), HALT_COMPILER ("halt compiler"),
222       DOUBLE_ARROW ("=>"), LIST ("list"), ARRAY ("array"), CLASS_C ("__CLASS__"),
223       METHOD_C ("__METHOD__"), FUNC_C ("__FUNCTION__"), LINE ("__LINE__"),
224       FILE ("__FILE__"), COMMENT ("comment"), DOC_COMMENT ("doc comment"),  PAAMAYIM_NEKUDOTAYIM ("::"),
225       INCLUDE ("include"), INCLUDE_ONCE ("include_once"), EVAL ("eval"), REQUIRE ("require"),
226       REQUIRE_ONCE ("require_once"), NAMESPACE ("namespace"), NAMESPACE_C("__NAMESPACE__"), USE("use"),
227       GOTO ("goto"), TRAIT ("trait"), INSTEADOF ("insteadof"), CALLABLE ("callable"),
228       VOID ("void"), DIR ("__DIR__"), TRAIT_C ("__TRAIT__"), YIELD ("yield"), YIELD_FROM("yield from") ;;
229
230-- casts:
231%token INT_CAST ("int cast"), DOUBLE_CAST ("double cast"), STRING_CAST ("string cast"),
232       ARRAY_CAST ("array cast"), OBJECT_CAST ("object cast"), BOOL_CAST ("bool cast"),
233       UNSET_CAST ("unset cast") ;;
234
235-- seperators:
236%token SEMICOLON (";"), DOUBLE_QUOTE ("\""), LBRACKET ("["),
237       RBRACKET ("]"),
238       LPAREN ("("), RPAREN (")"), LBRACE ("{"), RBRACE ("}"),
239       COMMA (","), AT ("@"),
240       CURLY_OPEN ("curly open"), -- { in "{$foo}"; not the same as LBRACE
241       DOLLAR_OPEN_CURLY_BRACES ("${"),
242       START_HEREDOC ("start heredoc"), END_HEREDOC ("end heredoc"),
243       BACKTICK ("`"), BACKSLASH ("\\"),
244       START_NOWDOC("start nowdoc"), END_NOWDOC("end nowdoc") ;;
245
246-- operators:
247%token IS_EQUAL ("=="), IS_NOT_EQUAL ("!="), IS_IDENTICAL ("==="),
248       IS_NOT_IDENTICAL ("!=="), IS_SMALLER ("<"), IS_GREATER (">"),
249       IS_SMALLER_OR_EQUAL ("<="), IS_GREATER_OR_EQUAL (">="),
250       BOOLEAN_OR ("||"), BOOLEAN_AND ("&&"), ASSIGN ("="), EXP_ASSIGN("**="),
251       PLUS_ASSIGN ("+="), MINUS_ASSIGN ("-="), MUL_ASSIGN ("*="), DIV_ASSIGN ("/="),
252       CONCAT_ASSIGN (".="), MOD_ASSIGN ("%="), AND_ASSIGN ("&="), OR_ASSIGN ("|="),
253       XOR_ASSIGN ("^="), SL_ASSIGN ("<<="), SR_ASSIGN (">>="), OBJECT_OPERATOR ("->"),
254       PLUS ("+"), MINUS("-"), CONCAT("."),
255       INC ("++"), DEC ("--"), BANG ("!"), QUESTION ("?"), COLON (":"),
256       BIT_AND ("&"), BIT_OR("|"), BIT_XOR ("^"),
257       SL ("<<"), SR (">>"), MUL("*"), DIV("/"), MOD ("%"),
258       TILDE ("~"), DOLLAR ("$"), EXP ("**"), ELLIPSIS ("..."),
259       NULL_COALESCE ("??"), SPACESHIP ("<=>"),
260       LOGICAL_OR ("logical or"), LOGICAL_AND ("logical and"), LOGICAL_XOR ("logical xor") ;;
261
262-- literals and identifiers:
263%token INLINE_HTML ("inline html"), WHITESPACE ("whitespace"),
264       CONSTANT_ENCAPSED_STRING ("constant encapsed string"),
265       VARIABLE ("variable"), ENCAPSED_AND_WHITESPACE ("encapsed and whitespace"),
266       DNUMBER ("double number"), LNUMBER ("long number"),
267       NUM_STRING ("num string"), STRING ("string"),
268       STRING_VARNAME ("string varname") ;; -- when in "${varname}"
269
270-- open/close tags
271%token OPEN_TAG ("<?"), CLOSE_TAG ("?>"), OPEN_TAG_WITH_ECHO ("<?=");;
272
273-- token that makes the parser fail in any case:
274%token INVALID ("invalid token") ;;
275
276
277
278-- The actual grammar starts here.
279
280#statements=outerTopStatement*
281-> start ;;
282
283  namespaceDeclaration=namespaceDeclarationStatement
284  | statement=topStatement
285-> outerTopStatement ;;
286
287-- first/first conflict for FUNCTION
288   (?[: (LA(1).kind == Token_FUNCTION && ((LA(2).kind == Token_BIT_AND && LA(3).kind == Token_LPAREN)
289            || LA(2).kind == Token_LPAREN))
290        || LA(1).kind != Token_FUNCTION :]
291    statement=statement )
292  | functionDeclaration=functionDeclarationStatement
293  | classDeclaration=classDeclarationStatement
294  | traitDeclaration=traitDeclarationStatement
295  | interfaceDeclaration=interfaceDeclarationStatement
296  | HALT_COMPILER LPAREN RPAREN SEMICOLON -- Lexer stops allready
297-> topStatement ;;
298
299[: bool reported = false; while ( true ) { :]
300    try/recover(#statements=topStatement)*
301[: if (yytoken != Token_RBRACE && yytoken != Token_EOF && yytoken != Token_CLOSE_TAG
302       && yytoken != Token_ELSEIF && yytoken != Token_ELSE
303       && yytoken != Token_ENDIF && yytoken != Token_ENDFOREACH && yytoken != Token_ENDFOR
304       && yytoken != Token_ENDWHILE && yytoken != Token_ENDSWITCH && yytoken != Token_ENDDECLARE
305       && yytoken != Token_CASE && yytoken != Token_DEFAULT) {
306        if (!reported) {
307            qint64 index = tokenStream->index() - 1;
308            Token &token = tokenStream->at(index);
309            QString tokenValue = token.kind != 0 ? tokenText(token.begin, token.end) : QStringLiteral("EOF");
310            reportProblem(Error, QStringLiteral("Unexpected token \"%1\".").arg(tokenValue));
311            reported = true;
312        }
313        yylex();
314   } else {
315        break;
316   }
317} :]
318-> innerStatementList ;;
319
320--Operator Precedence, from PHP Manual
321--left    or
322--left    xor
323--left    and
324--right   print
325--right   = += -= *= /= .= %= &= |= ^= <<= >>=    assignment
326--left    ? : ternary
327--right   ??  comparison
328--left    ||  logical
329--left    &&  logical
330--left    |   bitwise
331--left    ^   bitwise
332--left    &   bitwise and references
333--non-associative == != === !== <=>   comparison
334--non-associative < <= > >=   comparison
335--left    << >>   bitwise
336--left    + - .   arithmetic and string
337--left    * / %   arithmetic
338--non-associative ! ~ - (int) (float) (string) (array) (object) @ types
339--non-associative ++ --   increment/decrement
340--left    [   array()
341--non-associative new new
342
343  expression=logicalOrExpression
344-> expr ;;
345
346   #expression=logicalXorExpression @ LOGICAL_OR
347-> logicalOrExpression ;;
348
349   #expression=logicalAndExpression @ LOGICAL_XOR
350-> logicalXorExpression ;;
351
352   #expression=printExpression @ LOGICAL_AND
353-> logicalAndExpression ;;
354
355   (print=PRINT | 0) expression=assignmentExpression
356-> printExpression ;;
357
358-- leftside must me a variable, we check afterwards if it was a variable and
359-- if not we report an error
3600 --needed for line below
361[: m_state.varExpressionIsVariable = false; :] --reset flag
362expression=conditionalExpression
363(
364  assignmentExpressionEqual=assignmentExpressionEqual | (
365     (
366        PLUS_ASSIGN   [: (*yynode)->operation = OperationPlus; :]
367      | MINUS_ASSIGN  [: (*yynode)->operation = OperationMinus; :]
368      | MUL_ASSIGN    [: (*yynode)->operation = OperationMul; :]
369      | EXP_ASSIGN    [: (*yynode)->operation = OperationExp; :]
370      | DIV_ASSIGN    [: (*yynode)->operation = OperationDiv; :]
371      | CONCAT_ASSIGN [: (*yynode)->operation = OperationConcat; :]
372      | MOD_ASSIGN    [: (*yynode)->operation = OperationMod; :]
373      | AND_ASSIGN    [: (*yynode)->operation = OperationAnd; :]
374      | OR_ASSIGN     [: (*yynode)->operation = OperationOr; :]
375      | XOR_ASSIGN    [: (*yynode)->operation = OperationXor; :]
376      | SL_ASSIGN     [: (*yynode)->operation = OperationSl; :]
377      | SR_ASSIGN     [: (*yynode)->operation = OperationSr; :]
378     )
379     assignmentExpressionCheckIfVariable
380     assignmentExpression=assignmentExpression)
381   | 0)
382-> assignmentExpression [
383     member variable operation: OperationType;
384];;
385
386--=& is special:
387  -- $foo =& $var; is allowed but not $foo =& 'static';
388  -- $foo =& new bar(); is allowed too but deprecated and reports a warning
389  --we set a flag (varExpressionState) with that var_expression accepts only valid parts
390  --this is done in such a strage way because we need the full expression to allow
391  --things like $foo =& $bar || e();
392ASSIGN
393    assignmentExpressionCheckIfVariable --as in assignmentExpression
394    (BIT_AND [: if (yytoken == Token_NEW) {
395                reportProblem(Warning, QStringLiteral("=& new foo() is deprecated"), -2);
396                m_state.varExpressionState = OnlyNewObject;
397              } else {
398                m_state.varExpressionState = OnlyVariable;
399              }:]
400     | 0) assignmentExpression=assignmentExpression [: m_state.varExpressionState = Normal; :]
401-> assignmentExpressionEqual ;;
402
403
404-- check if var_expression was a variable, if not report an error
405-- varExpressionIsVariable is set in var_expression
4060 --to allow cpp-code
407[:
408    if (!m_state.varExpressionIsVariable) {
409        reportProblem(Error, QStringLiteral("Left side is not a variable"));
410        return false;
411    }
412:]
413-> assignmentExpressionCheckIfVariable ;;
414
415expression=nullCoalesceExpression
416   (  QUESTION (ifExpression=expr|0)
417      COLON    elseExpression=conditionalExpression
418    | 0
419   )
420-> conditionalExpression ;;
421
422   #expression=booleanOrExpression @ NULL_COALESCE
423-> nullCoalesceExpression ;;
424
425   #expression=booleanAndExpression @ BOOLEAN_OR
426-> booleanOrExpression ;;
427
428   #expression=bitOrExpression @ BOOLEAN_AND
429-> booleanAndExpression ;;
430
431   #expression=bitXorExpression @ BIT_OR
432-> bitOrExpression ;;
433
434   #expression=bitAndExpression @ BIT_XOR
435-> bitXorExpression ;;
436
437   #expression=equalityExpression @ BIT_AND
438-> bitAndExpression ;;
439
440   expression=relationalExpression
441   (additionalExpression=equalityExpressionRest | 0)
442-> equalityExpression ;;
443
444   (  IS_EQUAL | IS_NOT_EQUAL | IS_IDENTICAL | IS_NOT_IDENTICAL | SPACESHIP [: (*yynode)->operation = OperationSpaceship; :] )
445   expression=relationalExpression
446-> equalityExpressionRest [
447     member variable operation: OperationType;
448];;
449
450
451   expression=shiftExpression
452   (  additionalExpression=relationalExpressionRest
453      --instanceof as in java.g (correct??)
454    | INSTANCEOF instanceofType=classNameReference
455    | 0
456   )
457-> relationalExpression ;;
458
459   ( IS_SMALLER | IS_GREATER | IS_SMALLER_OR_EQUAL | IS_GREATER_OR_EQUAL )
460   expression=shiftExpression
461-> relationalExpressionRest ;;
462
463
464   expression=additiveExpression
465   (#additionalExpression=shiftExpressionRest)*
466-> shiftExpression ;;
467
468   ( SL | SR )
469   expression=additiveExpression
470-> shiftExpressionRest ;;
471
472
473   expression=multiplicativeExpression
474   (#additionalExpression=additiveExpressionRest)*
475-> additiveExpression ;;
476
477   (
478       PLUS   [: (*yynode)->operation = OperationPlus; :]
479     | MINUS  [: (*yynode)->operation = OperationMinus; :]
480     | CONCAT [: (*yynode)->operation = OperationConcat; :]
481   )
482   expression=multiplicativeExpression
483-> additiveExpressionRest [
484     member variable operation: OperationType;
485];;
486
487
488   expression=unaryExpression
489   (#additionalExpression=multiplicativeExpressionRest)*
490-> multiplicativeExpression ;;
491
492   (
493       MUL [: (*yynode)->operation = OperationMul; :]
494     | DIV [: (*yynode)->operation = OperationDiv; :]
495     | EXP [: (*yynode)->operation = OperationExp; :]
496     | MOD [: (*yynode)->operation = OperationMod; :]
497   )
498   expression=unaryExpression
499-> multiplicativeExpressionRest [
500     member variable operation: OperationType;
501];;
502
503 (
504    MINUS unaryExpression=unaryExpression
505  | PLUS  unaryExpression=unaryExpression
506  | BANG unaryExpression=unaryExpression
507  | TILDE unaryExpression=unaryExpression
508  | INT_CAST unaryExpression=unaryExpression    [: (*yynode)->castType = CastInt; :]
509  | DOUBLE_CAST unaryExpression=unaryExpression [: (*yynode)->castType = CastDouble; :]
510  | STRING_CAST unaryExpression=unaryExpression [: (*yynode)->castType = CastString; :]
511  | ARRAY_CAST unaryExpression=unaryExpression  [: (*yynode)->castType = CastArray; :]
512  | OBJECT_CAST unaryExpression=unaryExpression [: (*yynode)->castType = CastObject; :]
513  | BOOL_CAST unaryExpression=unaryExpression   [: (*yynode)->castType = CastBool; :]
514  | UNSET_CAST unaryExpression=unaryExpression  [: (*yynode)->castType = CastUnset; :]
515  | AT unaryExpression=unaryExpression
516  | LIST LPAREN assignmentList=assignmentList RPAREN ASSIGN unaryExpression=unaryExpression
517  | EXIT (LPAREN (expression=expr | 0) RPAREN | 0)
518  | EVAL LPAREN expression=expr RPAREN
519  | INCLUDE includeExpression=unaryExpression
520  | INCLUDE_ONCE includeExpression=unaryExpression
521  | REQUIRE includeExpression=unaryExpression
522  | REQUIRE_ONCE includeExpression=unaryExpression
523
524  | unaryExpressionNotPlusminus=unaryExpressionNotPlusminus
525 )
526-> unaryExpression [
527     member variable castType: CastType;
528];;
529
530    (#prefixOperator=postprefixOperator)*
531    varExpression=varExpression
532    (#postfixOperator=postprefixOperator)*
533-> unaryExpressionNotPlusminus ;;
534
535   op=INC | op=DEC
536-> postprefixOperator ;;
537
538-- 10 first follow conflicts because we go back up the chain (affects both print and yield)
539    (print=PRINT+) printExpression=assignmentExpression
540  | isGenerator=YIELD (generatorExpression=printExpression ( DOUBLE_ARROW generatorValueExpr=printExpression | 0 ) | 0)
541  | isGenerator=YIELD_FROM generatorExpression=printExpression
542--first/first conflict - no problem because of ifs
543  | ?[: m_state.varExpressionState == OnlyVariable :] 0 [: m_state.varExpressionState = Normal; :] variable=variable
544  | ?[: m_state.varExpressionState == OnlyNewObject :] 0 [: m_state.varExpressionState = Normal; :] newObject=varExpressionNewObject
545  | varExpressionNormal=varExpressionNormal
546  | varExpressionArray=varExpressionArray arrayIndex=arrayIndexSpecifier*
547-> varExpression ;;
548
549    (?[: LA(1).kind == Token_LPAREN && LA(2).kind == Token_FUNCTION && LA(3).kind == Token_LPAREN :] iife=iifeSyntax )
550  | LPAREN try/rollback (newObject=varExpressionNewObject RPAREN (#variableProperties=instantiationAccess*))
551    catch (expression=expr RPAREN)
552  | BACKTICK encapsList=encapsList BACKTICK
553  --try/rollback resolves conflict scalar vs. staticMember (foo::bar vs. foo::$bar)
554  --varExpressionIsVariable flag is needed for assignmentExpression
555  | try/rollback (variable=variable [: m_state.varExpressionIsVariable = true; :])
556    catch (scalar=scalar)
557  | ISSET LPAREN (#issetVariable=variable @ COMMA) RPAREN
558  | EMPTY LPAREN emptyExpression=expr RPAREN
559  | newObject=varExpressionNewObject
560  | CLONE cloneCar=varExpressionNormal
561  | closure=closure
562-> varExpressionNormal ;;
563
564    ARRAY LPAREN
565        (#arrayValues=arrayPairValue
566             -- break because array(1,) is allowed (solves FIRST/FOLLOW conflict)
567          @ (COMMA [: if (yytoken == Token_RPAREN) { break; } :] ) | 0)
568    RPAREN
569  | LBRACKET
570        (#arrayValues=arrayPairValue
571             -- break because [1,] is allowed (solves FIRST/FOLLOW conflict)
572          @ (COMMA [: if (yytoken == Token_RBRACKET) { break; } :] ) | 0)
573    RBRACKET
574-> varExpressionArray ;;
575
576-- https://wiki.php.net/rfc/closures
577    FUNCTION (isRef=BIT_AND|0) LPAREN parameters=parameterList RPAREN
578        ( USE LPAREN lexicalVars=lexicalVarList RPAREN | 0)
579        ( COLON returnType=returnType | 0)
580        LBRACE try/recover(functionBody=innerStatementList) RBRACE
581-> closure ;;
582
583    LPAREN try/rollback (closure=closure RPAREN LPAREN parameterList=functionCallParameterList RPAREN)
584    catch (expression=expr RPAREN)
585-> iifeSyntax ;;
586
587  (#lexicalVars=lexicalVar @ COMMA) | 0 [: reportProblem(Error, QStringLiteral("Use list of closure must not be empty.")); :]
588-> lexicalVarList ;;
589
590  (isRef=BIT_AND | 0) variable=variableIdentifier
591-> lexicalVar ;;
592
593    NEW classNameReference=classNameReference ctor=ctorArguments
594-> varExpressionNewObject ;;
595
596    LPAREN parameterList=functionCallParameterList RPAREN
597  | 0
598-> ctorArguments ;;
599
600    #parameters=functionCallParameterListElement @ COMMA | 0
601-> functionCallParameterList ;;
602
603    (BIT_AND variable=variable) | (isVariadic=ELLIPSIS | 0) expr=expr
604-> functionCallParameterListElement ;;
605
606    #element=assignmentListElement @COMMA
607-> assignmentList ;;
608
609    variable=variable
610  | LIST LPAREN assignmentList=assignmentList RPAREN
611  | 0
612-> assignmentListElement ;;
613
614    expr=expr (DOUBLE_ARROW (exprValue=expr | BIT_AND varValue=variable) | 0)
615  | BIT_AND variable=variable
616-> arrayPairValue ;;
617
618   var=baseVariableWithFunctionCalls (#variableProperties=variableObjectProperty*)
619-> variable ;;
620
621    OBJECT_OPERATOR
622  | PAAMAYIM_NEKUDOTAYIM
623-> objectOperator ;;
624
625    ( ?[: LA(1).kind == Token_DOLLAR:] LBRACE variable=variable RBRACE | objectProperty=objectProperty )
626    (isFunctionCall=LPAREN parameterList=functionCallParameterList RPAREN arrayIndex=arrayIndexSpecifier* | 0)
627-> variableProperty ;;
628
629   objectOperator variableProperty=variableProperty
630-> variableObjectProperty ;;
631
632   OBJECT_OPERATOR variableProperty=variableProperty
633-> instantiationAccess ;;
634
635   --Conflict
636   --   foo::$bar[0] (=baseVariable-staticMember)
637   --vs.foo::$bar[0](); (=static function call)
638   try/rollback (functionCall=functionCall arrayIndex=arrayIndexSpecifier*)
639   catch (baseVariable=baseVariable)
640-> baseVariableWithFunctionCalls ;;
641
642   LBRACKET (expr=expr | 0) RBRACKET
643-> arrayIndexSpecifier ;;
644
645   LBRACKET (expr=expr) RBRACKET
646-> stringIndexSpecifier ;;
647
648    stringFunctionNameOrClass=namespacedIdentifier (
649        LPAREN stringParameterList=functionCallParameterList RPAREN
650      | PAAMAYIM_NEKUDOTAYIM
651        (
652            stringFunctionName=semiReservedIdentifier LPAREN stringParameterList=functionCallParameterList RPAREN
653            | varFunctionName=variableWithoutObjects LPAREN stringParameterList=functionCallParameterList RPAREN
654            | LBRACE (expr=expr) RBRACE LPAREN stringParameterList=functionCallParameterList RPAREN
655        )
656    )
657  | varFunctionName=variableWithoutObjects LPAREN varParameterList=functionCallParameterList RPAREN
658-> functionCall ;;
659
660    var=compoundVariableWithSimpleIndirectReference #offsetItems=dimListItem*
661  | staticMember=staticMember
662-> baseVariable ;;
663
664    variable=variableIdentifier
665  | DOLLAR LBRACE expr=expr RBRACE
666-> compoundVariable ;;
667
668  ( DOLLAR ( DOLLAR+ | 0 ) ( indirectVariable=variableIdentifier | LBRACE expr=expr RBRACE ) | variable=variableIdentifier )
669-> compoundVariableWithSimpleIndirectReference ;;
670
671    className=namespacedIdentifier staticProperty=staticProperty
672-> staticMember ;;
673
674    LBRACE try/recover(statements=innerStatementList) RBRACE
675  | IF LPAREN ifExpr=expr RPAREN
676      (   COLON statements=innerStatementList newElseifList newElseSingle ENDIF semicolonOrCloseTag
677        | ifStatement=statement elseifList=elseifList elseSingle=elseSingle
678      )
679  | WHILE LPAREN whileExpr=expr RPAREN whileStatement=whileStatement
680  | FOR LPAREN forExpr1=forExpr SEMICOLON forExpr2=forExpr
681    SEMICOLON forExpr3=forExpr RPAREN forStatement=forStatement
682  | SWITCH LPAREN swtichExpr=expr RPAREN switchCaseList=switchCaseList
683
684  | FOREACH LPAREN (
685            -- allow $var as &$i and not expr() as &$i
686        try/rollback(foreachVar=variable AS foreachVarAsVar=foreachVariable)
687        catch(foreachExpr=expr AS foreachExprAsVar=variable))
688        (DOUBLE_ARROW foreachVariable=foreachVariable | 0) RPAREN
689        foreachStatement=foreachStatement
690  | DECLARE LPAREN declareItem=declareItem @ COMMA RPAREN declareStatement
691  | SEMICOLON     -- empty statement
692  | TRY  LBRACE try/recover(statements=innerStatementList) RBRACE
693    #catches=catchItem*
694    (FINALLY LBRACE finallyBody=innerStatementList RBRACE | 0)
695  | UNSET LPAREN #unsetVariables=variable @ COMMA RPAREN semicolonOrCloseTag
696  -- fix first/follow with goto target
697  | ( ?[: LA(1).kind != Token_STRING || LA(2).kind != Token_COLON :] expr=expr semicolonOrCloseTag )
698  | DO doStatement=statement WHILE LPAREN whileExpr=expr RPAREN semicolonOrCloseTag
699  | BREAK (breakExpr=expr | 0) semicolonOrCloseTag
700  | CONTINUE (continueExpr=expr | 0) semicolonOrCloseTag
701  | RETURN (returnExpr=expr | 0) semicolonOrCloseTag
702  | GLOBAL #globalVars=globalVar @ COMMA semicolonOrCloseTag
703  | STATIC #staticVars=staticVar @ COMMA semicolonOrCloseTag
704  | ECHO #echoExprs=expr @ COMMA semicolonOrCloseTag
705  | THROW throwExpr=expr semicolonOrCloseTag
706  -- throws error in zend parser, so ignored | USE use_filename  semicolonOrCloseTag
707
708  | CLOSE_TAG
709  | OPEN_TAG
710  | OPEN_TAG_WITH_ECHO expr=expr semicolonOrCloseTag
711  | INLINE_HTML
712  | CONST #consts=constantDeclaration @ COMMA SEMICOLON
713  | USE useStatement=useStatement
714  | GOTO gotoLabel=STRING SEMICOLON
715  | gotoTarget=STRING COLON
716-> statement ;;
717
718    ( useFunction=FUNCTION | useConst=CONST | 0 ) #useNamespace=useNamespace @ COMMA SEMICOLON
719-> useStatement ;;
720
721    identifier=namespacedIdentifier (AS aliasIdentifier=identifier | 0)
722-> useNamespace ;;
723
724    identifier=identifier ASSIGN scalar=expr
725-> constantDeclaration ;;
726
727    identifier=semiReservedIdentifier ASSIGN scalar=expr
728-> classConstantDeclaration ;;
729
730   SEMICOLON | CLOSE_TAG
731-> semicolonOrCloseTag ;;
732
733    LBRACE (SEMICOLON | 0) try/recover(caseList=caseList) RBRACE
734  | COLON (SEMICOLON | 0) caseList=caseList ENDSWITCH semicolonOrCloseTag
735-> switchCaseList ;;
736
737    #caseItems=case_item*
738-> caseList ;;
739
740    CASE expr=expr (COLON | SEMICOLON) statements=innerStatementList
741  | def=DEFAULT (COLON | SEMICOLON) statements=innerStatementList
742-> case_item ;;
743
744    CATCH LPAREN #catchClass=namespacedIdentifier @ BIT_OR var=variableIdentifier RPAREN
745    LBRACE try/recover(statements=innerStatementList) RBRACE
746-> catchItem ;;
747
748    statement=statement
749  | COLON statements=innerStatementList ENDDECLARE semicolonOrCloseTag
750-> declareStatement ;;
751
752    STRING ASSIGN scalar=staticScalar
753-> declareItem ;;
754
755    (BIT_AND | 0) variable=variable
756-> foreachVariable ;;
757
758    statement=statement
759  | COLON statements=innerStatementList ENDFOREACH semicolonOrCloseTag
760-> foreachStatement ;;
761
762  var=variableIdentifier (ASSIGN value=staticScalar | 0)
763-> staticVar ;;
764
765    var=variableIdentifier
766  | DOLLAR (dollarVar=variable | LBRACE expr=expr RBRACE)
767-> globalVar ;;
768
769    #exprs=expr @ COMMA | 0
770-> forExpr ;;
771
772    statement=statement
773  | COLON statements=innerStatementList ENDFOR semicolonOrCloseTag
774-> forStatement ;;
775
776    statement=statement
777  | COLON statements=innerStatementList ENDWHILE semicolonOrCloseTag
778-> whileStatement ;;
779
780    --first/follow conflict; todo check if this is a problem
781    #elseifListItem=elseifListItem*
782-> elseifList ;;
783
784    ELSEIF LPAREN expr=expr RPAREN statement=statement
785-> elseifListItem ;;
786
787    ELSE statement=statement | 0
788-> elseSingle ;;
789
790    #newElseifListItem=newelseifListItem*
791-> newElseifList ;;
792
793    ELSEIF LPAREN expr=expr RPAREN COLON statements=innerStatementList
794-> newelseifListItem ;;
795
796    ELSE COLON statements=innerStatementList | 0
797-> newElseSingle ;;
798
799    className=className (staticProperty=staticProperty #properties=classProperty* | 0)
800  | baseVariable=variableWithoutObjects #properties=classProperty*
801-> classNameReference ;;
802
803    identifier=namespacedIdentifier
804  | staticIdentifier = STATIC
805-> className ;;
806
807    PAAMAYIM_NEKUDOTAYIM staticProperty=compoundVariableWithSimpleIndirectReference #offsetItems=dimListItem*
808-> staticProperty ;;
809
810   (staticProperty=staticProperty | OBJECT_OPERATOR property=objectProperty)
811-> classProperty ;;
812
813    objectDimList=objectDimList
814  | variableWithoutObjects=variableWithoutObjects
815-> objectProperty ;;
816
817    variableName=variableName #offsetItems=dimListItem*
818-> objectDimList ;;
819
820  variable=compoundVariableWithSimpleIndirectReference #offsetItems=dimListItem*
821-> variableWithoutObjects ;;
822
823arrayIndex=arrayIndexSpecifier | LBRACE expr=expr RBRACE
824-> dimListItem ;;
825
826    name=identifier
827  | LBRACE expr=expr RBRACE
828-> variableName ;;
829
830    commonScalar=commonScalar
831  | constantOrClassConst=constantOrClassConst #offsetItem=dimListItem*
832  | varname=STRING_VARNAME
833  | DOUBLE_QUOTE encapsList=encapsList DOUBLE_QUOTE stringIndex=stringIndexSpecifier*
834  | START_HEREDOC encapsList=encapsList END_HEREDOC
835-> scalar ;;
836
837  constant=namespacedIdentifier
838  ( PAAMAYIM_NEKUDOTAYIM classConstant=classConstant | 0 )
839-> constantOrClassConst ;;
840
841  semiReservedIdentifier
842-> classConstant ;;
843
844    #encaps=encaps*
845-> encapsList ;;
846
847    var=encapsVar | value=ENCAPSED_AND_WHITESPACE
848-> encaps ;;
849
850     -- first/first conflict resolved by LA(2)
851     --(expr allows STRING_VARNAME too - but without [expr])
852    DOLLAR_OPEN_CURLY_BRACES ( ?[: LA(2).kind == Token_LBRACKET:] STRING_VARNAME arrayIndex=arrayIndexSpecifier RBRACE
853      | expr=expr RBRACE )
854  | variable=variableIdentifier (OBJECT_OPERATOR propertyIdentifier=identifier | LBRACKET offset=encapsVarOffset RBRACKET | 0)
855  | CURLY_OPEN expr=expr RBRACE
856-> encapsVar ;;
857
858    STRING
859  | NUM_STRING
860  | variableIdentifier
861-> encapsVarOffset ;;
862
863
864    LNUMBER                  [: (*yynode)->scalarType = ScalarTypeInt; :]
865  | DNUMBER                  [: (*yynode)->scalarType = ScalarTypeFloat; :]
866  | string=CONSTANT_ENCAPSED_STRING [: (*yynode)->scalarType = ScalarTypeString; :] stringIndex=stringIndexSpecifier*
867  | LINE                     [: (*yynode)->scalarType = ScalarTypeInt; :]
868  | DIR                      [: (*yynode)->scalarType = ScalarTypeString; :]
869  | FILE                     [: (*yynode)->scalarType = ScalarTypeString; :]
870  | CLASS_C                  [: (*yynode)->scalarType = ScalarTypeString; :]
871  | TRAIT_C                  [: (*yynode)->scalarType = ScalarTypeString; :]
872  | METHOD_C                 [: (*yynode)->scalarType = ScalarTypeString; :]
873  | FUNC_C                   [: (*yynode)->scalarType = ScalarTypeString; :]
874  | NAMESPACE_C              [: (*yynode)->scalarType = ScalarTypeString; :]
875  | START_NOWDOC STRING END_NOWDOC [: (*yynode)->scalarType = ScalarTypeString; :]
876-> commonScalar [
877     member variable scalarType: ScalarTypes;
878] ;;
879
880    FUNCTION (BIT_AND | 0) functionName=identifier
881    LPAREN parameters=parameterList RPAREN (COLON returnType=returnType | 0)
882    LBRACE try/recover(functionBody=innerStatementList) RBRACE
883-> functionDeclarationStatement ;;
884
885    (#parameters=parameter @ COMMA) | 0
886-> parameterList ;;
887
888(parameterType=parameterType | 0) (isRef=BIT_AND | 0)
889    (isVariadic=ELLIPSIS | 0) variable=variableIdentifier (ASSIGN defaultValue=expr | 0)
890-> parameter ;;
891
892    genericType=namespacedIdentifier
893  | arrayType=ARRAY
894  | callableType=CALLABLE
895-> genericTypeHint ;;
896
897    (isNullable=QUESTION | 0) typehint=genericTypeHint
898-> parameterType ;;
899
900    (isNullable=QUESTION | 0) (
901        typehint=genericTypeHint
902      | voidType=VOID
903    )
904-> returnType ;;
905
906    value=commonScalar
907  | constantOrClassConst=constantOrClassConst
908  | PLUS plusValue=staticScalar
909  | MINUS minusValue=staticScalar
910  | array=ARRAY LPAREN
911        (#arrayValues=staticArrayPairValue
912             -- break because array(1,) is allowed
913          @ (COMMA [: if (yytoken == Token_RPAREN) { break; } :] ) | 0)
914    RPAREN
915  | array=LBRACKET
916        (#arrayValues=staticArrayPairValue
917             -- break because [1,] is allowed
918          @ (COMMA [: if (yytoken == Token_RBRACKET) { break; } :] ) | 0)
919    RBRACKET
920-> staticScalar ;;
921
922    #val1=staticScalar (DOUBLE_ARROW #val2=staticScalar | 0)
923-> staticArrayPairValue ;;
924
925   (isGlobal=BACKSLASH | 0)
926   #namespaceName=identifier+ @ BACKSLASH
927-> namespacedIdentifier ;;
928
929     string=STRING
930-> identifier ;;
931
932      INCLUDE | INCLUDE_ONCE | EVAL | REQUIRE | REQUIRE_ONCE | LOGICAL_OR | LOGICAL_XOR | LOGICAL_AND
933    | INSTANCEOF | NEW | CLONE | EXIT | IF | ELSEIF | ELSE | ENDIF | ECHO | DO | WHILE | ENDWHILE
934    | FOR | ENDFOR | FOREACH | ENDFOREACH | DECLARE | ENDDECLARE | AS | TRY | CATCH | FINALLY
935    | THROW | USE | INSTEADOF | GLOBAL | VAR | UNSET | ISSET | EMPTY | CONTINUE | GOTO
936    | FUNCTION | CONST | RETURN | PRINT | YIELD | LIST | SWITCH | ENDSWITCH | CASE | DEFAULT | BREAK
937    | ARRAY | CALLABLE | EXTENDS | IMPLEMENTS | NAMESPACE | TRAIT | INTERFACE | CLASS
938    | CLASS_C | TRAIT_C | FUNC_C | METHOD_C | LINE | FILE | DIR | NAMESPACE_C
939-> reservedNonModifiers ;;
940
941      reservedNonModifiers
942    | STATIC | ABSTRACT | FINAL | PRIVATE | PROTECTED | PUBLIC
943-> semiReserved ;;
944
945      identifier
946[:
947    qint64 index = tokenStream->index() - 2;
948    (*yynode)->string = index;
949:]
950    | semiReserved
951[:
952    qint64 index = tokenStream->index() - 2;
953    (*yynode)->string = index;
954:]
955-> semiReservedIdentifier [
956     member variable string: qint64;
957] ;;
958
959      identifier
960[:
961    qint64 index = tokenStream->index() - 2;
962    (*yynode)->string = index;
963:]
964    | reservedNonModifiers
965[:
966    qint64 index = tokenStream->index() - 2;
967    (*yynode)->string = index;
968:]
969-> reservedNonModifierIdentifier [
970     member variable string: qint64;
971] ;;
972
973     variable=VARIABLE
974-> variableIdentifier ;;
975
976    NAMESPACE #namespaceName=identifier* @ BACKSLASH
977    (
978        -- the semicolon case needs at least one namespace identifier, the {...} case not...
979        SEMICOLON [: if (!(*yynode)->namespaceNameSequence) { reportProblem(Error, QStringLiteral("Missing namespace identifier."), -2); } :]
980    | LBRACE try/recover(body=innerStatementList) RBRACE )
981-> namespaceDeclarationStatement ;;
982
983    INTERFACE interfaceName=identifier (EXTENDS extends=classImplements | 0)
984    LBRACE try/recover(body=classBody) RBRACE
985-> interfaceDeclarationStatement ;;
986
987    TRAIT traitName=identifier
988    LBRACE body=classBody RBRACE
989-> traitDeclarationStatement ;;
990
991    modifier=optionalClassModifier CLASS className=identifier
992        (EXTENDS extends=classExtends | 0)
993        (IMPLEMENTS implements=classImplements | 0)
994    LBRACE body=classBody RBRACE
995-> classDeclarationStatement ;;
996
997identifier=namespacedIdentifier
998-> classExtends ;;
999
1000#implements=namespacedIdentifier @ COMMA
1001-> classImplements ;;
1002
1003-- error recovery, to understand it you probably have to look at the generated code ;-)
1004[: bool reported = false; while ( true ) { :]
1005try/recover(#classStatements=classStatement)*
1006[: if (yytoken != Token_RBRACE && yytoken != Token_EOF && yytoken != Token_CLOSE_TAG) {
1007        if (!reported) {
1008            reportProblem(Error, QStringLiteral("Unexpected token in class context."));
1009            reported = true;
1010        }
1011        yylex();
1012   } else {
1013        break;
1014   }
1015} :]
1016 RBRACE [: rewind(tokenStream->index() - 2); :]
1017-> classBody ;;
1018
1019    VAR variable=classVariableDeclaration SEMICOLON
1020  | modifiers=optionalModifiers
1021    ( (propertyType=propertyType | 0) variable=classVariableDeclaration SEMICOLON
1022      | FUNCTION (BIT_AND | 0) methodName=semiReservedIdentifier LPAREN parameters=parameterList RPAREN
1023        ( COLON returnType=returnType | 0)
1024        methodBody=methodBody
1025      | CONST #consts=classConstantDeclaration @ COMMA SEMICOLON
1026    )
1027  | USE #traits=namespacedIdentifier @ COMMA (imports=traitAliasDeclaration|SEMICOLON)
1028-> classStatement ;;
1029
1030    (isNullable=QUESTION | 0) typehint=propertyTypeHint
1031-> propertyType ;;
1032
1033  ( genericType=namespacedIdentifier
1034    | arrayType=ARRAY )
1035  [: (*yynode)->callableType = -1; :]
1036-> propertyTypeHint[
1037     member variable callableType: int;
1038] ;;
1039
1040    LBRACE #statements=traitAliasStatement
1041        @ (SEMICOLON [: if (yytoken == Token_RBRACE) { break; } :]) RBRACE
1042-> traitAliasDeclaration ;;
1043
1044    importIdentifier=traitAliasIdentifier
1045-- first/first conflict resolved by LA(2)
1046-- We can either have a single token (modifier or identifier), or a combination
1047    ( AS (?[: LA(2).kind == Token_SEMICOLON :]
1048        (modifiers=traitVisibilityModifiers | aliasNonModifierIdentifier=reservedNonModifierIdentifier)
1049        | modifiers=traitVisibilityModifiers aliasIdentifier=semiReservedIdentifier
1050      )
1051      | INSTEADOF #conflictIdentifier=namespacedIdentifier @ COMMA
1052    )
1053-> traitAliasStatement ;;
1054
1055    identifier=namespacedIdentifier PAAMAYIM_NEKUDOTAYIM methodIdentifier=semiReservedIdentifier
1056-> traitAliasIdentifier ;;
1057
1058    SEMICOLON -- abstract method
1059 |  LBRACE try/recover(statements=innerStatementList) RBRACE
1060-> methodBody ;;
1061
1062#vars=classVariable @ COMMA
1063-> classVariableDeclaration ;;
1064
1065    variable=variableIdentifier (ASSIGN value=staticScalar | 0)
1066-> classVariable ;;
1067
1068    PUBLIC     [: (*yynode)->modifiers |= ModifierPublic;     :]
1069  | PROTECTED  [: (*yynode)->modifiers |= ModifierProtected;  :]
1070  | PRIVATE    [: (*yynode)->modifiers |= ModifierPrivate;    :]
1071  | STATIC     [: (*yynode)->modifiers |= ModifierStatic;     :]
1072  | ABSTRACT   [: (*yynode)->modifiers |= ModifierAbstract;   :]
1073  | FINAL      [: (*yynode)->modifiers |= ModifierFinal;      :]
1074-> traitVisibilityModifiers[
1075     member variable modifiers: unsigned int;
1076] ;;
1077
1078  (
1079    PUBLIC     [: (*yynode)->modifiers |= ModifierPublic;      :]
1080  | PROTECTED  [: (*yynode)->modifiers |= ModifierProtected;      :]
1081  | PRIVATE    [: (*yynode)->modifiers |= ModifierPrivate;      :]
1082  | STATIC     [: (*yynode)->modifiers |= ModifierStatic;      :]
1083  | ABSTRACT   [: (*yynode)->modifiers |= ModifierAbstract;      :]
1084  | FINAL      [: (*yynode)->modifiers |= ModifierFinal;      :]
1085  | 0
1086  )*
1087-> optionalModifiers[
1088     member variable modifiers: unsigned int;
1089] ;;
1090
1091  (
1092    ABSTRACT   [: (*yynode)->modifier = AbstractClass; :]
1093  | FINAL      [: (*yynode)->modifier = FinalClass;    :]
1094  | 0
1095  )
1096-> optionalClassModifier[
1097     member variable modifier: ClassModifier;
1098] ;;
1099
1100
1101
1102
1103
1104-----------------------------------------------------------------
1105-- Code segments copied to the implementation (.cpp) file.
1106-- If existent, kdevelop-pg's current syntax requires this block
1107-- to occur at the end of the file.
1108-----------------------------------------------------------------
1109
1110[:
1111
1112#include <QtCore/QDebug>
1113#include <language/editor/documentrange.h>
1114
1115namespace Php
1116{
1117
1118void Parser::tokenize(const QString& contents, int initialState)
1119{
1120    m_contents = contents;
1121    Lexer lexer(tokenStream, contents, initialState);
1122    int kind = Parser::Token_EOF;
1123    int lastDocCommentBegin;
1124    int lastDocCommentEnd;
1125
1126    do
1127    {
1128        lastDocCommentBegin = 0;
1129        lastDocCommentEnd = 0;
1130        kind = lexer.nextTokenKind();
1131        while (kind == Parser::Token_WHITESPACE || kind == Parser::Token_COMMENT || kind == Parser::Token_DOC_COMMENT) {
1132            if (kind == Parser::Token_COMMENT || kind == Parser::Token_DOC_COMMENT) {
1133                extractTodosFromComment(tokenText(lexer.tokenBegin(), lexer.tokenEnd()), lexer.tokenBegin());
1134            }
1135            if (kind == Parser::Token_DOC_COMMENT) {
1136                lastDocCommentBegin = lexer.tokenBegin();
1137                lastDocCommentEnd = lexer.tokenEnd();
1138            }
1139            kind = lexer.nextTokenKind();
1140        }
1141        if ( !kind ) // when the lexer returns 0, the end of file is reached
1142        {
1143            kind = Parser::Token_EOF;
1144        }
1145        Parser::Token &t = tokenStream->push();
1146        t.begin = lexer.tokenBegin();
1147        t.end = lexer.tokenEnd();
1148        t.kind = kind;
1149        t.docCommentBegin = lastDocCommentBegin;
1150        t.docCommentEnd = lastDocCommentEnd;
1151        //if ( m_debug ) qDebug() << kind << tokenText(t.begin,t.end) << t.begin << t.end;
1152    }
1153    while ( kind != Parser::Token_EOF );
1154
1155    yylex(); // produce the look ahead token
1156}
1157
1158void Parser::extractTodosFromComment(const QString& comment, qint64 startPosition)
1159{
1160    auto i = m_todoMarkers.globalMatch(comment);
1161    while (i.hasNext()) {
1162        auto match = i.next();
1163        auto p = reportProblem(Todo, match.captured(1), 0);
1164        if (!p) {
1165            continue;
1166        }
1167
1168        qint64 line = 0;
1169        qint64 column = 0;
1170        tokenStream->locationTable()->positionAt(startPosition, &line, &column);
1171
1172        auto location = p->finalLocation();
1173        location.setStart(KTextEditor::Cursor(line, column + match.capturedStart(1)));
1174        location.setEnd(KTextEditor::Cursor(line, column + match.capturedEnd(1)));
1175        p->setFinalLocation(location);
1176    };
1177}
1178
1179void Parser::setTodoMarkers(const QStringList& markers)
1180{
1181    QString pattern = QStringLiteral("^(?:[/\\*\\s]*)(.*(?:");
1182    bool first = true;
1183    foreach(const QString& marker, markers) {
1184        if (!first) {
1185            pattern += '|';
1186        }
1187        pattern += QRegularExpression::escape(marker);
1188        first = false;
1189    }
1190    pattern += QStringLiteral(").*?)(?:[/\\*\\s]*)$");
1191    m_todoMarkers.setPatternOptions(QRegularExpression::MultilineOption);
1192    m_todoMarkers.setPattern(pattern);
1193}
1194
1195QString Parser::tokenText(qint64 begin, qint64 end)
1196{
1197    return m_contents.mid(begin,end-begin+1);
1198}
1199
1200
1201KDevelop::ProblemPointer Parser::reportProblem( Parser::ProblemType type, const QString& message, int offset )
1202{
1203    qint64 sLine;
1204    qint64 sCol;
1205    qint64 index = tokenStream->index() + offset;
1206    if (index >= tokenStream->size()) {
1207        return {};
1208    }
1209    tokenStream->startPosition(index, &sLine, &sCol);
1210    qint64 eLine;
1211    qint64 eCol;
1212    tokenStream->endPosition(index, &eLine, &eCol);
1213    auto p = KDevelop::ProblemPointer(new KDevelop::Problem());
1214    p->setSource(KDevelop::IProblem::Parser);
1215    switch ( type ) {
1216        case Error:
1217            p->setSeverity(KDevelop::IProblem::Error);
1218            break;
1219        case Warning:
1220            p->setSeverity(KDevelop::IProblem::Warning);
1221            break;
1222        case Info:
1223            p->setSeverity(KDevelop::IProblem::Hint);
1224            break;
1225        case Todo:
1226            p->setSeverity(KDevelop::IProblem::Hint);
1227            p->setSource(KDevelop::IProblem::ToDo);
1228            break;
1229    }
1230    p->setDescription(message);
1231    KTextEditor::Range range(sLine, sCol, eLine, eCol + 1);
1232    p->setFinalLocation(KDevelop::DocumentRange(m_currentDocument, range));
1233    m_problems << p;
1234    return p;
1235}
1236
1237
1238// custom error recovery
1239void Parser::expectedToken(int /*expected*/, qint64 /*where*/, const QString& name)
1240{
1241    reportProblem( Parser::Error, QStringLiteral("Expected token \"%1\"").arg(name));
1242}
1243
1244void Parser::expectedSymbol(int /*expectedSymbol*/, const QString& name)
1245{
1246    qint64 line;
1247    qint64 col;
1248    qint64 index = tokenStream->index()-1;
1249    Token &token = tokenStream->at(index);
1250    qCDebug(PARSER) << "token starts at:" << token.begin;
1251    qCDebug(PARSER) << "index is:" << index;
1252    tokenStream->startPosition(index, &line, &col);
1253    QString tokenValue = tokenText(token.begin, token.end);
1254    qint64 eLine;
1255    qint64 eCol;
1256    tokenStream->endPosition(index, &eLine, &eCol);
1257    reportProblem( Parser::Error,
1258                   QStringLiteral("Expected symbol \"%1\" (current token: \"%2\" [%3] at %4:%5 - %6:%7)")
1259                  .arg(name,
1260                       token.kind != 0 ? tokenValue : QStringLiteral("EOF"))
1261                  .arg(token.kind)
1262                  .arg(line)
1263                  .arg(col)
1264                  .arg(eLine)
1265                  .arg(eCol));
1266}
1267
1268void Parser::setDebug( bool debug )
1269{
1270    m_debug = debug;
1271}
1272
1273void Parser::setCurrentDocument(KDevelop::IndexedString url)
1274{
1275    m_currentDocument = url;
1276}
1277
1278
1279Parser::ParserState *Parser::copyCurrentState()
1280{
1281    ParserState *state = new ParserState();
1282    state->varExpressionState = m_state.varExpressionState;
1283    state->varExpressionIsVariable = m_state.varExpressionIsVariable;
1284    return state;
1285}
1286
1287void Parser::restoreState( Parser::ParserState* state)
1288{
1289    m_state.varExpressionState = state->varExpressionState;
1290    m_state.varExpressionIsVariable = state->varExpressionIsVariable;
1291}
1292
1293} // end of namespace Php
1294
1295:]
1296
1297-- kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on; mode KDevelop-PG[-Qt]
1298