|
| 1 | +#lang scribble/manual |
| 2 | +@(require (for-label racket)) |
| 3 | + |
| 4 | +@title{Parenthesis Matching} |
| 5 | + |
| 6 | +@defmodule[syntax-color/paren-tree] |
| 7 | + |
| 8 | +Parenthesis matching code built on top of @racket[token-tree%]. |
| 9 | + |
| 10 | +@defclass[ |
| 11 | + paren-tree% object% () |
| 12 | + |
| 13 | + @defconstructor/auto-super[([matches (listof (list/c symbol? symbol?))])]{ |
| 14 | + Creates a @racket[paren-tree%] object that treats @racket[(map car matches)] |
| 15 | + as open parens and @racket[(map cadr matches)] as close parens, |
| 16 | + where each element of @racket[matches] is a matching pair of parens. |
| 17 | + |
| 18 | + Each paren tree tracks a sequence of tokens (added with @method[paren-tree% add-token]) |
| 19 | + and can respond to queries about the location of a paren that matches a paren |
| 20 | + at some specific location via @method[paren-tree% match-forward] and |
| 21 | + @method[paren-tree% match-backward]. The @racket[paren-tree%] also supports a notion of invisible |
| 22 | + parentheses that take up no space, where the opens exist only at the start of a token |
| 23 | + and the closes exist only at the end of a token. |
| 24 | + } |
| 25 | + |
| 26 | + @defmethod[(add-token [type (or/c #f symbol?)] |
| 27 | + [length natural?] |
| 28 | + [#:invisible-opens invisible-opens natural? 0] |
| 29 | + [#:invisible-closes invisible-closes natural? 0]) |
| 30 | + void?]{ |
| 31 | + |
| 32 | + Adds one token to the end of the current tree. If |
| 33 | + @racket[type] is a symbol, it is expected to be one of the |
| 34 | + symbols in @racket[matches]. If it is @racket[#f], then |
| 35 | + there are no visible parenthese in this token. The |
| 36 | + @racket[invisible-opens] and @racket[invsible-closes] |
| 37 | + indicate how many of each there are on this token (note that |
| 38 | + the invisible opens all exist at the start of the token and |
| 39 | + the invisible closes all exist at the end of the token). |
| 40 | + } |
| 41 | + |
| 42 | + @defmethod[(match-forward [pos natural?] |
| 43 | + [#:invisible invisible (or/c #f natural? 'all) #f]) |
| 44 | + (values (or/c natural? #f) (or/c natural? #f) (or/c natural? #f))]{ |
| 45 | + Determines if there is a match for the paren at @racket[pos]. |
| 46 | + |
| 47 | + If @racket[invisible] is @racket[#f], then the invisible |
| 48 | + parens are ignored and the match considers only the |
| 49 | + parentheses that were explicit in the @racket[_token] |
| 50 | + argument to @method[paren-tree% add-token]. |
| 51 | + |
| 52 | + If @racket[invisible] is a natural number, then the |
| 53 | + matching starts outside of that many invisible parens. For |
| 54 | + example, if there are two invisible open parenthes on the |
| 55 | + token at @racket[pos], then passing @racket[1] as |
| 56 | + @racket[invisible] will find the match to only the inner |
| 57 | + invisible paren. If it is @racket[2], it will find the match |
| 58 | + to the outer invisible paren. If @racket[invisible] is |
| 59 | + @racket['all], then it is the same as passing the total |
| 60 | + number of invisible parens that are on the token at |
| 61 | + @racket[pos]. |
| 62 | + |
| 63 | + The first return is the starting position of the open paren |
| 64 | + The second return is the position of the matching close |
| 65 | + paren. If the third return is #f, then the first two returns |
| 66 | + represent a real match. If the third return is a number, it |
| 67 | + is the maximum position in the tree that was searched. If |
| 68 | + the third result indicates an error, the first two results |
| 69 | + give the starting and stopping positions for error |
| 70 | + highlighting. If all three are @racket[#f], then there was |
| 71 | + no tree to search, or the position did not immediately |
| 72 | + precede an open. |
| 73 | + } |
| 74 | + |
| 75 | + @defmethod[(match-backward [pos natural?] |
| 76 | + [#:invisible invisible (or/c #f natural? 'all) #f]) |
| 77 | + (values (or/c natural? #f) (or/c natural? #f) (or/c natural? #f))]{ |
| 78 | + |
| 79 | + Like @racket[paren-tree% match-forward], except matches |
| 80 | + backwards from the given paren. |
| 81 | + |
| 82 | + The matching goes backwards from @racket[pos] to the |
| 83 | + matching paren; accordingly, the count of invisibles starts |
| 84 | + from the close parens and goes through the opens (unlike |
| 85 | + @racket[paren-tree% match-forward] which starts with the |
| 86 | + opens and goes through the closes). |
| 87 | + |
| 88 | + The results are, however, identical to |
| 89 | + @racket[paren-tree% match-forward]. So, if the match is |
| 90 | + successful, the first result is the location of the open |
| 91 | + paren and the second is the close. |
| 92 | + } |
| 93 | + |
| 94 | + @defmethod[(split-tree [pos natural?]) void?]{ |
| 95 | + Splits the tree at @racket[pos], which must not be in the middle of a token. |
| 96 | + Everything following @racket[pos] is marked as invalid. |
| 97 | + } |
| 98 | + |
| 99 | + @defmethod[(merge-tree [num-to-keep natural?]) void?]{ |
| 100 | + Makes the @racket[num-to-keep] last positions that have been marked |
| 101 | + invalid valid again. |
| 102 | + } |
| 103 | + @defmethod[(truncate [pos natural?]) void?]{ |
| 104 | + Removes the tokens after @racket[pos]. |
| 105 | + } |
| 106 | + |
| 107 | + @defmethod[(is-open-pos? [pos natural?]) (or/c #f symbol?)]{ |
| 108 | + Returns @racket[#f] if the position does not have a visible paren. Returns the corresponding |
| 109 | + close if it does have an open. |
| 110 | + } |
| 111 | + @defmethod[(is-close-pos? [pos natural?]) (or/c #f symbol?)]{ |
| 112 | + Returns @racket[#f] if the position does not have a visible paren. Returns the corresponding |
| 113 | + open if it does have a close. |
| 114 | + } |
| 115 | + |
| 116 | + @defmethod[(get-invisible-count [pos natural?]) (values natural? natural?)]{ |
| 117 | + Returns the number of invisible opens and invisible closes at @racket[pos]. |
| 118 | + } |
| 119 | + ] |
| 120 | + |
| 121 | + |
| 122 | + |
0 commit comments