4

I am writing a VBA routine so I can use normal-mode commands in Microsoft Word. (Yes, I am! Edit And here you go!) I am only going to support the d, c, and y operators initially, and only char/word/sentence/paragraph motions. No visual mode, no ]] &c., no "*.

Is there an easy way to parse the command the user has entered? My thought is that it probably can be done with a regex, since the count/operator/count/motion sequence is fairly restrictive. Something like (very magic):

^([0-9]*)([dcy])([0-9]*)([ai][wWsp]|[hjklGfFtTwebWEB\)\(\}\{])$

Does anyone already have a regex or parser that will do this? I am wondering if I have missed any corner cases or subtleties in the above. (Edit For example, I had included 0 as a valid trailing character, which interacted badly with the [0-9]* before it.) Thanks!

2
  • 2
    These seem like normal mode commands, not ex commands; please clarify. Also since normal sequences form something of a finite state machine, it's plausible there may be such a regex, but I don't see how that helps you as opposed to hand-writing it. FYI glts.github.io/2013/04/28/vim-normal-mode-grammar.html
    – Mass
    Apr 6, 2018 at 18:10
  • @Mass Thanks for the correction. Yes, normal mode, not ex mode. Thanks for the link!
    – cxw
    Apr 6, 2018 at 18:15

1 Answer 1

3

I have written my Word add-in using VBScript regex v5.5 to parse the command. Feedback and contributions welcome! As of 2019-02-15, the add-in works in Word 2007–2016. Edit As of 2020-02-13, 64-bit Word is also supported.

I am largely following the model in the grammar @Mass kindly linked to. As of v0.2.9, my regex is:

^(([1-9][0-9]*)?((([HMLGhjklwbWB\x28\x29\x7b\x7d]|g?[eE0\^\$]|[fFtT](.))|(gW)?g?[\*#]|g?[pP])|([cdyv])([1-9][0-9]*)?(([\[\]])?([ai])([wWsp])|[fFtT](.)|[HMLGhjklwbWB\x28\x29\x7b\x7d]|g?[eE0\^\$])))$

with zero-based submatch numbers:

1   The initial count

3   Intransitive verb (e.g., 0, w, f<char>)
4   Motion for an intransitive verb
5   The <char> of an f, F, t, or T

7   Transitive verb (e.g., operators d, c, y)
8   The count after the operator
9   The motion or text object ("target")
10  vim-ninja-feet* [ or ]
11  a or i if you are selecting a text object
12   For a and i motions, which type of object is being selected (w, W, s, p)
13  For f, F, t and T motions, which character is being used

* vim-ninja-feet

Registers are still not supported.

The regex is complicated enough that I wrote a Perl script to generate the regex from a text description of it. That description is below. Run ./re2vba.pl vim-regex.txt to generate the regex and submatch-number assignments on stdout.

In the description, (?<name>...) represents a named submatch, and (?<=piece-name) pulls in a named piece of the regex. Comments (#... or (?#...)) and trailing whitespace (after stripping comments) are ignored.

# Main pattern
main        ^(  (?# Note: registers not yet implemented)
                (?<count1>[1-9][0-9]*)?
                (?:
                    (?<=intrans)
                    |
                    (?<=trans)
                )
            )$

# Motions that don't take arguments (although they may take counts).
# A motion of "0" is special-cased in the parsing code to keep the regex
# clean.  This regex, after backtracking, matches "10" as a count of "1"
# followed by a "0" motion.  We special-case rather than handling that here.
noarg-motion    [HMLG
                hjkl
                wbWB
                \x28\x29\x7b\x7d]|
                    (?# 28/29 are parens, move sentence )
                    (?# 7b/7d are braces, move paragraph )
                g?[eE0\^\$]         (?# gw, gb, etc. are not motions.)
                (?#  ^ Note: 0 is special-cased in the parsing code.)

# Intransitive, including motions.  These can take a count, namely ?<count1>.
intrans     (?<iverb>
                (?<imotion>             (?# motions)
                    (?<=noarg-motion)|
                    [fFtT](?<itext>.)
                )|

                (gW)?g?[\*#]|           (?# searches)

                g?[pP]                  (?# pastes)
            )
                # IVERB: what to do
                        # ITEXT: character to jump to

# Text-object selection ([ai][something])
textobj     (?<ninja>[\[\]])?               (?# Ninja-feet marker)
                                    # https://github.com/tommcdo/vim-ninja-feet
            (?<tobj_range>[ai])             (?# Introducer)
            (?<objtype>[wWsp])              (?# Type of text object)

# Transitive: verb, count, object, object type, text
trans       (?<tverb>[cdyv])                (?# TVERB: what to do)
            (?<count2>[1-9][0-9]*)?         (?# Count after the operator)
            (?<target>                      (?# What to work on)
                (?<=textobj)|               (?# Text objects)
                [fFtT](?<ttext>.)|          (?# Motions with an argument)
                (?<=noarg-motion)
            )

Note 1: Just like in Vim, v selects the text moved over. However, Word is essentially always in a combination of normal and visual modes, so v doesn't leave you in any special mode.

Note 2: I don't need the c intransitive verb for one-offs, since you are back in Word (i.e., insert mode) after a d. However, I am working on a loop version that will leave you in Normal mode after a d but not after a c(#3).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.