classScanner
final class Scanner
Splits a grammar file into the tokens Bison's own scanner produces.
This follows scan-gram.l: an identifier is looked past to see whether a colon or a bracketed name follows, a %name-prefix may be followed by =, a stray comma is whitespace, and everything after the second %% is one epilogue token.
Constants§
public const IDENTIFIER = '[.A-Za-z_][-.A-Za-z0-9_]*'Pattern of an identifier: a letter, underscore or dot, then also digits and hyphens.
Methods§
public function __construct(
private CodeReader $code = new \BisonParser\Scanner\CodeReader(),
private Escapes $escapes = new \BisonParser\Scanner\Escapes(),
)Parameters
$code | CodeReader | Reads host code |
$escapes | Escapes | Decodes literals |
Calls 2
- new CodeReader line 32
- new Escapes line 33
public function scan(string $source): list<Token>Tokenizes a grammar file.
Parameters
$source | string | Contents of the file |
Returns
list<Token> The tokens, ending with the end-of-file tokenThrows
SyntaxException When the file holds something the grammar language does not allowTest cases 57
ScannerTest::testScancallsScannerTest::testScanEndsWithoutAnEpiloguecallsScannerTest::testScanKeepsLineDirectivesAtTheStartOfALinecallsDeclarationParserTest::testCodescallsDeclarationParserTest::testCodesRejectsAMissingBlockcallsDeclarationParserTest::testDefinecallsDeclarationParserTest::testDefineRejectsAMissingVariablecallsDeclarationParserTest::testDirectivecallsDeclarationParserTest::testDirectiveReadsNtermAsNonterminalscallsDeclarationParserTest::testDirectiveRejectsAnOptionWithoutItsStringcallsDeclarationParserTest::testDirectiveRejectsANtermThatIsNotAnIdentifiercallsDeclarationParserTest::testParsecallsDeclarationParserTest::testParseReadsALineDirectivecallsDeclarationParserTest::testParseRejectsANonDeclarationcallsGrammarParserTest::testParsecallsGrammarParserTest::testParseKeepsLineDirectivesAmongTheRulescallsGrammarParserTest::testParseRejectsADeclarationAmongTheRulesWithoutASemicoloncallsGrammarParserTest::testParseRejectsARuleBeforeTheSectioncallsGrammarParserTest::testParseRejectsARulesSectionHoldingOnlyDeclarationscallsGrammarParserTest::testParseRejectsARulesSectionWithoutRulescallsGrammarParserTest::testParseRejectsAStrayTokenAmongTheRulescallsGrammarParserTest::testParseWithoutAnEpiloguecallsRuleParserTest::testActioncallsRuleParserTest::testActionRejectsATagWithoutCodecallsRuleParserTest::testAlternativecallsRuleParserTest::testCheckEmptyRejectsAMidrulePredicatecallsRuleParserTest::testCheckEmptyRejectsASymbolcallsRuleParserTest::testItemcallsRuleParserTest::testItemKeepsALineDirectivecallsRuleParserTest::testModifiercallsRuleParserTest::testModifierRejectsADprecWithoutANumbercallsRuleParserTest::testModifierRejectsAMergeWithoutATagcallsRuleParserTest::testParsecallsRuleParserTest::testParseAcceptsSemicolonsBetweenAlternativescallsRuleParserTest::testParseEndsAtTheNextRulecallsRuleParserTest::testParseRejectsANonRulecallsRuleParserTest::testSymbolTokencallsRuleParserTest::testSymbolTokenRejectsANonSymbolcallsSymbolListParserTest::testEntriescallsSymbolListParserTest::testEntriesPassOverALineBetweenEntriescallsSymbolListParserTest::testEntriesRejectASymbolOfAnotherKindcallsSymbolListParserTest::testEntriesRejectsAnEmptyListcallsSymbolListParserTest::testEntriesRejectsATagWithoutASymbolcallsSymbolListParserTest::testEntrycallsSymbolListParserTest::testNtermDeclarationscallsSymbolListParserTest::testNtermDeclarationsLeaveANumberUnreadcallsSymbolListParserTest::testNtermDeclarationsRejectACharacterLiteralcallsSymbolListParserTest::testNtermDeclarationsRejectAStringAliascallsSymbolListParserTest::testPassesLinecallsSymbolListParserTest::testPrecedenceDeclarationscallsSymbolListParserTest::testSymbolscallsSymbolListParserTest::testSymbolsRejectsAnEmptyListcallsSymbolListParserTest::testTargetscallsSymbolListParserTest::testTargetsRejectsAnEmptyListcallsSymbolListParserTest::testTokenDeclarationscallsSymbolListParserTest::testTokenDeclarationsRejectAStringStartingAnEntrycallsSymbolListParserTest::testTypeDeclarationscalls
Called from 1
Calls 12
- new Cursor line 48
- method-call Scanner::skipTrivia() line 52
- method-call Cursor::eof() line 53
- new Token line 54
- class-const TokenKind::End() line 54
- method-call Cursor::location() line 54
- method-call Scanner::next() line 58
- function-call
countline 61 - class-const TokenKind::Section() line 62
- class-const TokenKind::Epilogue() line 64
- method-call Cursor::take() line 64
- function-call
strlenline 64
public function skipTrivia(Cursor $cursor): voidSkips whitespace, comments, stray commas and #line directives.
Parameters
$cursor | Cursor | Cursor to advance |
Throws
SyntaxException When a comment never closesTest cases 2
Called from 5
- method-call BisonParser\Scanner\Scanner::scan() packages/bison-parser/src/Scanner/Scanner.php:52
- method-call BisonParser\Scanner\Scanner::identifier() packages/bison-parser/src/Scanner/Scanner.php:184
- method-call BisonParser\Scanner\Scanner::identifier() packages/bison-parser/src/Scanner/Scanner.php:188
- method-call BisonParser\Scanner\Scanner::bracketed() packages/bison-parser/src/Scanner/Scanner.php:212
- method-call BisonParser\Scanner\Scanner::bracketed() packages/bison-parser/src/Scanner/Scanner.php:217
Calls 6
- method-call Cursor::eof() line 81
- method-call Cursor::match() line 82
- method-call Cursor::startsWith() line 85
- method-call Cursor::location() line 86
- method-call Cursor::takeUntil() line 87
- static-call SyntaxException::unterminated() line 88
Reads the token, or the tokens, that begin at the cursor.
Parameters
$cursor | Cursor | Cursor positioned on a token |
Returns
list<Token> One token, or an identifier and its bracketed nameThrows
SyntaxException When no token begins here or it never closesTest cases 2
Called from 1
Calls 16
- method-call Cursor::location() line 108
- method-call Cursor::peek() line 109
- method-call Cursor::match() line 111
- new Token line 113
- class-const TokenKind::Line() line 113
- method-call Scanner::percent() line 117
- class-const TokenKind::Code() line 120
- method-call CodeReader::braced() line 120
- function-call
preg_matchline 122 - method-call Cursor::startsWith() line 122
- method-call Scanner::identifier() line 123
- function-call
ctype_digitline 125 - method-call Scanner::integer() line 126
- method-call Scanner::literal() line 128
- method-call Scanner::punctuation() line 128
- static-call SyntaxException::invalid() line 130
Reads what begins with a percent sign: a prologue, a section marker, a predicate or a directive.
Returns
Token The tokenThrows
SyntaxException When the directive is unknown or the code never closesTest cases 3
Called from 1
Calls 16
- method-call Cursor::startsWith() line 148
- new Token line 149
- class-const TokenKind::Prologue() line 149
- method-call CodeReader::prologue() line 149
- method-call Cursor::take() line 152
- class-const TokenKind::Section() line 154
- method-call Cursor::match() line 156
- class-const TokenKind::Predicate() line 157
- method-call CodeReader::braced() line 157
- class-const Scanner::IDENTIFIER() line 159
- static-call Directives::canonical() line 160
- function-call
substrline 160 - static-call SyntaxException::invalid() line 162
- function-call
in_arrayline 164 - class-const Directives::EQUAL_OPTIONAL() line 164
- class-const TokenKind::Directive() line 168
Reads an identifier and looks past it for a bracketed name and a colon.
Returns
list<Token> The identifier, followed by its bracketed name when one follows itThrows
SyntaxException When a bracketed name is malformedTest cases 1
Called from 1
Calls 8
- method-call Cursor::match() line 183
- class-const Scanner::IDENTIFIER() line 183
- method-call Scanner::skipTrivia() line 184
- method-call Cursor::peek() line 186
- method-call Scanner::bracketed() line 187
- class-const TokenKind::IdentifierColon() line 190
- class-const TokenKind::Identifier() line 190
- new Token line 191
Reads a bracketed name such as [left].
Parameters
$cursor | Cursor | Cursor positioned on the opening bracket |
Returns
Token The bracketed identifierThrows
SyntaxException When the brackets hold anything but one identifierTest cases 4
Called from 2
Calls 11
- method-call Cursor::location() line 210
- method-call Cursor::take() line 211
- method-call Scanner::skipTrivia() line 212
- method-call Cursor::match() line 213
- class-const Scanner::IDENTIFIER() line 213
- static-call SyntaxException::invalid() line 215
- method-call Cursor::peek() line 218
- static-call SyntaxException::unexpected() line 219
- method-call Cursor::eof() line 219
- new Token line 223
- class-const TokenKind::BracketedIdentifier() line 223
Reads a decimal or hexadecimal integer.
Returns
Token The integer, its text in decimalThrows
SyntaxException When letters follow the digitsTest cases 6
ScannerTest::testIntegercallsScannerTest::testIntegerRejectsADecimalOutOfRangecallsScannerTest::testIntegerRejectsADecimalTooLongToReadcallsScannerTest::testIntegerRejectsAHexadecimalOutOfRangecallsScannerTest::testIntegerRejectsAHexadecimalTooLongToReadcallsScannerTest::testIntegerRejectsAnIdentifierStartingWithADigitcalls
Called from 1
Calls 11
- method-call Cursor::match() line 238
- method-call Cursor::lookingAt() line 239
- static-call SyntaxException::invalid() line 240
- function-call
str_starts_withline 242 - function-call
strtolowerline 242 - function-call
ltrimline 243 - function-call
substrline 243 - function-call
strlenline 244 - function-call
hexdecline 244 - new Token line 249
- class-const TokenKind::Integer() line 249
Reads a character literal, a string, a translatable string, or a tag.
Returns
Token|null The token, or null when no literal begins hereThrows
SyntaxException When the literal never closes or a character literal is not one byteTest cases 5
Called from 1
Calls 13
- method-call Cursor::peek() line 264
- method-call Cursor::match() line 266
- static-call SyntaxException::unterminated() line 268
- method-call Escapes::decode() line 270
- function-call
substrline 270 - static-call SyntaxException::invalid() line 272
- function-call
strlenline 274 - new Token line 278
- class-const TokenKind::CharLiteral() line 278
- method-call Cursor::startsWith() line 280
- class-const TokenKind::TranslatableString() line 288
- class-const TokenKind::String() line 288
- method-call Scanner::tag() line 291
Reads a tag, which may nest angle brackets as a C++ template does.
Returns
Token The tagThrows
SyntaxException When the tag never closesTest cases 2
Called from 1
Calls 10
- method-call Cursor::startsWith() line 309
- method-call Cursor::take() line 310
- new Token line 312
- class-const TokenKind::TagAny() line 312
- class-const TokenKind::TagNone() line 317
- method-call Cursor::eof() line 322
- method-call Cursor::match() line 323
- class-const TokenKind::Tag() line 326
- function-call
strlenline 330 - static-call SyntaxException::unterminated() line 335
Reads one punctuation byte.
Returns
Token|null The token, or null when the byte is not punctuation the language hasThrows
SyntaxException When a bracketed name is malformedTest cases 1
Called from 1
Calls 8
- method-call Cursor::peek() line 350
- method-call Scanner::bracketed() line 352
- class-const TokenKind::Colon() line 355
- class-const TokenKind::Equal() line 356
- class-const TokenKind::Pipe() line 357
- class-const TokenKind::Semicolon() line 358
- method-call Cursor::take() line 364
- new Token line 366
Private surface 2§
Implementation details, listed for orientation only.
private CodeReader $code = new \BisonParser\Scanner\CodeReader()private Escapes $escapes = new \BisonParser\Scanner\Escapes()Test cases 86§
Test cases that cover or call this symbol, from the coverage report and from the analyzed test sources.
Dedicated tests 29
ScannerTest::testBracketedcallsScannerTest::testBracketedRejectsAMissingClosercallsScannerTest::testBracketedRejectsAMissingNamecallsScannerTest::testBracketedRejectsTheEndOfTheFilecallsScannerTest::testIdentifiercallsScannerTest::testIntegercallsScannerTest::testIntegerRejectsADecimalOutOfRangecallsScannerTest::testIntegerRejectsADecimalTooLongToReadcallsScannerTest::testIntegerRejectsAHexadecimalOutOfRangecallsScannerTest::testIntegerRejectsAHexadecimalTooLongToReadcallsScannerTest::testIntegerRejectsAnIdentifierStartingWithADigitcallsScannerTest::testLiteralcallsScannerTest::testLiteralRejectsAnEmptyCharactercallsScannerTest::testLiteralRejectsAnUnterminatedCharactercallsScannerTest::testLiteralRejectsAnUnterminatedStringcallsScannerTest::testLiteralRejectsAWideCharactercallsScannerTest::testNextcallsScannerTest::testNextRejectsAnInvalidCharactercallsScannerTest::testPercentcallsScannerTest::testPercentRejectsABarePercentSigncallsScannerTest::testPercentRejectsAnUnknownDirectivecallsScannerTest::testPunctuationcallsScannerTest::testScancallsScannerTest::testScanEndsWithoutAnEpiloguecallsScannerTest::testScanKeepsLineDirectivesAtTheStartOfALinecallsScannerTest::testSkipTriviacallsScannerTest::testSkipTriviaRejectsAnUnterminatedCommentcallsScannerTest::testTagcallsScannerTest::testTagRejectsAnUnterminatedTagcalls
Other tests reaching this symbol 57
BisonGrammarCompilerTestcallsBisonGrammarReaderTestcallsParserTestcallsDeclarationParserTest::testCodescallsDeclarationParserTest::testCodesRejectsAMissingBlockcallsDeclarationParserTest::testDefinecallsDeclarationParserTest::testDefineRejectsAMissingVariablecallsDeclarationParserTest::testDirectivecallsDeclarationParserTest::testDirectiveReadsNtermAsNonterminalscallsDeclarationParserTest::testDirectiveRejectsAnOptionWithoutItsStringcallsDeclarationParserTest::testDirectiveRejectsANtermThatIsNotAnIdentifiercallsDeclarationParserTest::testParsecallsDeclarationParserTest::testParseReadsALineDirectivecallsDeclarationParserTest::testParseRejectsANonDeclarationcallsGrammarParserTest::testParsecallsGrammarParserTest::testParseKeepsLineDirectivesAmongTheRulescallsGrammarParserTest::testParseRejectsADeclarationAmongTheRulesWithoutASemicoloncallsGrammarParserTest::testParseRejectsARuleBeforeTheSectioncallsGrammarParserTest::testParseRejectsARulesSectionHoldingOnlyDeclarationscallsGrammarParserTest::testParseRejectsARulesSectionWithoutRulescallsGrammarParserTest::testParseRejectsAStrayTokenAmongTheRulescallsGrammarParserTest::testParseWithoutAnEpiloguecallsRuleParserTest::testActioncallsRuleParserTest::testActionRejectsATagWithoutCodecallsRuleParserTest::testAlternativecallsRuleParserTest::testCheckEmptyRejectsAMidrulePredicatecallsRuleParserTest::testCheckEmptyRejectsASymbolcallsRuleParserTest::testItemcallsRuleParserTest::testItemKeepsALineDirectivecallsRuleParserTest::testModifiercallsRuleParserTest::testModifierRejectsADprecWithoutANumbercallsRuleParserTest::testModifierRejectsAMergeWithoutATagcallsRuleParserTest::testParsecallsRuleParserTest::testParseAcceptsSemicolonsBetweenAlternativescallsRuleParserTest::testParseEndsAtTheNextRulecallsRuleParserTest::testParseRejectsANonRulecallsRuleParserTest::testSymbolTokencallsRuleParserTest::testSymbolTokenRejectsANonSymbolcallsSymbolListParserTest::testEntriescallsSymbolListParserTest::testEntriesPassOverALineBetweenEntriescallsSymbolListParserTest::testEntriesRejectASymbolOfAnotherKindcallsSymbolListParserTest::testEntriesRejectsAnEmptyListcallsSymbolListParserTest::testEntriesRejectsATagWithoutASymbolcallsSymbolListParserTest::testEntrycallsSymbolListParserTest::testNtermDeclarationscallsSymbolListParserTest::testNtermDeclarationsLeaveANumberUnreadcallsSymbolListParserTest::testNtermDeclarationsRejectACharacterLiteralcallsSymbolListParserTest::testNtermDeclarationsRejectAStringAliascallsSymbolListParserTest::testPassesLinecallsSymbolListParserTest::testPrecedenceDeclarationscallsSymbolListParserTest::testSymbolscallsSymbolListParserTest::testSymbolsRejectsAnEmptyListcallsSymbolListParserTest::testTargetscallsSymbolListParserTest::testTargetsRejectsAnEmptyListcallsSymbolListParserTest::testTokenDeclarationscallsSymbolListParserTest::testTokenDeclarationsRejectAStringStartingAnEntrycallsSymbolListParserTest::testTypeDeclarationscalls