]> git.madduck.net Git - etc/vim.git/blob - autoload/explainpat.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

566273266fc981d00b81d37c22cb182946766377
[etc/vim.git] / autoload / explainpat.vim
1 " File:         explainpat.vim
2 " Created:      2011 Nov 02
3 " Last Change:  2017 Oct 20
4 " Version:      0.7
5 " Author:       Andy Wokula <anwoku@yahoo.de>
6 " License:      Vim License, see :h license
7
8 " Implements :ExplainPattern [pattern]
9
10 " History: "{{{
11 " 2013 Jun 21   AND/OR text is confusing, removed
12 " 2013 Apr 20   ...
13 "}}}
14
15 " TODO {{{
16 " - add something like "(empty) ... match everywhere" ... example: '\v(&&|str)'
17 "   Pattern: \v(&&|str)
18 "   Magic Pattern: \(\&\&\|str\)
19 "     \(         start of first capturing group
20 "     |                  (empty) match everywhere
21 "     |   \&         AND
22 "     |                  (empty) match everywhere
23 "     |   \&         AND
24 "     |                  (empty) match everywhere
25 "     | \|         OR
26 "     |   str        literal string (3 atom(s))
27 "     \)         end of group
28 " - more testing, completeness check
29 " ? detailed collections
30 " ? literal string: also print the unescaped magic items
31 " ? literal string: show leading/trailing spaces
32 "
33 "}}}
34
35 " Init Folklore {{{
36 let s:cpo_save = &cpo
37 set cpo&vim
38 let g:explainpat#loaded = 1
39 "}}}
40
41 func! explainpat#ExplainPattern(cmd_arg, ...) "{{{
42     " {a:1}     alternative help printer object (caution, no sanity check)
43     "           (for test running)
44     if a:cmd_arg == ""
45         " let pattern_str = nwo#vis#Get()
46         echo "(usage) :ExplainPattern [{register} | {pattern}]"
47         return
48     elseif strlen(a:cmd_arg) == 1 && a:cmd_arg =~ '["@0-9a-z\-:.*+/]'
49         echo 'Register:' a:cmd_arg
50         let pattern_str = getreg(a:cmd_arg)
51     else
52         let pattern_str = a:cmd_arg
53     endif
54
55     echo printf('Pattern: %s', pattern_str)
56     let magicpat = nwo#magic#MakeMagic(pattern_str)
57     if magicpat !=# pattern_str
58         echo printf('Magic Pattern: %s', magicpat)
59     endif
60
61     " we need state:
62     " set flag when in `\%[ ... ]' (optionally matched atoms):
63     let s:in_opt_atoms = 0
64     " counter for `\(':
65     let s:capture_group_nr = 0
66     " >=1 at pos 0 or after '\|', '\&', '\(', '\%(' or '\n'; else 0 or less:
67     let s:at_begin_of_pat = 1
68
69     let hulit = a:0>=1 && type(a:1)==s:DICT ? a:1 : s:NewHelpPrinter()
70     call hulit.AddIndent('  ')
71     let bull = s:NewTokenBiter(magicpat)
72     while !bull.AtEnd()
73         let item = bull.Bite(s:magic_item_pattern)
74         if item != ''
75             let Doc = get(s:doc, item, '')
76             if empty(Doc)
77                 call hulit.AddLiteral(item)
78             elseif type(Doc) == s:STRING
79                 call hulit.Print(item, Doc)
80             elseif type(Doc) == s:FUNCREF
81                 call call(Doc, [bull, hulit, item])
82             elseif type(Doc) == s:LIST
83                 call call(Doc[0], [bull, hulit, item, Doc[1]])
84             endif
85             let s:at_begin_of_pat -= 1
86         else
87             echoerr printf('ExplainPattern: cannot parse "%s"', bull.Rest())
88             break
89         endif
90         unlet Doc
91     endwhile
92     call hulit.FlushLiterals()
93 endfunc "}}}
94
95 " s: types {{{
96 let s:STRING = type("")
97 let s:DICT   = type({})
98 let s:FUNCREF = type(function("tr"))
99 let s:LIST = type([])
100 " }}}
101
102 let s:magic_item_pattern = '\C^\%(\\\%(@<\|%#=\|%[dxouU[(^$V#<>]\=\|z[1-9se(]\|@[>=!]\|_[[^$.]\=\|.\)\|.\)'
103
104 let s:doc = {} " {{{
105 " this is all the help data ...
106 "   strings, funcrefs and intermixed s:DocFoo() functions
107 " strongly depends on s:magic_item_pattern
108
109 func! s:DocOrBranch(bull, hulit, item) "{{{
110     call a:hulit.RemIndent()
111     call a:hulit.Print(a:item, "OR")
112     call a:hulit.AddIndent('  ')
113     let s:at_begin_of_pat = 2
114 endfunc "}}}
115
116 let s:doc['\|'] = function("s:DocOrBranch")
117
118 func! s:DocBeginOfPat(bull, hulit, item, msg) "{{{
119     call a:hulit.Print(a:item, a:msg)
120     let s:at_begin_of_pat = 2
121 endfunc "}}}
122
123 let s:doc['\&'] = [function("s:DocBeginOfPat"), "AND"]
124
125 let s:ord = split('n first second third fourth fifth sixth seventh eighth ninth')
126
127 func! s:DocGroupStart(bull, hulit, item) "{{{
128     if a:item == '\%('
129         call a:hulit.Print(a:item, "start of non-capturing group")
130     elseif a:item == '\('
131         let s:capture_group_nr += 1
132         call a:hulit.Print(a:item, printf("start of %s capturing group", get(s:ord, s:capture_group_nr, '(invalid)')))
133     else " a:item == '\z('
134         call a:hulit.Print(a:item, 'start of "external" group (only usable in :syn-region)')
135     endif
136     call a:hulit.AddIndent('| ', '  ')
137     let s:at_begin_of_pat = 2
138 endfunc "}}}
139 func! s:DocGroupEnd(bull, hulit, item) "{{{
140     call a:hulit.RemIndent(2)
141     call a:hulit.Print(a:item, "end of group")
142 endfunc "}}}
143
144 let s:doc['\('] = function("s:DocGroupStart")
145 let s:doc['\%('] = function("s:DocGroupStart")
146 let s:doc['\)'] =  function("s:DocGroupEnd")
147 " let s:doc['\z('] = "only in syntax scripts"
148 let s:doc['\z('] = function("s:DocGroupStart")
149
150 func! s:DocStar(bull, hulit, item) "{{{
151     if s:at_begin_of_pat >= 1
152         " call a:hulit.Print(a:item, "(at begin of pattern) literal `*'")
153         call a:hulit.AddLiteral(a:item)
154     else
155         call a:hulit.Print(a:item, "(multi) zero or more of the preceding atom")
156     endif
157 endfunc "}}}
158
159 " let s:doc['*'] = "(multi) zero or more of the preceding atom"
160 let s:doc['*'] = function("s:DocStar")
161
162 let s:doc['\+'] = "(multi) one or more of the preceding atom"
163 let s:doc['\='] = "(multi) zero or one of the preceding atom"
164 let s:doc['\?'] = "(multi) zero or one of the preceding atom"
165 " let s:doc['\{'] = "(multi) N to M, greedy"
166 " let s:doc['\{-'] = "(multi) N to M, non-greedy"
167
168 func! s:DocBraceMulti(bull, hulit, item) "{{{
169     let rest = a:bull.Bite('^-\=\d*\%(,\d*\)\=\\\=}')
170     if rest != ""
171         if rest == '-}'
172             call a:hulit.Print(a:item. rest, "non-greedy version of `*'")
173         elseif rest =~ '^-'
174             call a:hulit.Print(a:item. rest, "(multi) N to M, non-greedy")
175         else
176             call a:hulit.Print(a:item. rest, "(multi) N to M, greedy")
177         endif
178     else
179         call a:hulit.Print(a:item, "(invalid) incomplete `\\{...}' item")
180     endif
181 endfunc "}}}
182
183 let s:doc['\{'] = function("s:DocBraceMulti")
184
185 let s:doc['\@>'] = "(multi) match preceding atom like a full pattern"
186 let s:doc['\@='] = "(assertion) require match for preceding atom"
187 let s:doc['\@!'] = "(assertion) forbid match for preceding atom"
188
189 func! s:DocBefore(bull, hulit, item) "{{{
190     let rest = a:bull.Bite('^[=!]')
191     if rest == "="
192         call a:hulit.Print(a:item.rest, "(assertion) require match for preceding atom to the left")
193     elseif rest == "!"
194         call a:hulit.Print(a:item.rest, "(assertion) forbid match for preceding atom to the left")
195     else
196         call a:hulit.Print(a:item.rest, "(invalid) `\\@<' must be followed by `=' or `!'")
197     endif
198 endfunc "}}}
199
200 let s:doc['\@<'] = function("s:DocBefore")
201
202 func! s:DocCircumFlex(bull, hulit, item) "{{{
203     if s:at_begin_of_pat >= 1
204         call a:hulit.Print(a:item, "(assertion) require match at start of line")
205         " after `^' is not at begin of pattern ... handle special case `^*' here:
206         if a:bull.Bite('^\*') == "*"
207             call a:hulit.AddLiteral("*")
208         endif
209     else
210         " call a:hulit.Print(a:item, "(not at begin of pattern) literal `^'")
211         call a:hulit.AddLiteral(a:item)
212     endif
213 endfunc "}}}
214
215 " let s:doc['^'] = "(assertion) require match at start of line"
216 let s:doc['^'] = function("s:DocCircumFlex")
217
218 let s:doc['\_^'] = "(assertion) like `^', allowed anywhere in the pattern"
219
220 func! s:DocDollar(bull, hulit, item) "{{{
221     if a:bull.Rest() =~ '^$\|^\\[&|)n]'
222         call a:hulit.Print(a:item, "(assertion) require match at end of line")
223     else
224         call a:hulit.AddLiteral(a:item)
225     endif
226 endfunc "}}}
227
228 " let s:doc['$'] = "(assertion) require match at end of line"
229 let s:doc['$'] = function("s:DocDollar")
230
231 let s:doc['\_$'] = "(assertion) like `$', allowed anywhere in the pattern"
232 let s:doc['.'] = "match any character"
233 let s:doc['\_.'] = "match any character or newline"
234
235 func! s:DocUnderscore(bull, hulit, item) "{{{
236     let cclass = a:bull.Bite('^\a')
237     if cclass != ''
238         let cclass_doc = get(s:doc, '\'. cclass, '(invalid character class)')
239         call a:hulit.Print(a:item. cclass, printf('%s or end-of-line', cclass_doc))
240     else
241         call a:hulit.Print(a:item, "(invalid) `\\_' should be followed by a letter or `[...]'")
242         " echoerr printf('ExplainPattern: cannot parse %s', a:item. matchstr(a:bull.Rest(), '.'))
243     endif
244 endfunc "}}}
245
246 let s:doc['\_'] = function("s:DocUnderscore")
247 let s:doc['\<'] = "(assertion) require match at begin of word, :h word"
248 let s:doc['\>'] = "(assertion) require match at end of word, :h word"
249 let s:doc['\zs'] = "set begin of match here"
250 let s:doc['\ze'] = "set end of match here"
251 let s:doc['\%^'] = "(assertion) match at begin of buffer"
252 let s:doc['\%$'] = "(assertion) match at end of buffer"
253 let s:doc['\%V'] = "(assertion) match within the Visual area"
254 let s:doc['\%#'] = "(assertion) match with cursor position"
255
256 func! s:DocRegexEngine(bull, hulit, item) "{{{
257     let engine = a:bull.Bite('^[012]')
258     if engine == "0"
259         call a:hulit.Print(a:item.engine, 'Force automatic selection of the regexp engine (since v7.3.970).')
260     elseif engine == "1" 
261         call a:hulit.Print(a:item.engine, 'Force using the old engine (since v7.3.970).')
262     elseif engine == "2"
263         call a:hulit.Print(a:item.engine, 'Force using the NFA engine (since v7.3.970).')
264     else
265         call a:hulit.Print(a:item, '(invalid) \%#= can only be followed by 0, 1, or 2')
266     endif
267 endfunc "}}}
268
269 let s:doc['\%#='] = function("s:DocRegexEngine")
270
271 " \%'m   \%<'m   \%>'m
272 " \%23l  \%<23l  \%>23l
273 " \%23c  \%<23c  \%>23c
274 " \%23v  \%<23v  \%>23v
275 " backslash percent at/before/after
276 func! s:DocBspercAt(bull, hulit, item) "{{{
277     let rest = a:bull.Bite('^\%(''.\|\d\+[lvc]\)\C')
278     if rest[0] == "'"
279         call a:hulit.Print(a:item.rest, "(assertion) match with position of mark ". rest[1])
280     else
281         let number = rest[:-2]
282         let type = rest[-1:]
283         if type ==# "l"
284             call a:hulit.Print(a:item.rest, "match in line ". number)
285         elseif type ==# "c"
286             call a:hulit.Print(a:item.rest, "match in column ". number)
287         elseif type ==# "v"
288             call a:hulit.Print(a:item.rest, "match in virtual column ". number)
289         else
290             call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%' item")
291             " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
292         endif
293     endif
294 endfunc "}}}
295 func! s:DocBspercBefore(bull, hulit, item) "{{{
296     let rest = a:bull.Bite('^\%(''.\|\d\+[lvc]\)\C')
297     if rest[0] == "'"
298         call a:hulit.Print(a:item.rest, "(assertion) match before position of mark ". rest[1])
299     else
300         let number = rest[:-2]
301         let type = rest[-1:]
302         if type ==# "l"
303             call a:hulit.Print(a:item.rest, printf("match above line %d (towards start of buffer)", number))
304         elseif type ==# "c"
305             call a:hulit.Print(a:item.rest, "match before column ". number)
306         elseif type ==# "v"
307             call a:hulit.Print(a:item.rest, "match before virtual column ". number)
308         else
309             call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%<' item")
310             " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
311         endif
312     endif
313 endfunc "}}}
314 func! s:DocBspercAfter(bull, hulit, item) "{{{
315     let rest = a:bull.Bite('^\%(''.\|\d\+[lvc]\)\C')
316     if rest[0] == "'"
317         call a:hulit.Print(a:item.rest, "(assertion) match after position of mark ". rest[1])
318     else
319         let number = rest[:-2]
320         let type = rest[-1:]
321         if type ==# "l"
322             call a:hulit.Print(a:item.rest, printf("match below line %d (towards end of buffer)", number))
323         elseif type ==# "c"
324             call a:hulit.Print(a:item.rest, "match after column ". number)
325         elseif type ==# "v"
326             call a:hulit.Print(a:item.rest, "match after virtual column ". number)
327         else
328             call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%>' item")
329             " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
330         endif
331     endif
332 endfunc "}}}
333
334 let s:doc['\%'] = function("s:DocBspercAt")
335 let s:doc['\%<'] = function("s:DocBspercBefore")
336 let s:doc['\%>'] = function("s:DocBspercAfter")
337
338 let s:doc['\i'] = "identifier character (see 'isident' option)"
339 let s:doc['\I'] = "like \"\\i\", but excluding digits"
340 let s:doc['\k'] = "keyword character (see 'iskeyword' option)"
341 let s:doc['\K'] = "like \"\\k\", but excluding digits"
342 let s:doc['\f'] = "file name character (see 'isfname' option)"
343 let s:doc['\F'] = "like \"\\f\", but excluding digits"
344 let s:doc['\p'] = "printable character (see 'isprint' option)"
345 let s:doc['\P'] = "like \"\\p\", but excluding digits"
346 let s:doc['\s'] = "whitespace character: <Space> and <Tab>"
347 let s:doc['\S'] = "non-whitespace character; opposite of \\s"
348 let s:doc['\d'] = "digit: [0-9]"
349 let s:doc['\D'] = "non-digit: [^0-9]"
350 let s:doc['\x'] = "hex digit: [0-9A-Fa-f]"
351 let s:doc['\X'] = "non-hex digit: [^0-9A-Fa-f]"
352 let s:doc['\o'] = "octal digit: [0-7]"
353 let s:doc['\O'] = "non-octal digit: [^0-7]"
354 let s:doc['\w'] = "word character: [0-9A-Za-z_]"
355 let s:doc['\W'] = "non-word character: [^0-9A-Za-z_]"
356 let s:doc['\h'] = "head of word character: [A-Za-z_]"
357 let s:doc['\H'] = "non-head of word character: [^A-Za-z_]"
358 let s:doc['\a'] = "alphabetic character: [A-Za-z]"
359 let s:doc['\A'] = "non-alphabetic character: [^A-Za-z]"
360 let s:doc['\l'] = "lowercase character: [a-z]"
361 let s:doc['\L'] = "non-lowercase character: [^a-z]"
362 let s:doc['\u'] = "uppercase character: [A-Z]"
363 let s:doc['\U'] = "non-uppercase character: [^A-Z]"
364
365 let s:doc['\e'] = "match <Esc>"
366 let s:doc['\t'] = "match <Tab>"
367 let s:doc['\r'] = "match <CR>"
368 let s:doc['\b'] = "match CTRL-H"
369 let s:doc['\n'] = [function("s:DocBeginOfPat"), "match a newline"]
370 let s:doc['~'] = "match the last given substitute string"
371 let s:doc['\1'] = "match first captured string"
372 let s:doc['\2'] = "match second captured string"
373 let s:doc['\3'] = "match third captured string"
374 let s:doc['\4'] = "match fourth captured string "
375 let s:doc['\5'] = "match fifth captured string"
376 let s:doc['\6'] = "match sixth captured string"
377 let s:doc['\7'] = "match seventh captured string"
378 let s:doc['\8'] = "match eighth captured string"
379 let s:doc['\9'] = "match ninth captured string"
380
381 let s:doc['\z1'] = 'match same string matched by first "external" group'
382 let s:doc['\z2'] = 'match same string matched by second "external" group'
383 let s:doc['\z3'] = 'match same string matched by third "external" group'
384 let s:doc['\z4'] = 'match same string matched by fourth "external" group '
385 let s:doc['\z5'] = 'match same string matched by fifth "external" group'
386 let s:doc['\z6'] = 'match same string matched by sixth "external" group'
387 let s:doc['\z7'] = 'match same string matched by seventh "external" group'
388 let s:doc['\z8'] = 'match same string matched by eighth "external" group'
389 let s:doc['\z9'] = 'match same string matched by ninth "external" group'
390
391 " from MakeMagic()
392 " skip the rest of a collection
393 let s:coll_skip_pat = '^\^\=]\=\%(\%(\\[\^\]\-\\bertn]\|\[:\w\+:]\|\[=.=]\|\[\..\.]\|[^\]]\)\@>\)*]'
394
395 func! s:DocCollection(bull, hulit, item) "{{{
396     let collstr = a:bull.Bite(s:coll_skip_pat)
397     if collstr == "" || collstr == "]"
398         call a:hulit.AddLiteral('['. collstr)
399     else
400         let inverse = collstr =~ '^\^'
401         let with_nl = a:item == '\_['
402         let descr = inverse ? printf('collection not matching [%s', collstr[1:]) : 'collection'
403         let descr_nl = printf("%s%s", (inverse && with_nl ? ', but' : ''), (with_nl ? ' with end-of-line added' : ''))
404         call a:hulit.Print(a:item. collstr, descr. descr_nl)
405     endif
406 endfunc "}}}
407
408 let s:doc['['] = function("s:DocCollection")
409 let s:doc['\_['] = function("s:DocCollection")
410
411 func! s:DocOptAtoms(bull, hulit, item) "{{{
412     if a:item == '\%['
413         call a:hulit.Print(a:item, "start a sequence of optionally matched atoms")
414         let s:in_opt_atoms = 1
415         call a:hulit.AddIndent('. ')
416     else " a:item == ']'
417         if s:in_opt_atoms
418             call a:hulit.RemIndent()
419             call a:hulit.Print(a:item, "end of optionally matched atoms")
420             let s:in_opt_atoms = 0
421         else
422             call a:hulit.AddLiteral(a:item)
423         endif
424     endif
425 endfunc "}}}
426
427 " let s:doc['\%['] = "start a sequence of optionally matched atoms"
428 let s:doc['\%['] = function("s:DocOptAtoms")
429 let s:doc[']'] = function("s:DocOptAtoms")
430
431 func! s:DocAnywhere(bull, hulit, item, msg) "{{{
432     call a:hulit.Print(a:item, a:msg)
433     " keep state:
434     let s:at_begin_of_pat += 1
435 endfunc "}}}
436
437 let s:doc['\c'] = [function("s:DocAnywhere"), "ignore case while matching the pattern"]
438 let s:doc['\C'] = [function("s:DocAnywhere"), "match case while matching the pattern"]
439 let s:doc['\Z'] = [function("s:DocAnywhere"), "ignore composing characters in the pattern"]
440
441 " \%d 123
442 " \%x 2a
443 " \%o 0377
444 " \%u 20AC
445 " \%U 1234abcd
446
447 func! s:DocBspercDecimal(bull, hulit, item) "{{{
448     let number = a:bull.Bite('^\d\{,3}')
449     let char = strtrans(nr2char(str2nr(number)))
450     call a:hulit.Print(a:item. number, printf("match character specified by decimal number %s (%s)", number, char))
451 endfunc "}}}
452 func! s:DocBspercHexTwo(bull, hulit, item) "{{{
453     let number = a:bull.Bite('^\x\{,2}')
454     let char = strtrans(nr2char(str2nr(number,16)))
455     call a:hulit.Print(a:item. number, printf("match character specified with hex number 0x%s (%s)", number, char))
456 endfunc "}}}
457 func! s:DocBspercOctal(bull, hulit, item) "{{{
458     let number = a:bull.Bite('^\o\{,4}')
459     let char = strtrans(nr2char(str2nr(number,8)))
460     call a:hulit.Print(a:item. number, printf("match character specified with octal number 0%s (%s)", substitute(number, '^0*', '', ''), char))
461 endfunc "}}}
462 func! s:DocBspercHexFour(bull, hulit, item) "{{{
463     let number = a:bull.Bite('^\x\{,4}')
464     let char = has("multi_byte_encoding") ? ' ('. strtrans(nr2char(str2nr(number,16))).')' : ''
465     call a:hulit.Print(a:item. number, printf("match character specified with hex number 0x%s%s", number, char))
466 endfunc "}}}
467 func! s:DocBspercHexEight(bull, hulit, item) "{{{
468     let number = a:bull.Bite('^\x\{,8}')
469     let char = has("multi_byte_encoding") ? ' ('. strtrans(nr2char(str2nr(number,16))).')' : ''
470     call a:hulit.Print(a:item. number, printf("match character specified with hex number 0x%s%s", number, char))
471 endfunc "}}}
472
473 let s:doc['\%d'] = function("s:DocBspercDecimal") " 123
474 let s:doc['\%x'] = function("s:DocBspercHexTwo") " 2a
475 let s:doc['\%o'] = function("s:DocBspercOctal") " 0377
476 let s:doc['\%u'] = function("s:DocBspercHexFour") " 20AC
477 let s:doc['\%U'] = function("s:DocBspercHexEight") " 1234abcd
478
479 " \m
480 " \M
481 " \v
482 " \V
483 "}}}
484
485 func! s:NewHelpPrinter() "{{{
486     let obj = {}
487     let obj.literals = ''
488     let obj.indents = []
489     let obj.len = 0         " can be negative (!)
490
491     func! obj.Print(str, ...) "{{{
492         call self.FlushLiterals()
493         let indstr = join(self.indents, '')
494         echohl Comment
495         echo indstr
496         echohl None
497         if a:0 == 0
498             echon a:str
499         else
500             " echo indstr. printf("`%s'   %s", a:str, a:1)
501             echohl PreProc
502             echon printf("%-10s", a:str)
503             echohl None
504             echohl Comment
505             echon printf(" %s", a:1)
506             echohl None
507         endif
508     endfunc "}}}
509
510     func! obj.AddLiteral(item) "{{{
511         let self.literals .= a:item
512     endfunc "}}}
513
514     func! obj.FlushLiterals() "{{{
515         if self.literals == ''
516             return
517         endif
518         let indstr = join(self.indents, '')
519         echohl Comment
520         echo indstr
521         echohl None
522         if self.literals =~ '^\s\|\s$'
523             echon printf("%-10s", '"'. self.literals. '"')
524         else
525             echon printf("%-10s", self.literals)
526         endif
527         echohl Comment
528         echon " literal string"
529         if exists("*strchars")
530             if self.literals =~ '\\'
531                 let self.literals = substitute(self.literals, '\\\(.\)', '\1', 'g')
532             endif
533             let spconly = self.literals =~ '[^ ]' ? '' : ', spaces only'
534             let nlit = strchars(self.literals)
535             echon " (". nlit. (nlit==1 ? " atom" : " atoms"). spconly.")"
536         endif
537         echohl None
538         let self.literals = ''
539     endfunc  "}}}
540
541     func! obj.AddIndent(...) "{{{
542         call self.FlushLiterals()
543         if self.len >= 0
544             call extend(self.indents, copy(a:000))
545         elseif self.len + a:0 >= 1
546             call extend(self.indents, a:000[-(self.len+a:0):])
547         endif
548         let self.len += a:0
549     endfunc "}}}
550
551     func! obj.RemIndent(...) "{{{
552         call self.FlushLiterals()
553         if a:0 == 0
554             if self.len >= 1
555                 call remove(self.indents, -1)
556             endif
557             let self.len -= 1
558         else
559             if self.len > a:1
560                 call remove(self.indents, -a:1, -1)
561             elseif self.len >= 1
562                 call remove(self.indents, 0, -1)
563             endif
564             let self.len -= a:1
565         endif
566     endfunc "}}}
567
568     return obj
569 endfunc "}}}
570
571 func! s:NewTokenBiter(str) "{{{
572     " {str}     string to eat pieces from
573     let obj = {'str': a:str}
574
575     " consume piece from start of input matching {pat}
576     func! obj.Bite(pat) "{{{
577         " {pat}     should start with '^'
578         let bite = matchstr(self.str, a:pat)
579         let self.str = strpart(self.str, strlen(bite))
580         return bite
581     endfunc "}}}
582
583     " get the unparsed rest of input (not consuming)
584     func! obj.Rest() "{{{
585         return self.str
586     endfunc "}}}
587
588     " check if end of input reached
589     func! obj.AtEnd() "{{{
590         return self.str == ""
591     endfunc "}}}
592
593     return obj
594 endfunc "}}}
595
596 " Modeline: {{{1
597 let &cpo = s:cpo_save
598 unlet s:cpo_save
599 " vim:ts=8:fdm=marker: