annotate vendor/nikic/php-parser/doc/component/Lexer.markdown @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 5fb285c0d0e3
rev   line source
Chris@0 1 Lexer component documentation
Chris@0 2 =============================
Chris@0 3
Chris@0 4 The lexer is responsible for providing tokens to the parser. The project comes with two lexers: `PhpParser\Lexer` and
Chris@0 5 `PhpParser\Lexer\Emulative`. The latter is an extension of the former, which adds the ability to emulate tokens of
Chris@0 6 newer PHP versions and thus allows parsing of new code on older versions.
Chris@0 7
Chris@0 8 This documentation discusses options available for the default lexers and explains how lexers can be extended.
Chris@0 9
Chris@0 10 Lexer options
Chris@0 11 -------------
Chris@0 12
Chris@0 13 The two default lexers accept an `$options` array in the constructor. Currently only the `'usedAttributes'` option is
Chris@0 14 supported, which allows you to specify which attributes will be added to the AST nodes. The attributes can then be
Chris@0 15 accessed using `$node->getAttribute()`, `$node->setAttribute()`, `$node->hasAttribute()` and `$node->getAttributes()`
Chris@0 16 methods. A sample options array:
Chris@0 17
Chris@0 18 ```php
Chris@0 19 $lexer = new PhpParser\Lexer(array(
Chris@0 20 'usedAttributes' => array(
Chris@0 21 'comments', 'startLine', 'endLine'
Chris@0 22 )
Chris@0 23 ));
Chris@0 24 ```
Chris@0 25
Chris@0 26 The attributes used in this example match the default behavior of the lexer. The following attributes are supported:
Chris@0 27
Chris@0 28 * `comments`: Array of `PhpParser\Comment` or `PhpParser\Comment\Doc` instances, representing all comments that occurred
Chris@0 29 between the previous non-discarded token and the current one. Use of this attribute is required for the
Chris@0 30 `$node->getDocComment()` method to work. The attribute is also needed if you wish the pretty printer to retain
Chris@0 31 comments present in the original code.
Chris@0 32 * `startLine`: Line in which the node starts. This attribute is required for the `$node->getLine()` to work. It is also
Chris@0 33 required if syntax errors should contain line number information.
Chris@0 34 * `endLine`: Line in which the node ends.
Chris@0 35 * `startTokenPos`: Offset into the token array of the first token in the node.
Chris@0 36 * `endTokenPos`: Offset into the token array of the last token in the node.
Chris@0 37 * `startFilePos`: Offset into the code string of the first character that is part of the node.
Chris@0 38 * `endFilePos`: Offset into the code string of the last character that is part of the node.
Chris@0 39
Chris@0 40 ### Using token positions
Chris@0 41
Chris@0 42 The token offset information is useful if you wish to examine the exact formatting used for a node. For example the AST
Chris@0 43 does not distinguish whether a property was declared using `public` or using `var`, but you can retrieve this
Chris@0 44 information based on the token position:
Chris@0 45
Chris@0 46 ```php
Chris@0 47 function isDeclaredUsingVar(array $tokens, PhpParser\Node\Stmt\Property $prop) {
Chris@0 48 $i = $prop->getAttribute('startTokenPos');
Chris@0 49 return $tokens[$i][0] === T_VAR;
Chris@0 50 }
Chris@0 51 ```
Chris@0 52
Chris@0 53 In order to make use of this function, you will have to provide the tokens from the lexer to your node visitor using
Chris@0 54 code similar to the following:
Chris@0 55
Chris@0 56 ```php
Chris@0 57 class MyNodeVisitor extends PhpParser\NodeVisitorAbstract {
Chris@0 58 private $tokens;
Chris@0 59 public function setTokens(array $tokens) {
Chris@0 60 $this->tokens = $tokens;
Chris@0 61 }
Chris@0 62
Chris@0 63 public function leaveNode(PhpParser\Node $node) {
Chris@0 64 if ($node instanceof PhpParser\Node\Stmt\Property) {
Chris@0 65 var_dump(isDeclaredUsingVar($this->tokens, $node));
Chris@0 66 }
Chris@0 67 }
Chris@0 68 }
Chris@0 69
Chris@0 70 $lexer = new PhpParser\Lexer(array(
Chris@0 71 'usedAttributes' => array(
Chris@0 72 'comments', 'startLine', 'endLine', 'startTokenPos', 'endTokenPos'
Chris@0 73 )
Chris@0 74 ));
Chris@0 75 $parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7, $lexer);
Chris@0 76
Chris@0 77 $visitor = new MyNodeVisitor();
Chris@0 78 $traverser = new PhpParser\NodeTraverser();
Chris@0 79 $traverser->addVisitor($visitor);
Chris@0 80
Chris@0 81 try {
Chris@0 82 $stmts = $parser->parse($code);
Chris@0 83 $visitor->setTokens($lexer->getTokens());
Chris@0 84 $stmts = $traverser->traverse($stmts);
Chris@0 85 } catch (PhpParser\Error $e) {
Chris@0 86 echo 'Parse Error: ', $e->getMessage();
Chris@0 87 }
Chris@0 88 ```
Chris@0 89
Chris@0 90 The same approach can also be used to perform specific modifications in the code, without changing the formatting in
Chris@0 91 other places (which is the case when using the pretty printer).
Chris@0 92
Chris@0 93 Lexer extension
Chris@0 94 ---------------
Chris@0 95
Chris@0 96 A lexer has to define the following public interface:
Chris@0 97
Chris@0 98 void startLexing(string $code, ErrorHandler $errorHandler = null);
Chris@0 99 array getTokens();
Chris@0 100 string handleHaltCompiler();
Chris@0 101 int getNextToken(string &$value = null, array &$startAttributes = null, array &$endAttributes = null);
Chris@0 102
Chris@0 103 The `startLexing()` method is invoked with the source code that is to be lexed (including the opening tag) whenever the
Chris@0 104 `parse()` method of the parser is called. It can be used to reset state or preprocess the source code or tokens. The
Chris@0 105 passes `ErrorHandler` should be used to report lexing errors.
Chris@0 106
Chris@0 107 The `getTokens()` method returns the current token array, in the usual `token_get_all()` format. This method is not
Chris@0 108 used by the parser (which uses `getNextToken()`), but is useful in combination with the token position attributes.
Chris@0 109
Chris@0 110 The `handleHaltCompiler()` method is called whenever a `T_HALT_COMPILER` token is encountered. It has to return the
Chris@0 111 remaining string after the construct (not including `();`).
Chris@0 112
Chris@0 113 The `getNextToken()` method returns the ID of the next token (as defined by the `Parser::T_*` constants). If no more
Chris@0 114 tokens are available it must return `0`, which is the ID of the `EOF` token. Furthermore the string content of the
Chris@0 115 token should be written into the by-reference `$value` parameter (which will then be available as `$n` in the parser).
Chris@0 116
Chris@0 117 ### Attribute handling
Chris@0 118
Chris@0 119 The other two by-ref variables `$startAttributes` and `$endAttributes` define which attributes will eventually be
Chris@0 120 assigned to the generated nodes: The parser will take the `$startAttributes` from the first token which is part of the
Chris@0 121 node and the `$endAttributes` from the last token that is part of the node.
Chris@0 122
Chris@0 123 E.g. if the tokens `T_FUNCTION T_STRING ... '{' ... '}'` constitute a node, then the `$startAttributes` from the
Chris@0 124 `T_FUNCTION` token will be taken and the `$endAttributes` from the `'}'` token.
Chris@0 125
Chris@0 126 An application of custom attributes is storing the exact original formatting of literals: While the parser does retain
Chris@0 127 some information about the formatting of integers (like decimal vs. hexadecimal) or strings (like used quote type), it
Chris@0 128 does not preserve the exact original formatting (e.g. leading zeros for integers or escape sequences in strings). This
Chris@0 129 can be remedied by storing the original value in an attribute:
Chris@0 130
Chris@0 131 ```php
Chris@0 132 use PhpParser\Lexer;
Chris@0 133 use PhpParser\Parser\Tokens;
Chris@0 134
Chris@0 135 class KeepOriginalValueLexer extends Lexer // or Lexer\Emulative
Chris@0 136 {
Chris@0 137 public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
Chris@0 138 $tokenId = parent::getNextToken($value, $startAttributes, $endAttributes);
Chris@0 139
Chris@0 140 if ($tokenId == Tokens::T_CONSTANT_ENCAPSED_STRING // non-interpolated string
Chris@0 141 || $tokenId == Tokens::T_ENCAPSED_AND_WHITESPACE // interpolated string
Chris@0 142 || $tokenId == Tokens::T_LNUMBER // integer
Chris@0 143 || $tokenId == Tokens::T_DNUMBER // floating point number
Chris@0 144 ) {
Chris@0 145 // could also use $startAttributes, doesn't really matter here
Chris@0 146 $endAttributes['originalValue'] = $value;
Chris@0 147 }
Chris@0 148
Chris@0 149 return $tokenId;
Chris@0 150 }
Chris@0 151 }
Chris@0 152 ```