docs: improve external scanner details and fix conflicting tokens details
* Removed convention notes introduced in #1947 due to:
* It doesn't so strict for bindings and they may need to obey to some
target language conventions.
* For language grammars there is a note that states the same in the
`Creating Parsers` section.
* Removed `External Scanning` item introduced in 87a0517 commit
originated from #1612 due to unclear consistency with other
5 original statements. There is a similar explanation in the
`Other External Scanner Details` section.
This commit is contained in:
parent
613382c70a
commit
3c806913d8
2 changed files with 14 additions and 24 deletions
|
|
@ -30,8 +30,6 @@ There are currently bindings that allow Tree-sitter to be used from the followin
|
|||
* [Rust](https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_rust)
|
||||
* [Swift](https://github.com/ChimeHQ/SwiftTreeSitter)
|
||||
|
||||
By convention, bindings are named with the language first, eg. ruby-tree-sitter.
|
||||
|
||||
### Parsers
|
||||
|
||||
* [Ada](https://github.com/briot/tree-sitter-ada)
|
||||
|
|
@ -148,8 +146,6 @@ By convention, bindings are named with the language first, eg. ruby-tree-sitter.
|
|||
* [YANG](https://github.com/Hubro/tree-sitter-yang)
|
||||
* [Zig](https://github.com/maxxnino/tree-sitter-zig)
|
||||
|
||||
By convention, parsers are named with the language last, eg. tree-sitter-ruby.
|
||||
|
||||
### Talks on Tree-sitter
|
||||
|
||||
* [Strange Loop 2018](https://www.thestrangeloop.com/2018/tree-sitter---a-new-parsing-system-for-programming-tools.html)
|
||||
|
|
|
|||
|
|
@ -527,27 +527,21 @@ Tree-sitter's parsing process is divided into two phases: parsing (which is desc
|
|||
|
||||
Grammars often contain multiple tokens that can match the same characters. For example, a grammar might contain the tokens (`"if"` and `/[a-z]+/`). Tree-sitter differentiates between these conflicting tokens in a few ways.
|
||||
|
||||
1. **External Scanning** - If your grammar has an external scanner and one or more tokens in your `externals` array are valid at the current location, your external scanner will always be called first to determine whether those tokens are present.
|
||||
1. **Context-aware Lexing** - Tree-sitter performs lexing on-demand, during the parsing process. At any given position in a source document, the lexer only tries to recognize tokens that are *valid* at that position in the document.
|
||||
|
||||
1. **Context-Aware Lexing** - Tree-sitter performs lexing on-demand, during the parsing process. At any given position in a source document, the lexer only tries to recognize tokens that are *valid* at that position in the document.
|
||||
2. **Lexical Precedence** - When the precedence functions described [above](#the-grammar-dsl) are used *within* the `token` function, the given explicit precedence values serve as instructions to the lexer. If there are two valid tokens that match the characters at a given position in the document, Tree-sitter will select the one with the higher precedence.
|
||||
|
||||
1. **Explicit Lexical Precedence** - When the precedence functions described [above](#the-grammar-dsl) are used within the `token` function like `token(prec(N, ...))`, the given precedence values serve as instructions to the lexer. If there are two valid tokens that match the characters at a given position in the document, Tree-sitter will select the one with the higher precedence.
|
||||
3. **Match Length** - If multiple valid tokens with the same precedence match the characters at a given position in a document, Tree-sitter will select the token that matches the [longest sequence of characters][longest-match].
|
||||
|
||||
1. **Match Length** - If multiple valid tokens with the same precedence match the characters at a given position in a document, Tree-sitter will select the token that matches the [longest sequence of characters][longest-match].
|
||||
4. **Match Specificity** - If there are two valid tokens with the same precedence and which both match the same number of characters, Tree-sitter will prefer a token that is specified in the grammar as a `String` over a token specified as a `RegExp`.
|
||||
|
||||
1. **Match Specificity** - If there are two valid tokens with the same precedence and which both match the same number of characters, Tree-sitter will prefer a token that is specified in the grammar as a `String` over a token specified as a `RegExp`.
|
||||
5. **Rule Order** - If none of the above criteria can be used to select one token over another, Tree-sitter will prefer the token that appears earlier in the grammar.
|
||||
|
||||
1. **Rule Order** - If none of the above criteria can be used to select one token over another, Tree-sitter will prefer the token that appears earlier in the grammar.
|
||||
If there is an external scanner it may have [an additional impact](#other-external-scanner-details) over regular tokens defined in the grammar.
|
||||
|
||||
### Lexical Precedence vs. Parse Precedence
|
||||
|
||||
One common mistake involves not distinguishing lexical precedence from parse precedence.
|
||||
Parse precedence determines which rule is chosen to interpret a given sequence of tokens.
|
||||
Lexical precedence determines which token is chosen to interpret a given section of text.
|
||||
It is a lower-level operation that is done first.
|
||||
The above list fully capture tree-sitter's lexical precedence rules, and you will probably refer back to this section of the documentation more often than any other.
|
||||
Most of the time when you really get stuck, you're dealing with a lexical precedence problem.
|
||||
Pay particular attention to the difference in meaning between using `prec` inside the `token` function versus outside of it.
|
||||
One common mistake involves not distinguishing *lexical precedence* from *parse precedence*. Parse precedence determines which rule is chosen to interpret a given sequence of tokens. *Lexical precedence* determines which token is chosen to interpret at a given position of text and it is a lower-level operation that is done first. The above list fully captures Tree-sitter's lexical precedence rules, and you will probably refer back to this section of the documentation more often than any other. Most of the time when you really get stuck, you're dealing with a lexical precedence problem. Pay particular attention to the difference in meaning between using `prec` inside of the `token` function versus outside of it. The *lexical precedence* syntax is `token(prec(N, ...))`.
|
||||
|
||||
### Keywords
|
||||
|
||||
|
|
@ -737,15 +731,15 @@ if (valid_symbols[INDENT] || valid_symbol[DEDENT]) {
|
|||
|
||||
#### Other External Scanner Details
|
||||
|
||||
If a token in your `externals` array is valid at the current position in the parse, your external scanner will be called first before anything else is done.
|
||||
This means your external scanner functions as a powerful override of tree-sitter's lexing behavior, and can be used to solve problems that can't be cracked with ordinary lexical, parse, or dynamic precedence.
|
||||
If a token in the `externals` array is valid at a given position in the parse, the external scanner will be called first before anything else is done. This means the external scanner functions as a powerful override of Tree-sitter's lexing behavior, and can be used to solve problems that can't be cracked with ordinary lexical, parse, or dynamic precedence.
|
||||
|
||||
If a syntax error is encountered during regular parsing, tree-sitter's first action during error recovery will be to call your external scanner's `scan` function with all tokens marked valid.
|
||||
Your scanner should detect this case and handle it appropriately.
|
||||
One simple method of detection is to add an unused token to the end of your `externals` array, for example `externals: $ => [$.token1, $.token2, $.error_sentinel]`, then check whether that token is marked valid to determine whether tree-sitter is in error correction mode.
|
||||
If a syntax error is encountered during regular parsing, Tree-sitter's first action during error recovery will be to call the external scanner's `scan` function with all tokens marked valid. The scanner should detect this case and handle it appropriately. One simple method of detection is to add an unused token to the end of the `externals` array, for example `externals: $ => [$.token1, $.token2, $.error_sentinel]`, then check whether that token is marked valid to determine whether Tree-sitter is in error correction mode.
|
||||
|
||||
If you put terminal keywords in your `externals` array, for example `externals: $ => ['if', 'then', 'else']`, then any time those terminals are present in your grammar they will be tokenized by your external scanner.
|
||||
It is equivalent to writing `externals: [$.if_keyword, $.then_keyword, $.else_keyword]` then using `alias($.if_keyword, 'if')` in your grammar.
|
||||
If you put terminal keywords in the `externals` array, for example `externals: $ => ['if', 'then', 'else']`, then any time those terminals are present in the grammar they will be tokenized by the external scanner. It is similar to writing `externals: [$.if_keyword, $.then_keyword, $.else_keyword]` then using `alias($.if_keyword, 'if')` in the grammar.
|
||||
|
||||
If in the `externals` array use literal keywords then lexing works in two steps, the external scanner will be called first and if it sets a resulting token and returns `true` then the token considered as recognized and Tree-sitter moves to a next token. But the external scanner may return `false` and in this case Tree-sitter fallbacks to the internal lexing mechanism.
|
||||
|
||||
In case of some keywords defined in the `externals` array in a rule referencing form like `$.if_keyword` and there is no additional definition of that rule in the grammar rules, e.g., `if_keyword: $ => 'if'` then fallback to the internal lexer isn't possible because Tree-sitter doesn't know the actual keyword and it's fully the external scanner resposibilty to recognize such tokens.
|
||||
|
||||
External scanners are a common cause of infinite loops.
|
||||
Be very careful when emitting zero-width tokens from your external scanner, and if you consume characters in a loop be sure use the `eof` function to check whether you are at the end of the file.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue