Source code for udapi.block.ud.pt.addmwt

"""Block ud.pt.AddMwt for heuristic detection of Portuguese contractions.

According to the UD guidelines, contractions such as "dele" = "de ele"
should be annotated using multi-word tokens.

Note that this block should be used only for converting legacy conllu files.
Ideally a tokenizer should have already split the MWTs.
"""
import udapi.block.ud.addmwt

MWTS = {
    'à':       {'form': 'a a', 'lemma': 'a o'},
    'às':      {'form': 'a as', 'lemma': 'a o'},
    'ao':      {'form': 'a o', 'lemma': 'a o'},
    'aos':     {'form': 'a os', 'lemma': 'a o'},
    'da':      {'form': 'de a', 'lemma': 'de o'},
    'das':     {'form': 'de as', 'lemma': 'de o'},
    'dessa':   {'form': 'de essa', 'lemma': 'de esse'},
    'dessas':  {'form': 'de essas', 'lemma': 'de esse'},
    'desse':   {'form': 'de esse', 'lemma': 'de esse'},
    'desses':  {'form': 'de esses', 'lemma': 'de esse'},
    'desta':   {'form': 'de esta', 'lemma': 'de este'},
    'destas':  {'form': 'de estas', 'lemma': 'de este'},
    'deste':   {'form': 'de este', 'lemma': 'de este'},
    'destes':  {'form': 'de estes', 'lemma': 'de este'},
    'disso':   {'form': 'de isso', 'lemma': 'de este'},
    'disto':   {'form': 'de isto', 'lemma': 'de este'},
    'do':      {'form': 'de o', 'lemma': 'de o'},  # 'upos': 'ADP PRON', 'deprel': 'case *''
    'dos':     {'form': 'de os', 'lemma': 'de o'},
    'dum':     {'form': 'de um', 'lemma': 'de um'},
    'duma':    {'form': 'de uma', 'lemma': 'de um'},
    'dumas':   {'form': 'de umas', 'lemma': 'de um'},
    'duns':    {'form': 'de uns', 'lemma': 'de um'},
    'na':      {'form': 'em a', 'lemma': 'em o'},
    'nas':     {'form': 'em as', 'lemma': 'em o'},  # ADP PRON
    'nesses':  {'form': 'em esses', 'lemma': 'em esse'},
    'nesta':   {'form': 'em esta', 'lemma': 'em este'},
    'neste':   {'form': 'em este', 'lemma': 'em este'},
    'nisso':   {'form': 'em isso', 'lemma': 'em este'},
    'nisto':   {'form': 'em isto', 'lemma': 'em este',
                'upos': 'ADP PRON', 'main': 1, 'shape': 'subtree'},
    'no':      {'form': 'em o', 'lemma': 'em o'}, # PRON cases are excluded below
    'nos':     {'form': 'em os', 'lemma': 'em o'}, # PRON cases are excluded below
    'num':     {'form': 'em um', 'lemma': 'em um'},
    'numa':    {'form': 'em uma', 'lemma': 'em um'},
    'numas':   {'form': 'em umas', 'lemma': 'em um'},
    'nuns':    {'form': 'em uns', 'lemma': 'em um'},
    'pela':    {'form': 'por a', 'lemma': 'por o'},
    'pelas':   {'form': 'por as', 'lemma': 'por o'},
    'pelos':   {'form': 'por os', 'lemma': 'por o'},
    'pelo':    {'form': 'por o', 'lemma': 'por o'},
    # TODO daí = de aí = ADP ADV = case advmod
}

# shared values for all entries in MWTS
for v in MWTS.values():
    if not v.get('upos'):
        v['upos'] = 'ADP DET'
    if not v.get('deprel'):
        v['deprel'] = 'case det'
    v['feats'] = '_ *'
    # The following are the default values
    # v['main'] = 0 # which of the two words will inherit the original children (if any)
    # v['shape'] = 'siblings', # the newly created nodes will be siblings

for pronoun in 'ela ele eles elas'.split():
    MWTS['d' + pronoun] = {
        'form': 'de ' + pronoun,
        'lemma': 'de ' + pronoun,
        'upos': 'ADP PRON',
        'deprel': 'case *',
        'main': 1,
        'shape': 'subtree',
    }


[docs] class AddMwt(udapi.block.ud.addmwt.AddMwt): """Detect and mark MWTs (split them into words and add the words to the tree)."""
[docs] def multiword_analysis(self, node): """Return a dict with MWT info or None if `node` does not represent a multiword token.""" # "no" can be either a contraction of "em o", or a pronoun if node.form.lower() in ('no', 'nos') and node.upos == 'PRON': return analysis = MWTS.get(node.form.lower(), None) # If the input is e.g.: # 1 na _ ADP _ _ deprel_x ? # 2 verdade _ NOUN _ _ fixed 1 # The expected output is: # 1-2 na _ _ _ _ _ _ # 1 em _ ADP _ _ deprel_x ? # 2 a _ DET _ _ fixed 1 # 3 verdade _ NOUN _ _ fixed 1 if analysis and analysis['deprel'] == 'case det' and node.udeprel != 'case': copy = dict(analysis) copy['deprel'] = '* det' copy['shape'] = 'subtree' first_child = next((c for c in node.children if node.precedes(c)), None) if first_child is not None and first_child.udeprel == 'fixed': copy['deprel'] = '* fixed' return copy if analysis is not None: return analysis if node.form.lower().endswith('-se') and node.upos == 'VERB': return { 'form': node.form.lower()[:-3] + ' se', 'lemma': '* se', 'upos': '* PRON', 'feats': '* _', 'deprel': '* nsubj', # or '* expl' 'main': 0, 'shape': 'subtree', } elif node.form.lower().endswith('-lo') and node.upos == 'VERB': return { 'form': node.form.lower()[:-3] + ' lo', 'lemma': '* ele', 'upos': '* PRON', 'feats': '* _', 'deprel': '* obj', 'main': 0, 'shape': 'subtree', } elif node.form.lower().endswith('-los') and node.upos == 'VERB': return { 'form': node.form.lower()[:-4] + ' los', 'lemma': '* eles', 'upos': '* PRON', 'feats': '* _', 'deprel': '* obj', 'main': 0, 'shape': 'subtree', } elif node.form.lower().endswith('-o') and node.upos == 'VERB': return { 'form': node.form.lower()[:-2] + ' o', 'lemma': '* ele', 'upos': '* PRON', 'feats': '* _', 'deprel': '* obj', 'main': 0, 'shape': 'subtree', } return None