def read_verb_dictionary(verb_path):
    """ Reads the verb dictionary from VerbFileName """

    """
    ======= VERB DICTIONARY ORGANIZATION =======

    The verb dictionary consists of a set of synsets followed by a series of verb
    synonyms and patterns.

    VERB SYNONYM BLOCKS AND PATTERNS

    A verb synonym block is a set of verbs which are synonymous (or close enough) with
    respect to the patterns. The program automatically generates the regular forms of the
    verb -- endings of 'S','ED' and 'ING' -- if it is regular (and, implicitly, English);
    otherwise the irregular forms can be specified in {...} following the primary verb.
    Note that if irregular forms are provided in {...}, ALL forms need to be included,
    even if some of those are the same as the regular form (in other words, no
    alternative forms are generated when {...} is present). An optional code for the
    isolated verb can follow in [...].

    The verb block begins with a comment of the form

    --- <GENERAL DESCRIPTION> [<CODE>] ---

    where the "---" signals the beginning of a new block. The code in [...] is the
    primary code -- typically a two-digit+0 cue-category code -- for the block, and this
    will be used for all other verbs unless these have their own code. If no code is
    present, this defaults to the null code "---"  which indicates that the isolated verb
    does not generate an event. The null code also can be used as a secondary code.

    This is followed by a set of patterns -- these begin with '-' -- which generally
    follow the same syntax as TABARI patterns. The pattern set is terminated with a
    blank line.

    ====== EXAMPLE =====

    &CURRENCY
    +DOLLARS
    +EUROS
    +AUSTRIAN FLORIN
    +GOLDEN_GOBLIN_GALLEONS_
    +PESO
    +KRONER_
    +YUM YENNYEN
    +JAVANESE YEN
    +SWISS FRANCS
    +YEN

    &ALTCURR
    +BITCOIN
    +PIRATE GOLD_
    +LEPRECHAUN GOLD_

    &AUXVERB3_
    +HAVE
    +HAS
    +HAD


    --- GRANT [070] ---
    GRANT
    GIVE {GAVE GIVEN GIVING }  # jw  11/14/91
    CONTRIBUTE # tony  3/12/91
    - * &CURRENCY [903] # -PAS 12.01.12
    - * &ALTCURR [904] # -PAS 14.05.08
    - * RUPEES  [071]


    --- EXPLAIN_VERBAL [010] ---
    EXPLAIN
    COMMENT
    ASSERT
    SAY  {SAID SAYING }
    CLARIFY {CLARIFIES CLARIFIED CLARIFYING} [040]
    CLEAR_UP
    - * RESTORATION RELATIONS [050:050]  # ANNOUNCE <ab 02 Dec 2005>
    - * COMMIT &MILITARY TO + [0332]  # SAY <sls 13 Mar 2008>
    - * ATTACK ON + AS &CRIME [018]  # DESCRIBE <ab 31 Dec 2005>
    - * &CURRENCY DEBT_RELIEF [0331]  # ANNOUNCE <ab 02 Dec 2005>  , ANNOUNCE
    - * WELCOMED OFFER FROM + [050]  # ANNOUNCE <ab 02 Dec 2005>
    - * + THAT $ WILL PULLOUT [0356]  # INFORM <sms 30 Nov 2007>
    - * POSSIBILITY OF &FIGHT [138]  # MENTION <OY 11 Mar 2006>
    - * AGREED JOIN COALITION [031]  # ANNOUNCE <OY 15 Mar 2006>
    - * TRACES RESPONSIBILITY [112]  # REPORT
    - CONFIRMED * OF BOMBINGS [010]  # REPORT
    - * INITIATIVE END &FIGHT [036]  # ANNOUNCE <ab 02 Dec 2005>

    &TESTSYN3
        +TO THE END
    +TO THE DEATH
    +UNTIL HELL FREEZES OVER

    &TESTSYN4
    +TO THE END OF THE EARTH
    +TO THE DEATH

    --- VOW  [170] ---
    VOW ;tony  3/9/91
    - * RESIST &TESTSYN3 [113] ; pas 4/20/03
    - * RESIST &TESTSYN4  [115] ; pas 4/20/03
    - * RESISTANCE TO THE INVADING  [114] ; pas 4/20/03
    - * RESIST  [112] ;tony  4/29/91
    - * WAR  [173] ;tony  4/22/91


    STORAGE STRUCTURE
    
    
    The verbs are stored withing the PETRglobals.VerbDict in two different subdictionaries.
    The first, "verbs", contains the pattern of the base verb. For most verbs, this is just 
    the word itself, but some verbs like "cordon off" and "wire tap" are multi-word verbs that
    function as single words. These words are stored as a search tree, with the base root as
    the word that will be marked as a verb (e.g. cordon and tap). Following these will be a tree 
    structure of all the words in the compound verb that follow the verb, with a # on the end to
    indicate the end of the search. After the #, all the words before the marked verb are in a similar 
    tree structure in reverse order. A dictionary containing the list [arrest, wire tap, cordon off] 
    would look like:
    
    
                                     'CORDON' --- ' OFF' --- '#' --- '#'
                                    /
                                   /
                                  /
    PETRglobals.VerbDict --- 'verbs'------ 'ARREST' ---  '#' --- '#'
                         \         \
                          |         \
                          |          \
                          |          'TAP --- '#' --- 'WIRE' --- '#'
                          |
                          |
                      'patterns'
                                     
                                     
    After the final '#' there is a dictionary with two entries: meaning and code. The meaning
    is used to find the entry in the patterns dictionary, and the code stores the specific code 
    for that verb if it differs from the code of the meaning. The patterns dictionary stores all
    the pattern information contained in the files after the synonyms, in a similar 
    verb-after-#-before-#-info dictionary.
    
    
    
    PROGRAMMING NOTES

    Notes
    1.  TABARI allowed recursive synsets -- that is, synsetS embedded in patterns and other
        synsets. It should be possible to do this fairly easily, at least with basic
        synsets as elements (not as patterns) but a simple call in syn_match(isupperseq)
        was not sufficient, so this needs more work.

    2.  For TABARI legacy purposes, the construction "XXXX_ " is converted to "XXXX ",
        an open match.  However, per the comments below, generally TABARI dictionaries
        should be converted before being used with PETRARCH.


    VERB DICTIONARY DIFFERENCES FROM TABARI

    On the *very* remote chance -- see Note 1 -- that you are trying to modify a TABARI
    .verbs dictionary to the PETRARCH format, the main thing you will need to eliminate
    are stemmed words:  PETRARCH only works with complete words. On the positive side,
    PETRARCH will only look at string as a "verb" if it has been identified as such by
    the parser -- that is, it is preceded with (VP and a tag that starts with (VB, so
    the [numerous] patterns required for noun/verb disambiguation are no longer
    needed. PETRARCH also does not allow disconjunctive sets in patterns: to accommodate
    legacy dictionaries, patterns containing these are skipped, but in order to work,
    these should be replaced with synsets. Also see additional remarks at the beginning
    of the file.

    The other big difference between PETRARCH and TABARI is verb-noun disambiguation:
    the pattern-based approach of TABARI needed a lot of information to insure that a
    word that *might* be a verb was, in fact, a verb (or was a noun that occurred in a
    context where it indicated an event anyway: TABARI's [in]famous tendency to code the
    right thing for the wrong reason). PETRARCH, in contrast, only looks as a verb when
    the parsing has identified it as, in fact, a verb. This dramatically reduces false
    positives and eliminates the need for any pattern which was required simply for
    disambiguation, but it also means that PETRARCH is a lot more discriminating about
    what actually constitutes an event. The big difference here is that verb-only
    codes are the norm in PETRARCH dictionaries but the exception in TABARI dictionaries.

    The active PETRARCH verbs dictionary has been extensively reorganized into both
    verb and noun synonym sets, and you are probably better off adding vocabulary to
    this [see Note 1] than converting a dictionary, but it can be done. An unconverted
    TABARI dictionary, on the other hand, will generally not work particularly well with
    PETRARCH.

    Note 1.

    Yeah, right. Every project we've encountered -- including those lavishly funded by
    multiple millions of taxpayers dollars and those allegedly producing multiple millions
    of events -- has regarded the NSF-funded CAMEO verbs dictionaries as a sacred artifact
    of the [infamous] Data Fairy, lowered from Asgaard along the lines of this

    http://www.wikiart.org/en/jacob-jordaens/allegory-of-the-peace-of-westphalia-1654

    [not exactly sure where the .verbs file is in that painting, but it must be in
    there somewhere]

    but then subsequently subject said dictionaries to bitter complaints that they aren't
    coding comprehensively.

    Look, dudes and dudettes, these dictionaries have been open source for about as long
    as the US has been at war in Afghanistan -- which is to say, a really long time -- and
    if you don't like how the coding is being done, add some new open-source vocabulary
    to the dictionaries instead of merely parasitizing the existing work. Dudes.

    The *real* problem, one suspects, is embodied in the following nugget of wisdom:

    Opportunity is missed by most people because it is dressed in overalls and looks
    like work.
    Thomas A. Edison

    Dudes.

    """
    global theverb, verb
    PETRglobals.VerbDict = {'verbs': {}, 'phrases': {}}

    def add_dict_tree(targ, verb, meaning="", code='---',
                      upper=[], synset=False, dict='phrases', line=""):
        prev = verb
        list = PETRglobals.VerbDict[dict].setdefault(verb, {})
        while targ != []:
            if targ[0] in [' ', '']:
                targ = targ[1:]
                continue

            # Put synset trees in their own bin so we can
            if targ[0][0] == '&':
                list = list.setdefault(
                    'synsets',
                    {})   # consider this case later
            list = list.setdefault(targ[0], {})
            targ = targ[1:]

        list["#"] = list.setdefault(
            "#",
            {})  # termination symbol for the lower phrase

        list = list["#"]

        targ = upper
        while targ != []:
            if targ[-1] in [' ', '']:
                targ = targ[:-1]
                continue

            # Put synset trees in their own bin so we can
            if targ[-1][0] == '&':
                list = list.setdefault(
                    'synsets',
                    {})   # consider this case later
            list = list.setdefault(targ[-1], {})
            targ = targ[:-1]

        list['#'] = {'meaning': meaning, 'code': code, 'line': line}

    def make_phrase_list(thepat):
        """ Converts a pattern phrase into a list of alternating words and connectors """
        if len(thepat) == 0:
            return []
        phlist = []
        start = 0
        maxlen = len(thepat) + 1  # this is just a telltail
        while start < len(thepat):  # break phrase on ' ' and '_'
            spfind = thepat.find(' ', start)
            if spfind == -1:
                spfind = maxlen
            unfind = thepat.find('_', start)
            if unfind == -1:
                unfind = maxlen
            # somehow think I don't need this check...well, I just need the
            # terminating point, still need to see which is lower
            if unfind < spfind:
                phlist.append(thepat[start:unfind])
                start = unfind + 1
            else:
                phlist.append(thepat[start:spfind])
                phlist.append(' ')
                start = spfind + 1
                # check for missing synsets
        ka = 0
        while ka < len(phlist):
            if len(phlist[ka]) > 0:
                if (phlist[ka][0] == '&') and (
                        phlist[ka] not in PETRglobals.VerbDict):
                    print("WTF", phlist[ka])
                    print(sorted(PETRglobals.VerbDict.keys()))
                    exit()

                    logger.warning("Synset " + phlist[ka] +
                                   " has not been defined; pattern skipped")
                    raise ValueError  # this will do...
            ka += 2
        return phlist

    def get_verb_forms(loccode, line):
        """  Read the irregular forms of a verb. """
        # need error checking here
        global verb, theverb

        forms = verb[verb.find('{') + 1:verb.find('}')].split()
        for wrd in forms:
            vscr = wrd
            add_dict_tree([], vscr, theverb, loccode, dict='verbs', line=line)

    def store_multi_word_verb(loccode, line):
        """  Store a multi-word verb and optional irregular forms. """
        global verb, theverb

        if '{' in verb:
            forms = verb[verb.find('{') + 1:verb.find('}')].split()
            forms.append(verb[:verb.find('{')].strip())
        else:
            forms = [verb]
            # add the regular forms to the verb designated by '+'
            plind = verb.index('+') + 1
            if verb.find('_', plind) > 0:
                vroot = verb[plind:verb.find('_', plind)]
            else:
                vroot = verb[plind:]
            forms.append(verb.replace(vroot, vroot + "S"))
            if vroot[-1] == 'E':  # root ends in 'E'
                forms.append(verb.replace(vroot, vroot + "D"))
                forms.append(verb.replace(vroot, vroot[:-1] + "ING"))
            else:
                forms.append(verb.replace(vroot, vroot + "ED"))
                forms.append(verb.replace(vroot, vroot + "ING"))

        for phrase in forms:
            if '+' in phrase:  # otherwise not in correct form so skip it
                words = phrase.split('_')

                multilist = []
                upper = []
                phrase
                if words[0].startswith('+'):
                    multilist = [True]
                    for ka in range(1, len(words)):
                        multilist.append(words[ka])
                    targverb = words[0][1:]

                else:
                    upper = [False]
                    for ka in range(2, len(words) + 1):

                        upper.append(words[len(words) - ka])
                    targverb = words[len(words) - 1][1:]

                add_dict_tree(
                    multilist[
                        1:], targverb, theverb, loccode, upper=upper[
                        1:], dict='verbs', line=line)

            else:
                logger.warning(
                    'Error in read_verb_dictionary()/store_multi_word_verb(): ' +
                    phrase +
                    ' in ' +
                    verb +
                    ' is part of a multi-word verb and should contain a +; this was skipped')

    def make_verb_forms(loccode, line):
        """ Create the regular forms of a verb. """
        global verb, theverb
        vroot = verb
        vscr = vroot + \
            "S" if vroot[-1] not in ["S", "X", "Z"] else vroot + "ES"
        add_dict_tree([], vscr, theverb, loccode, dict='verbs', line=line)
        if vroot[-1] == 'E':  # root ends in 'E'
            vscr = vroot + "D"

            add_dict_tree([], vscr, theverb, loccode, dict='verbs', line=line)

            vscr = vroot[:-1] + "ING"

        else:
            vscr = vroot + "ED"  # if vroot[-1] not == "Y" else vroot[-1]+"IES"

            add_dict_tree([], vscr, theverb, loccode, dict='verbs', line=line)
            vscr = vroot + "ING"

        add_dict_tree([], vscr, theverb, loccode, dict='verbs', line=line)

    def make_plural(st):
        """ Create the plural of a synonym noun st """

        if 'Y' == st[-1]:
            return st[:-1] + 'IES'  # space is added below
        elif 'S' == st[-1]:
            return st[:-1] + 'ES'
        else:
            return st + 'S'

    # note that this will be ignored if there are no errors
    logger = logging.getLogger('petr_log')
    logger.info("Reading " + PETRglobals.VerbFileName)
    open_FIN(verb_path, "verb")

    theverb = ''
    newblock = False
    ka = 0   # primary verb count ( debug )
    line = read_FIN_line()
    while len(line) > 0:  # loop through the file

        if '[' in line:
            part = line.partition('[')
            verb = part[0].strip()
            code = part[2][:part[2].find(']')]
        else:
            verb = line.strip()
            code = ''
        if verb.startswith('---'):  # start of new block
            if len(code) > 0:
                primarycode = code
            else:
                primarycode = '---'
            newblock = True
            line = read_FIN_line()

        elif verb[0] == '-':   # pattern

            # TABARI legacy: currently aren't processing these
            if '{' in verb:
                line = read_FIN_line()
                continue
            # resolve the ambiguous '_ ' construction to ' '

            verb = verb.replace('_ ', ' ')
            if verb[-1] == "_":
                verb = verb[:-1]
            targ = verb[1:].partition('*')
            try:
                highpat = make_phrase_list(targ[0].lstrip())
                lowphrase = targ[2].rstrip()
                if len(lowphrase) == 0:
                    lowpat = []
                else:
                    lowpat = [targ[2][0]]   # start with connector
                    loclist = make_phrase_list(lowphrase[1:])
                    lowpat.extend(loclist[:-1])   # don't need the final blank

                add_dict_tree(lowpat, theverb, "", code, highpat, line=line)

            except __HOLE__:
                # just trap the error, which will skip the line containing it
                pass
            line = read_FIN_line()

        elif verb[0] == '&':  # Read and store a synset.

            if verb[-1] == '_':
                noplural = True
                verb = verb[:-1]  # remove final _
            else:
                noplural = False
            PETRglobals.VerbDict[verb] = {}
            line = read_FIN_line()
            while line[0] == '+':
                wordstr = line[1:].strip()
                if noplural or wordstr[-1] == '_':
                    wordstr = wordstr.strip().replace('_', ' ')
                    add_dict_tree(
                        wordstr.split(),
                        verb,
                        synset=True,
                        dict='verbs',
                        line=line)
                else:
                    wordstr = wordstr.replace('_', ' ')
                    add_dict_tree(
                        wordstr.split(),
                        verb,
                        synset=True,
                        dict='verbs',
                        line=line)
                    add_dict_tree(
                        make_plural(wordstr).split(),
                        verb,
                        synset=True,
                        dict='verbs',
                        line=line)

                line = read_FIN_line()

        else:  # verb
            if len(code) > 0:
                curcode = code
            else:
                curcode = primarycode
            if newblock:
                if '{' in verb:
                    # theverb is the index to the pattern storage for the
                    # remainder of the block
                    theverb = verb[:verb.find('{')].strip()

                else:
                    theverb = verb
                add_dict_tree([], theverb, code=curcode, line=line)
                newblock = False
            if '_' in verb:
                store_multi_word_verb(curcode, line)
            else:
                add_dict_tree(
                    [],
                    verb.split()[0],
                    theverb,
                    curcode,
                    dict='verbs',
                    line=line)
                if '{' in verb:

                    get_verb_forms(curcode, line)
                else:
                    make_verb_forms(curcode, line)
            ka += 1   # counting primary verbs
            line = read_FIN_line()

    close_FIN()
dataset/ETHPy150Open openeventdata/petrarch/petrarch/PETRreader.py/read_verb_dictionary