Passed
Pull Request — master (#145)
by Christoffer
02:24
created

LexerUtilsTrait::expectKeyword()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 2
nop 2
1
<?php
2
3
namespace Digia\GraphQL\Language;
4
5
use Digia\GraphQL\Error\SyntaxErrorException;
6
7
trait LexerUtilsTrait
8
{
9
    /**
10
     * Determines if the next token is of a given kind.
11
     *
12
     * @param LexerInterface $lexer
13
     * @param string         $kind
14
     * @return bool
15
     */
16
    protected function peek(LexerInterface $lexer, string $kind): bool
17
    {
18
        return $lexer->getTokenKind() === $kind;
19
    }
20
21
    /**
22
     * If the next token is of the given kind, return true after advancing
23
     * the lexer. Otherwise, do not change the parser state and return false.
24
     *
25
     * @param LexerInterface $lexer
26
     * @param string         $kind
27
     * @return bool
28
     */
29
    protected function skip(LexerInterface $lexer, string $kind): bool
30
    {
31
        if ($match = $this->peek($lexer, $kind)) {
32
            $lexer->advance();
33
        }
34
35
        return $match;
36
    }
37
38
    /**
39
     * If the next token is of the given kind, return that token after advancing
40
     * the lexer. Otherwise, do not change the parser state and throw an error.
41
     *
42
     * @param LexerInterface $lexer
43
     * @param string         $kind
44
     * @return Token
45
     * @throws SyntaxErrorException
46
     */
47
    protected function expect(LexerInterface $lexer, string $kind): Token
48
    {
49
        $token = $lexer->getToken();
50
51
        if ($token->getKind() === $kind) {
52
            $lexer->advance();
53
            return $token;
54
        }
55
56
        throw new SyntaxErrorException(
57
            $lexer->getSource(),
58
            $token->getStart(),
59
            sprintf('Expected %s, found %s', $kind, $token)
60
        );
61
    }
62
63
    /**
64
     * @param LexerInterface $lexer
65
     * @param string         $value
66
     * @return Token
67
     * @throws SyntaxErrorException
68
     */
69
    protected function expectKeyword(LexerInterface $lexer, string $value): Token
70
    {
71
        $token = $lexer->getToken();
72
73
        if ($token->getKind() === TokenKindEnum::NAME && $token->getValue() === $value) {
74
            $lexer->advance();
75
            return $token;
76
        }
77
78
        throw new SyntaxErrorException(
79
            $lexer->getSource(),
80
            $token->getStart(),
81
            sprintf('Expected %s, found %s', $value, $token)
82
        );
83
    }
84
85
    /**
86
     * Helper function for creating an error when an unexpected lexed token
87
     * is encountered.
88
     *
89
     * @param LexerInterface $lexer
90
     * @param Token|null     $atToken
91
     * @return SyntaxErrorException
92
     */
93
    protected function unexpected(LexerInterface $lexer, ?Token $atToken = null): SyntaxErrorException
94
    {
95
        $token = $atToken ?: $lexer->getToken();
96
97
        return new SyntaxErrorException(
98
            $lexer->getSource(),
99
            $token->getStart(),
100
            sprintf('Unexpected %s', $token)
101
        );
102
    }
103
104
    /**
105
     * Returns a possibly empty list of parse nodes, determined by
106
     * the parseFn. This list begins with a lex token of openKind
107
     * and ends with a lex token of closeKind. Advances the parser
108
     * to the next lex token after the closing token.
109
     *
110
     * @param LexerInterface $lexer
111
     * @param string         $openKind
112
     * @param callable       $parseFunction
113
     * @param string         $closeKind
114
     * @return array
115
     * @throws SyntaxErrorException
116
     */
117
    protected function any(LexerInterface $lexer, string $openKind, callable $parseFunction, string $closeKind): array
118
    {
119
        $this->expect($lexer, $openKind);
120
121
        $nodes = [];
122
123
        while (!$this->skip($lexer, $closeKind)) {
124
            $nodes[] = $parseFunction($lexer);
125
        }
126
127
        return $nodes;
128
    }
129
130
    /**
131
     * Returns a non-empty list of parse nodes, determined by
132
     * the parseFn. This list begins with a lex token of openKind
133
     * and ends with a lex token of closeKind. Advances the parser
134
     * to the next lex token after the closing token.
135
     *
136
     * @param LexerInterface $lexer
137
     * @param string         $openKind
138
     * @param callable       $parseFunction
139
     * @param string         $closeKind
140
     * @return array
141
     * @throws SyntaxErrorException
142
     */
143
    protected function many(LexerInterface $lexer, string $openKind, callable $parseFunction, string $closeKind): array
144
    {
145
        $this->expect($lexer, $openKind);
146
147
        $nodes = [$parseFunction($lexer)];
148
149
        while (!$this->skip($lexer, $closeKind)) {
150
            $nodes[] = $parseFunction($lexer);
151
        }
152
153
        return $nodes;
154
    }
155
156
    /**
157
     * @param LexerInterface $lexer
158
     * @return bool
159
     */
160
    protected function peekDescription(LexerInterface $lexer): bool
161
    {
162
        return $this->peek($lexer, TokenKindEnum::STRING) || $this->peek($lexer, TokenKindEnum::BLOCK_STRING);
163
    }
164
165
    /**
166
     * @param LexerInterface $lexer
167
     * @param Token          $startToken
168
     * @return array|null
169
     */
170
    protected function buildLocation(LexerInterface $lexer, Token $startToken): ?array
171
    {
172
        return !$lexer->getOption('noLocation', false) ? [
173
            'start'  => $startToken->getStart(),
174
            'end'    => $lexer->getLastToken()->getEnd(),
175
            'source' => $lexer->getSource(),
176
        ] : null;
177
    }
178
}
179