From 04ace52ccc4cca1cc64ed57712e78eb2635a58a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 17 Jul 2022 16:50:50 -0300 Subject: [PATCH 01/34] =?UTF-8?q?Modifico=20funci=C3=B3n=20principal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/main.py b/main.py index 80386f7..c8940ed 100644 --- a/main.py +++ b/main.py @@ -1,20 +1,23 @@ from derivations import * from structures import * -def main(): - lexicon = Lexicon() - counter = 0 - ug = UniversalGrammar(set(), set(), set()) # todo: add feature import to UG - i_lang = ILanguage(lexicon, ug) - derivation = Derivation(i_lang, word_list=list(i_lang.lexicon.lex)) - derivation.derive() - - - # kate_token1 = LexicalItemToken(list(lexicon.lex)[0], 0) - # kate_token2 = LexicalItemToken(list(lexicon.lex)[0], 1) - # kate_runs = kate_token1.merge(kate_token2, 2) - # tr = tree(kate_runs) - # tr.pretty_print() +# def main(): +# lexicon = Lexicon() +# counter = 0 +# ug = UniversalGrammar(set(), set(), set()) # todo: add feature import to UG +# i_lang = ILanguage(lexicon, ug) +# derivation = Derivation(i_lang, word_list=list(i_lang.lexicon.lex)) +# derivation.derive() +def main(): + while True: + sentence = input("Insert a sentence to parse or press (f) to finish: ") + if sentence != 'f': + try: + parse(sentence) + except AssertionError: + print("Some words are not in the lexicon") + else: + break main() \ No newline at end of file From af0ed8c8fd3d217d148a6aff3b025af78fd3866f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 17 Jul 2022 16:51:48 -0300 Subject: [PATCH 02/34] =?UTF-8?q?Agrego=20parser=20y=20a=C3=B1ado=20m?= =?UTF-8?q?=C3=A9todos=20a=20la=20clase=20Derivation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- derivations/parser.py | 127 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 derivations/parser.py diff --git a/derivations/parser.py b/derivations/parser.py new file mode 100644 index 0000000..caed95c --- /dev/null +++ b/derivations/parser.py @@ -0,0 +1,127 @@ +from structures import * +from derivations import derivation + +class Derivation(derivation.Derivation): + + def autotf(self, index): + self.print_derivation() + x = self.stages[-1].workspace.find_workspace(index) # get x + + # Rule 1 - Successful derivation + if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0: + print("Successfull parsing!") + print('') + return True + + # Rule 2 + if len(x.triggers) > 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) != SyntacticObjectSet: + return None + + # Rule 3 - Internal merge + if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) == SyntacticObjectSet: + print('Aplying rule 3: internal merge (X,Y)') + features = [trigger for trigger in x.triggers] + for y in x.syntactic_object_set: + if y.category.label == features[0].label: + self.automerge(index,y.idx) + return True + elif type(y) == SyntacticObjectSet: + for z in y: + features = [trigger for trigger in x.triggers] + if z.category.label == features[0].label: + y = z + self.automerge(index,y.idx) + return True + return None + + # Rule 4 - Select + if len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) > 0: + print('Aplying rule 4: select(Y)') + list = [y.idx for y in self.stages[-1].lexical_array.the_list] + self.autoselect(min(list)) + return True + + # Rule 5 - External merge (Y,X) + if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 2: + print('Aplying rule 5: external merge (Y,X)') + for y in self.stages[-1].workspace.w: + features = [trigger for trigger in y.triggers] + if len(features) > 0 and x.category.label == features[0].label: + self.automerge(y.idx,index) + return True + # Rule 7 + if len(self.stages[-1].lexical_array.the_list) > 0: + print('Aplying rule 7: select(Z)') + index_z = min([y.idx for y in self.stages[-1].lexical_array.the_list]) + self.autoselect(index_z) + return True + return None + + # Rule 6 - External merge (X,Y) + if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 2: + features = [trigger for trigger in x.triggers] + for y in self.stages[-1].workspace.w: + if y.category.label == features[0].label: + self.automerge(index,y.idx) + return True + return None + + # Rule 8 - External merge + if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 3: + print('Aplying rule 8') + self.automerge(index,index-1) + return True + + def autoselect(self, index): + last_stage = self.stages[-1] + lexical_item_token = last_stage.lexical_array.find_lexical_array(index) + new_stage = last_stage.select_stage(lexical_item_token) + self.stages.append(new_stage) + if self.autotf(index) == None: + print('Derivation failed') + print('') + return + + def automerge(self, idx1, idx2): + last_stage = self.stages[-1] + try: + new_stage = last_stage.merge_stage(idx1, idx2) + except InteractionError: + print('Derivation failed') + return + self.stages.append(new_stage) + #print('Stage: ',len(self.stages)) + index = max([x.idx for x in last_stage.workspace.w]) + if self.autotf(index) == None: + print('Derivation failed') + print('') + return + + def autoderive(self): + self.autoselect(0) + +def word_to_lex(lexicon,word): + """ Matches a word with its lexical items + Return None if word is not in lexicon """ + lexical_item = None + for x in lexicon.lex: + if (list(x.phon))[0].label == word: + lexical_item = x + return lexical_item + +def phrase_to_list(lexicon, phrase): + """ Param: phrase (str) + Return: list of lexical_item """ + phrase = phrase.split() + list = [word_to_lex(lexicon, w) for w in phrase] + list.reverse() + return list + +def parse(phrase): + lexicon = Lexicon() + ug = UniversalGrammar(set(), set(), set()) + i_lang = ILanguage(lexicon, ug) + list = phrase_to_list(lexicon, phrase) + deriv = Derivation(i_lang, word_list=list) + deriv.autoderive() + del deriv From be86065ac045e2c76d882d64ed142051f32edf42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 17 Jul 2022 16:52:14 -0300 Subject: [PATCH 03/34] =?UTF-8?q?Agrego=20palabras=20en=20espa=C3=B1ol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/lexicon.xml | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/data/lexicon.xml b/data/lexicon.xml index 52848ee..a72b666 100644 --- a/data/lexicon.xml +++ b/data/lexicon.xml @@ -16,16 +16,39 @@ none - ate + comió V - N - N + D + D + + none + + + ladra + + V + D none - apples + manzanas + + D + + none + + + el + + D + N + + none + + + perro N From b4b114ef8a85ce462e4f8e98bf71c2fc48af8951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 17 Jul 2022 16:53:02 -0300 Subject: [PATCH 04/34] Algoritmo de parser.py --- derivations/rules.pdf | Bin 0 -> 34650 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 derivations/rules.pdf diff --git a/derivations/rules.pdf b/derivations/rules.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3dbf14f8a6f11d236a2861851f794665842b08fc GIT binary patch literal 34650 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
m<-5^=Q0v=J7I24XZ7DJ9C- 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 W*2M8KG((tA6X)f%*zIl}Z^aKZ@-yrJ literal 0 HcmV?d00001 From 24bcadef403c17623489580fdf3074163fdb29a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 17 Jul 2022 16:53:38 -0300 Subject: [PATCH 05/34] Agrego parser --- derivations/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/derivations/__init__.py b/derivations/__init__.py index 00c0886..d80bd89 100644 --- a/derivations/__init__.py +++ b/derivations/__init__.py @@ -1,3 +1,4 @@ -__all__ = ["Derivation"] +__all__ = ["Derivation","parse","phrase_to_list","word_to_lex"] -from .derivation import * \ No newline at end of file +from .derivation import * +from .parser import * \ No newline at end of file From 98b066e786a07596cf0c9dd3784e191a319c26f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sat, 23 Jul 2022 20:51:54 -0300 Subject: [PATCH 06/34] Transfer y restricciones al merge interno --- derivations/parser.py | 90 ++++++++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/derivations/parser.py b/derivations/parser.py index caed95c..a4841be 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -3,40 +3,78 @@ class Derivation(derivation.Derivation): + sentence_to_parse = '' + transferred_sentence = [] + + def transfer(self,so): + list_from_set = list(so.syntactic_object_set) + x,y = list_from_set[0],list_from_set[1] + if type(x) == LexicalItemToken and type(y) == LexicalItemToken: + if len(x.triggers) != 0: # if head + self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) + self.transferred_sentence.append(list(y.lexical_item.phon)[0].label) + else: # if complement + self.transferred_sentence.append(list(y.lexical_item.phon)[0].label) + self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) + elif type(x) == SyntacticObjectSet and type(y) == SyntacticObjectSet: + if len(x.triggers) != 0: # if head + self.transfer(y) + self.transfer(x) + else: # if specifier + self.transfer(x) + self.transfer(y) + else: # internal merge + if type(x) == LexicalItemToken: + self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) + for z in list(y.syntactic_object_set): + if z.lexical_item != x.lexical_item: + self.transferred_sentence.append(list(z.lexical_item.phon)[0].label) + def autotf(self, index): self.print_derivation() x = self.stages[-1].workspace.find_workspace(index) # get x # Rule 1 - Successful derivation if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0: - print("Successfull parsing!") - print('') - return True + print('Aplying transfer') + self.transfer(x) + self.transferred_sentence = ' '.join(self.transferred_sentence) + if self.transferred_sentence == self.sentence_to_parse: + print("Successfull parsing!") + print('') + return True + else: + #print('Transferred sentence: ',self.transferred_sentence) + return None # Rule 2 if len(x.triggers) > 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) != SyntacticObjectSet: return None # Rule 3 - Internal merge - if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) == SyntacticObjectSet: - print('Aplying rule 3: internal merge (X,Y)') + if len(x.triggers) == 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) == SyntacticObjectSet: features = [trigger for trigger in x.triggers] - for y in x.syntactic_object_set: - if y.category.label == features[0].label: - self.automerge(index,y.idx) - return True - elif type(y) == SyntacticObjectSet: - for z in y: - features = [trigger for trigger in x.triggers] - if z.category.label == features[0].label: - y = z - self.automerge(index,y.idx) - return True + if features[0].label[-1] == '/': + features[0].label = features[0].label[0:-1] + print('Aplying rule 3: internal merge (X,Y)') + print('') + for y in x.syntactic_object_set: + if y.category.label == features[0].label: + self.automerge(index,y.idx) + return True + elif type(y) == SyntacticObjectSet: + for z in y: + features = [trigger for trigger in x.triggers] + if z.category.label == features[0].label: + y = z + self.automerge(index,y.idx) + return True return None # Rule 4 - Select if len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) > 0: print('Aplying rule 4: select(Y)') + print('') list = [y.idx for y in self.stages[-1].lexical_array.the_list] self.autoselect(min(list)) return True @@ -44,6 +82,7 @@ def autotf(self, index): # Rule 5 - External merge (Y,X) if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 2: print('Aplying rule 5: external merge (Y,X)') + print('') for y in self.stages[-1].workspace.w: features = [trigger for trigger in y.triggers] if len(features) > 0 and x.category.label == features[0].label: @@ -52,6 +91,7 @@ def autotf(self, index): # Rule 7 if len(self.stages[-1].lexical_array.the_list) > 0: print('Aplying rule 7: select(Z)') + print('') index_z = min([y.idx for y in self.stages[-1].lexical_array.the_list]) self.autoselect(index_z) return True @@ -59,6 +99,8 @@ def autotf(self, index): # Rule 6 - External merge (X,Y) if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 2: + print('Aplying rule 6: external merge (X,Y)') + print('') features = [trigger for trigger in x.triggers] for y in self.stages[-1].workspace.w: if y.category.label == features[0].label: @@ -68,7 +110,8 @@ def autotf(self, index): # Rule 8 - External merge if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 3: - print('Aplying rule 8') + print('Aplying rule 8: external merge(Z,Y)') + print('') self.automerge(index,index-1) return True @@ -97,7 +140,8 @@ def automerge(self, idx1, idx2): print('') return - def autoderive(self): + def autoderive(self,sentence): + self.sentence_to_parse = sentence self.autoselect(0) def word_to_lex(lexicon,word): @@ -117,11 +161,11 @@ def phrase_to_list(lexicon, phrase): list.reverse() return list -def parse(phrase): - lexicon = Lexicon() +def parse(sentence,filename="lexicon.xml"): + lexicon = Lexicon(filename) ug = UniversalGrammar(set(), set(), set()) i_lang = ILanguage(lexicon, ug) - list = phrase_to_list(lexicon, phrase) + list = phrase_to_list(lexicon, sentence) deriv = Derivation(i_lang, word_list=list) - deriv.autoderive() - del deriv + deriv.autoderive(sentence) + del deriv \ No newline at end of file From 53e40092556dd53d4194f83b7f2db3b832a4071c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sat, 23 Jul 2022 20:53:00 -0300 Subject: [PATCH 07/34] Cambio de interfaz --- main.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/main.py b/main.py index c8940ed..814bda1 100644 --- a/main.py +++ b/main.py @@ -1,23 +1,61 @@ from derivations import * from structures import * +import os -# def main(): -# lexicon = Lexicon() -# counter = 0 -# ug = UniversalGrammar(set(), set(), set()) # todo: add feature import to UG -# i_lang = ILanguage(lexicon, ug) -# derivation = Derivation(i_lang, word_list=list(i_lang.lexicon.lex)) -# derivation.derive() +filename = "lexicon.xml" + +def print_grammars(): + global filename + full_path = os.path.abspath("data") + print(full_path) + filenames = [f for f in os.listdir(full_path) if os.path.isfile(os.path.join(full_path, f))] + for f in filenames: + if f == filename: + filenames.pop(filenames.index(f)) + filenames.insert(0, f) + indexes = {i:f for i,f in enumerate(filenames)} + print(f'{len(indexes)} grammar(s) found: ') + for key,value in indexes.items(): + if key == 0: + print(f"{key}: '{value}' (current)") + else: + print(f"{key}: '{value}'") + return indexes + +def manual_derivation(filename=filename): + lexicon = Lexicon(filename) + counter = 0 + ug = UniversalGrammar(set(), set(), set()) # todo: add feature import to UG + i_lang = ILanguage(lexicon, ug) + derivation = Derivation(i_lang, word_list=list(i_lang.lexicon.lex)) + derivation.derive() def main(): + global filename while True: - sentence = input("Insert a sentence to parse or press (f) to finish: ") - if sentence != 'f': + new_line = "\n" + user_input = input(f'Select an option and press enter:{new_line*2}1 - Parser{new_line}2 - Manual derivation{new_line}3 - Change default grammar{new_line}4 - Quit{new_line*2}') + if user_input == '1': + sentence = input(f'Insert a sentence to parse:{new_line}') try: - parse(sentence) + #agregar preprocesamiento + parse(sentence,filename) except AssertionError: - print("Some words are not in the lexicon") - else: + print(f"{new_line}[ERROR] Some words are not in the lexicon{new_line}") + elif user_input == '2': + manual_derivation(filename) + elif user_input == '3': + while True: + indexes = print_grammars() + file_index = input(f'Select the index of the new grammar:{new_line}') + try: + filename = indexes[int(file_index)] + break + except: + print(f'{new_line}[ERROR] Insert a valid option{new_line}') + elif user_input == '4': break + else: + print('Select a valid option (1-4)') main() \ No newline at end of file From e099525a30746ce295b3be3c8dcee50a26acdf9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sat, 23 Jul 2022 20:53:47 -0300 Subject: [PATCH 08/34] Rasgos de merge interno --- data/lexicon.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/data/lexicon.xml b/data/lexicon.xml index a72b666..c530ae7 100644 --- a/data/lexicon.xml +++ b/data/lexicon.xml @@ -23,6 +23,15 @@ D none + + + cayó + + V + D + D/ + + none ladra From b7d0149d10c6bdb025025904734301947f4370de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sat, 23 Jul 2022 20:54:35 -0300 Subject: [PATCH 09/34] =?UTF-8?q?Par=C3=A1metro=20opcional=20filename?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- structures/lexicon.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/structures/lexicon.py b/structures/lexicon.py index 5af8620..24fbf92 100644 --- a/structures/lexicon.py +++ b/structures/lexicon.py @@ -11,10 +11,13 @@ class Lexicon(object): """ Definition 3: A lexicon is a finite set of lexical items. """ - def __init__(self): + def __init__(self,file_name=None): self.lex: Set[LexicalItem] = set() # get the file path to lexicon data - file_name = "lexicon.xml" + if file_name == None: + file_name = "lexicon.xml" + else: + file_name = file_name full_file = os.path.abspath(os.path.join("data", file_name)) # create xml tree dom = ElementTree.parse(full_file) @@ -32,4 +35,4 @@ def __init__(self): cat_set = set([Cat_Feature(f) for f in w.find('syn/cat').text.strip().split()]) syn_set = sel_set.union(cat_set) new_word = LexicalItem(syn_set, sem_set, phon_set) - self.lex.add(new_word) + self.lex.add(new_word) \ No newline at end of file From 3752484b3885421220ddc3bf099fdc5544cb2fcd Mon Sep 17 00:00:00 2001 From: Facundo Date: Tue, 26 Jul 2022 00:22:54 -0300 Subject: [PATCH 10/34] =?UTF-8?q?Cambio=20operaci=C3=B3n=20transfer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/lexicon.xml | 25 +++---------- data/lexicon_en.xml | 66 +++++++++++++++++++++++++++++++++ derivations/parser.py | 36 ++++++++++++------ structures/syntactic_objects.py | 9 +++++ 4 files changed, 104 insertions(+), 32 deletions(-) create mode 100644 data/lexicon_en.xml diff --git a/data/lexicon.xml b/data/lexicon.xml index c530ae7..4baeb28 100644 --- a/data/lexicon.xml +++ b/data/lexicon.xml @@ -1,17 +1,10 @@ - K8 - - N - - none - - - runs + corrió V - N + D none @@ -25,26 +18,18 @@ none - cayó + llegó V D D/ none - - - ladra - - V - D - - none - manzanas + hueso - D + N none diff --git a/data/lexicon_en.xml b/data/lexicon_en.xml new file mode 100644 index 0000000..c530ae7 --- /dev/null +++ b/data/lexicon_en.xml @@ -0,0 +1,66 @@ + + + + K8 + + N + + none + + + runs + + V + N + + none + + + comió + + V + D + D + + none + + + cayó + + V + D + D/ + + none + + + ladra + + V + D + + none + + + manzanas + + D + + none + + + el + + D + N + + none + + + perro + + N + + none + + \ No newline at end of file diff --git a/derivations/parser.py b/derivations/parser.py index a4841be..2873fb2 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -9,6 +9,16 @@ class Derivation(derivation.Derivation): def transfer(self,so): list_from_set = list(so.syntactic_object_set) x,y = list_from_set[0],list_from_set[1] + W = list(self.stages[-1].workspace.w)[0] + + empty_li = LexicalItem({Cat_Feature('E')},{Sem_Feature('')},{Phon_Feature("")}) + self.i_lang.lexicon.lex.add(empty_li) + empty_so = LexicalItemToken(empty_li,0) + if x.is_final(y,W) == False: + x = empty_so + if y.is_final(x,W) == False: + y = empty_so + if type(x) == LexicalItemToken and type(y) == LexicalItemToken: if len(x.triggers) != 0: # if head self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) @@ -23,13 +33,14 @@ def transfer(self,so): else: # if specifier self.transfer(x) self.transfer(y) - else: # internal merge - if type(x) == LexicalItemToken: + else: + if type(x) == LexicalItemToken and type(y) == SyntacticObjectSet: self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) - for z in list(y.syntactic_object_set): - if z.lexical_item != x.lexical_item: - self.transferred_sentence.append(list(z.lexical_item.phon)[0].label) - + self.transfer(y) + elif type(y) == LexicalItemToken and type(x) == SyntacticObjectSet: + self.transferred_sentence.append(list(y.lexical_item.phon)[0].label) + self.transfer(x) + def autotf(self, index): self.print_derivation() x = self.stages[-1].workspace.find_workspace(index) # get x @@ -37,14 +48,15 @@ def autotf(self, index): # Rule 1 - Successful derivation if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0: print('Aplying transfer') + self.transferred_sentence = [] self.transfer(x) + self.transferred_sentence = [word for word in self.transferred_sentence if word != ''] self.transferred_sentence = ' '.join(self.transferred_sentence) if self.transferred_sentence == self.sentence_to_parse: print("Successfull parsing!") print('') return True else: - #print('Transferred sentence: ',self.transferred_sentence) return None # Rule 2 @@ -84,8 +96,8 @@ def autotf(self, index): print('Aplying rule 5: external merge (Y,X)') print('') for y in self.stages[-1].workspace.w: - features = [trigger for trigger in y.triggers] - if len(features) > 0 and x.category.label == features[0].label: + features = [trigger.label for trigger in y.triggers] + if len(features) > 0 and x.category.label in features: self.automerge(y.idx,index) return True # Rule 7 @@ -101,9 +113,9 @@ def autotf(self, index): if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 2: print('Aplying rule 6: external merge (X,Y)') print('') - features = [trigger for trigger in x.triggers] + features = [trigger.label for trigger in x.triggers] for y in self.stages[-1].workspace.w: - if y.category.label == features[0].label: + if y.category.label in features: self.automerge(index,y.idx) return True return None @@ -168,4 +180,4 @@ def parse(sentence,filename="lexicon.xml"): list = phrase_to_list(lexicon, sentence) deriv = Derivation(i_lang, word_list=list) deriv.autoderive(sentence) - del deriv \ No newline at end of file + #del deriv diff --git a/structures/syntactic_objects.py b/structures/syntactic_objects.py index ae810e7..bba7487 100644 --- a/structures/syntactic_objects.py +++ b/structures/syntactic_objects.py @@ -180,6 +180,15 @@ def sister_finder(self, container, sisterset = set()): else: return False + def is_final(self, syster, workspace): + if self.c_commands(self, workspace): + if syster.immediately_contains(self): + return True + else: + return False + else: + return True + def is_derivable(self, lexicon): # Not implemented """ DEFINITION 15: From 7898a74933fe4fbd274143cce43a855f0e5151b0 Mon Sep 17 00:00:00 2001 From: Facundo Date: Tue, 26 Jul 2022 19:38:30 -0300 Subject: [PATCH 11/34] Procesamiento de input (sentence) --- derivations/parser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/derivations/parser.py b/derivations/parser.py index 2873fb2..cbc9759 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -1,3 +1,4 @@ +import re from structures import * from derivations import derivation @@ -174,6 +175,7 @@ def phrase_to_list(lexicon, phrase): return list def parse(sentence,filename="lexicon.xml"): + sentence = ' '.join(((re.sub("[\.\,\!\?\:\;\-\=¿¡\|\(\)#\[\]\"]", "", sentence).lower()).split())) lexicon = Lexicon(filename) ug = UniversalGrammar(set(), set(), set()) i_lang = ILanguage(lexicon, ug) From e7b75c637763eeded97cf06521f0f409a48c142b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Thu, 28 Jul 2022 20:59:24 -0300 Subject: [PATCH 12/34] =?UTF-8?q?Categor=C3=ADas=20funcionales?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/lexicon.xml | 10 +++++++++- derivations/parser.py | 43 +++++++++++++++++++++++++++++++++++------- structures/__init__.py | 5 ++--- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/data/lexicon.xml b/data/lexicon.xml index 4baeb28..c82a688 100644 --- a/data/lexicon.xml +++ b/data/lexicon.xml @@ -4,7 +4,6 @@ corrió V - D none @@ -48,4 +47,13 @@ none + + [] + + v + V + D + + none + \ No newline at end of file diff --git a/derivations/parser.py b/derivations/parser.py index cbc9759..9777767 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -51,7 +51,7 @@ def autotf(self, index): print('Aplying transfer') self.transferred_sentence = [] self.transfer(x) - self.transferred_sentence = [word for word in self.transferred_sentence if word != ''] + self.transferred_sentence = [word for word in self.transferred_sentence if word != '' and not word.startswith('[')] self.transferred_sentence = ' '.join(self.transferred_sentence) if self.transferred_sentence == self.sentence_to_parse: print("Successfull parsing!") @@ -170,16 +170,45 @@ def phrase_to_list(lexicon, phrase): """ Param: phrase (str) Return: list of lexical_item """ phrase = phrase.split() - list = [word_to_lex(lexicon, w) for w in phrase] - list.reverse() - return list + new_list = [word_to_lex(lexicon, w) for w in phrase] + new_list.reverse() + return new_list + +def add_functional_categories(word_list,functional_list): + + def get_fc_index(word_list): + counter = 0 + for word in word_list: + index = word_list.index(word) + word_triggers = [t.label for t in word.syn if isinstance(t,Trigger_Feature)] + counter += len(word_triggers) + for w in word_list[index:index+2*len(word_triggers)]: + w_triggers = [t.label for t in w.syn if isinstance(t,Trigger_Feature)] + w_category = [c.label for c in word.syn if isinstance(c,Cat_Feature)] + if w_category in word_triggers: + counter += len(w_triggers) + return counter + + for word in word_list.copy(): + category = [c.label for c in word.syn if isinstance(c,Cat_Feature)] + for fcw in functional_list: + triggers = [t.label for t in fcw.syn if isinstance(t,Trigger_Feature)] + if category[0] in triggers: + if fcw not in word_list: + index = get_fc_index(word_list) + word_list.insert(index,fcw) + return word_list def parse(sentence,filename="lexicon.xml"): sentence = ' '.join(((re.sub("[\.\,\!\?\:\;\-\=¿¡\|\(\)#\[\]\"]", "", sentence).lower()).split())) lexicon = Lexicon(filename) ug = UniversalGrammar(set(), set(), set()) i_lang = ILanguage(lexicon, ug) - list = phrase_to_list(lexicon, sentence) - deriv = Derivation(i_lang, word_list=list) - deriv.autoderive(sentence) + word_list = phrase_to_list(lexicon, sentence) + functional_list = [item for item in lexicon.lex if (list(item.phon))[0].label.startswith('[')] + word_list = add_functional_categories(word_list,functional_list) + deriv = Derivation(i_lang, word_list=word_list) + #deriv.autoderive(sentence) + for w in word_list: + print(list(w.phon)[0].label) #del deriv diff --git a/structures/__init__.py b/structures/__init__.py index ddb8a71..302e7d3 100644 --- a/structures/__init__.py +++ b/structures/__init__.py @@ -1,7 +1,6 @@ -__all__ = ["Feature", "Syn_Feature", "Sem_Feature", "Cat_Feature", "Sel_Feature", "Phon_Feature", +__all__ = ["Feature", "Syn_Feature", "Sem_Feature", "Cat_Feature", "Sel_Feature", "Phon_Feature","Trigger_Feature", "UniversalGrammar", "ILanguage", "SyntacticObject", "SyntacticObjectSet", "LexicalItem", - "LexicalArray", "Workspace", "Stage", - "Lexicon", "LexicalItemToken", "tree", "InteractionError"] + "LexicalArray", "Workspace", "Stage","Lexicon", "LexicalItemToken", "tree", "InteractionError"] from .definitions import * from .features import * From 03b15b13cd7d0efa11a2732ad672664c5a439be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Thu, 28 Jul 2022 21:10:58 -0300 Subject: [PATCH 13/34] =?UTF-8?q?Modificaci=C3=B3n=20funci=C3=B3n=20get=5F?= =?UTF-8?q?place=5Ffc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- derivations/parser.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/derivations/parser.py b/derivations/parser.py index 9777767..f7669f5 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -176,17 +176,18 @@ def phrase_to_list(lexicon, phrase): def add_functional_categories(word_list,functional_list): - def get_fc_index(word_list): + def get_fc_place(word, word_list): counter = 0 - for word in word_list: - index = word_list.index(word) - word_triggers = [t.label for t in word.syn if isinstance(t,Trigger_Feature)] - counter += len(word_triggers) - for w in word_list[index:index+2*len(word_triggers)]: - w_triggers = [t.label for t in w.syn if isinstance(t,Trigger_Feature)] - w_category = [c.label for c in word.syn if isinstance(c,Cat_Feature)] - if w_category in word_triggers: - counter += len(w_triggers) + for w1 in word_list: + if w1 == word: + w1_index = word_list.index(word) + w1_triggers = [t.label for t in word.syn if isinstance(t,Trigger_Feature)] + counter += len(w1_triggers) + for w2 in word_list[index:index+2*len(word_triggers)]: + w2_triggers = [t.label for t in w2.syn if isinstance(t,Trigger_Feature)] + w2_category = [c.label for c in w2.syn if isinstance(c,Cat_Feature)] + if w2_category in w1_triggers and w1_index == 0: + counter += len(w2_triggers) return counter for word in word_list.copy(): @@ -195,8 +196,8 @@ def get_fc_index(word_list): triggers = [t.label for t in fcw.syn if isinstance(t,Trigger_Feature)] if category[0] in triggers: if fcw not in word_list: - index = get_fc_index(word_list) - word_list.insert(index,fcw) + place_to_insert = get_fc_place(word, word_list) + word_list.insert(place_to_insert,fcw) return word_list def parse(sentence,filename="lexicon.xml"): From 5e62ca62bb50b5c255890c9f90f9b0d58e146a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Fri, 29 Jul 2022 16:10:20 -0300 Subject: [PATCH 14/34] Elimino get_fc_place y modifico Rule 3 --- derivations/parser.py | 92 ++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/derivations/parser.py b/derivations/parser.py index f7669f5..f7153c6 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -45,7 +45,9 @@ def transfer(self,so): def autotf(self, index): self.print_derivation() x = self.stages[-1].workspace.find_workspace(index) # get x - + triggers = [trigger for trigger in x.triggers] + trigger_labels = [trigger.label for trigger in triggers] + # Rule 1 - Successful derivation if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0: print('Aplying transfer') @@ -61,44 +63,51 @@ def autotf(self, index): return None # Rule 2 - if len(x.triggers) > 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) != SyntacticObjectSet: + if len(triggers) > 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) != SyntacticObjectSet: return None # Rule 3 - Internal merge - if len(x.triggers) == 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) == SyntacticObjectSet: - features = [trigger for trigger in x.triggers] - if features[0].label[-1] == '/': - features[0].label = features[0].label[0:-1] - print('Aplying rule 3: internal merge (X,Y)') - print('') - for y in x.syntactic_object_set: - if y.category.label == features[0].label: - self.automerge(index,y.idx) - return True - elif type(y) == SyntacticObjectSet: - for z in y: - features = [trigger for trigger in x.triggers] - if z.category.label == features[0].label: - y = z - self.automerge(index,y.idx) - return True + if len(triggers) == 1 and triggers[0].label[-1] == '/' and len(self.stages[-1].workspace.w) == 1 and isinstance(x, SyntacticObjectSet): + triggers[0].label = triggers[0].label[0:-1] + print('Aplying rule 3: internal merge (X,Y)') + print('') + for y in x.syntactic_object_set: + if y.category.label == triggers[0].label: + self.automerge(index,y.idx) + return True + elif isinstance(y, SyntacticObjectSet): + for z in y.syntactic_object_set: + if z.category.label == triggers[0].label: + y = z + self.automerge(index,y.idx) + return True return None # Rule 4 - Select if len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) > 0: print('Aplying rule 4: select(Y)') print('') - list = [y.idx for y in self.stages[-1].lexical_array.the_list] - self.autoselect(min(list)) + for li in self.stages[-1].lexical_array.the_list: + if (list(li.lexical_item.phon)[0]).label.startswith('[') and len(x.triggers) == 0: + triggers_fc = [re.sub("/","",trigger.label) for trigger in li.triggers if trigger.label.startswith('/')] + if x.category.label in triggers_fc: + for trigger in li.triggers: + if trigger.label.startswith("/"): + trigger.label = re.sub("/","",trigger.label) + self.autoselect(li.idx) + return True + + list_of_idxs = [y.idx for y in self.stages[-1].lexical_array.the_list] + self.autoselect(min(list_of_idxs)) return True # Rule 5 - External merge (Y,X) - if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 2: + if len(triggers) == 0 and len(self.stages[-1].workspace.w) == 2: print('Aplying rule 5: external merge (Y,X)') print('') for y in self.stages[-1].workspace.w: - features = [trigger.label for trigger in y.triggers] - if len(features) > 0 and x.category.label in features: + triggers_y = [trigger.label for trigger in y.triggers] + if len(triggers_y) > 0 and x.category.label in triggers_y: self.automerge(y.idx,index) return True # Rule 7 @@ -111,12 +120,11 @@ def autotf(self, index): return None # Rule 6 - External merge (X,Y) - if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 2: + if len(triggers) > 0 and len(self.stages[-1].workspace.w) == 2: print('Aplying rule 6: external merge (X,Y)') print('') - features = [trigger.label for trigger in x.triggers] for y in self.stages[-1].workspace.w: - if y.category.label in features: + if y.category.label in trigger_labels: self.automerge(index,y.idx) return True return None @@ -175,29 +183,15 @@ def phrase_to_list(lexicon, phrase): return new_list def add_functional_categories(word_list,functional_list): - - def get_fc_place(word, word_list): - counter = 0 - for w1 in word_list: - if w1 == word: - w1_index = word_list.index(word) - w1_triggers = [t.label for t in word.syn if isinstance(t,Trigger_Feature)] - counter += len(w1_triggers) - for w2 in word_list[index:index+2*len(word_triggers)]: - w2_triggers = [t.label for t in w2.syn if isinstance(t,Trigger_Feature)] - w2_category = [c.label for c in w2.syn if isinstance(c,Cat_Feature)] - if w2_category in w1_triggers and w1_index == 0: - counter += len(w2_triggers) - return counter - - for word in word_list.copy(): + words_with_fc = [] + for word in word_list: category = [c.label for c in word.syn if isinstance(c,Cat_Feature)] for fcw in functional_list: - triggers = [t.label for t in fcw.syn if isinstance(t,Trigger_Feature)] + triggers = [re.sub("/","",t.label) for t in fcw.syn if isinstance(t,Trigger_Feature) and t.label.startswith('/')] if category[0] in triggers: - if fcw not in word_list: - place_to_insert = get_fc_place(word, word_list) - word_list.insert(place_to_insert,fcw) + if fcw not in word_list and word not in words_with_fc: + word_list.append(fcw) + words_with_fc.append(word) return word_list def parse(sentence,filename="lexicon.xml"): @@ -209,7 +203,5 @@ def parse(sentence,filename="lexicon.xml"): functional_list = [item for item in lexicon.lex if (list(item.phon))[0].label.startswith('[')] word_list = add_functional_categories(word_list,functional_list) deriv = Derivation(i_lang, word_list=word_list) - #deriv.autoderive(sentence) - for w in word_list: - print(list(w.phon)[0].label) + deriv.autoderive(sentence) #del deriv From c18229d5398d16894355e45c4f906f950c7937ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Fri, 29 Jul 2022 16:10:47 -0300 Subject: [PATCH 15/34] =?UTF-8?q?Agrego=20categor=C3=ADas=20funcionales?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/lexicon.xml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/data/lexicon.xml b/data/lexicon.xml index c82a688..783811d 100644 --- a/data/lexicon.xml +++ b/data/lexicon.xml @@ -12,7 +12,6 @@ V D - D none @@ -51,9 +50,25 @@ [] v - V + /V D none + + [] + + T + /v + + none + + + [] + + C + /T + + none + \ No newline at end of file From db24161132bed7287212d90cf2a0f05c642e10c6 Mon Sep 17 00:00:00 2001 From: Facundo Date: Fri, 29 Jul 2022 23:17:38 -0300 Subject: [PATCH 16/34] Manejo de duplicados (incompleto) --- derivations/parser.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/derivations/parser.py b/derivations/parser.py index f7153c6..028d99a 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -1,4 +1,5 @@ import re +import itertools from structures import * from derivations import derivation @@ -194,6 +195,21 @@ def add_functional_categories(word_list,functional_list): words_with_fc.append(word) return word_list +def get_all_posible_item_lists(lexicon): + duplicates = [] + for item1 in lexicon.lex: + phon1 = list(item1.phon)[0].label + category1 = list(item1.syn)[0].label + for item2 in lexicon.lex: + phon2 = list(item2.phon)[0].label + category2 = list(item2.syn)[0].label + if phon1 == phon2 and category1 == category2: + duplicates.append([item1,item2]) + + all_possibilities = list(itertools.product(*duplicates)) + return all_possibilities + + def parse(sentence,filename="lexicon.xml"): sentence = ' '.join(((re.sub("[\.\,\!\?\:\;\-\=¿¡\|\(\)#\[\]\"]", "", sentence).lower()).split())) lexicon = Lexicon(filename) From 275298545bb0abaaa201f5ccebd152c0aac5dac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sat, 30 Jul 2022 17:24:52 -0300 Subject: [PATCH 17/34] Debug --- data/lexicon.xml | 9 +++ derivations/parser.py | 128 ++++++++++++++++++++++++++---------------- main.py | 1 - 3 files changed, 90 insertions(+), 48 deletions(-) diff --git a/data/lexicon.xml b/data/lexicon.xml index 783811d..673122a 100644 --- a/data/lexicon.xml +++ b/data/lexicon.xml @@ -55,6 +55,15 @@ none + + [] + + v + /V + D/ + + none + [] diff --git a/derivations/parser.py b/derivations/parser.py index 028d99a..046d93a 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -43,20 +43,23 @@ def transfer(self,so): self.transferred_sentence.append(list(y.lexical_item.phon)[0].label) self.transfer(x) - def autotf(self, index): - self.print_derivation() + def autotf(self, index, debug = False): + if debug == True: + self.print_derivation() x = self.stages[-1].workspace.find_workspace(index) # get x triggers = [trigger for trigger in x.triggers] trigger_labels = [trigger.label for trigger in triggers] # Rule 1 - Successful derivation if len(x.triggers) == 0 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0: - print('Aplying transfer') + if debug == True: + print('Aplying transfer') self.transferred_sentence = [] self.transfer(x) self.transferred_sentence = [word for word in self.transferred_sentence if word != '' and not word.startswith('[')] self.transferred_sentence = ' '.join(self.transferred_sentence) if self.transferred_sentence == self.sentence_to_parse: + self.print_derivation() print("Successfull parsing!") print('') return True @@ -70,8 +73,9 @@ def autotf(self, index): # Rule 3 - Internal merge if len(triggers) == 1 and triggers[0].label[-1] == '/' and len(self.stages[-1].workspace.w) == 1 and isinstance(x, SyntacticObjectSet): triggers[0].label = triggers[0].label[0:-1] - print('Aplying rule 3: internal merge (X,Y)') - print('') + if debug == True: + print('Aplying rule 3: internal merge (X,Y)') + print('') for y in x.syntactic_object_set: if y.category.label == triggers[0].label: self.automerge(index,y.idx) @@ -86,8 +90,9 @@ def autotf(self, index): # Rule 4 - Select if len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) > 0: - print('Aplying rule 4: select(Y)') - print('') + if debug == True: + print('Aplying rule 4: select(Y)') + print('') for li in self.stages[-1].lexical_array.the_list: if (list(li.lexical_item.phon)[0]).label.startswith('[') and len(x.triggers) == 0: triggers_fc = [re.sub("/","",trigger.label) for trigger in li.triggers if trigger.label.startswith('/')] @@ -104,8 +109,9 @@ def autotf(self, index): # Rule 5 - External merge (Y,X) if len(triggers) == 0 and len(self.stages[-1].workspace.w) == 2: - print('Aplying rule 5: external merge (Y,X)') - print('') + if debug == True: + print('Aplying rule 5: external merge (Y,X)') + print('') for y in self.stages[-1].workspace.w: triggers_y = [trigger.label for trigger in y.triggers] if len(triggers_y) > 0 and x.category.label in triggers_y: @@ -113,8 +119,9 @@ def autotf(self, index): return True # Rule 7 if len(self.stages[-1].lexical_array.the_list) > 0: - print('Aplying rule 7: select(Z)') - print('') + if debug == True: + print('Aplying rule 7: select(Z)') + print('') index_z = min([y.idx for y in self.stages[-1].lexical_array.the_list]) self.autoselect(index_z) return True @@ -122,8 +129,9 @@ def autotf(self, index): # Rule 6 - External merge (X,Y) if len(triggers) > 0 and len(self.stages[-1].workspace.w) == 2: - print('Aplying rule 6: external merge (X,Y)') - print('') + if debug == True: + print('Aplying rule 6: external merge (X,Y)') + print('') for y in self.stages[-1].workspace.w: if y.category.label in trigger_labels: self.automerge(index,y.idx) @@ -132,54 +140,58 @@ def autotf(self, index): # Rule 8 - External merge if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 3: - print('Aplying rule 8: external merge(Z,Y)') - print('') + if debug == True: + print('Aplying rule 8: external merge(Z,Y)') + print('') self.automerge(index,index-1) return True - def autoselect(self, index): + def autoselect(self, index, debug=False): last_stage = self.stages[-1] lexical_item_token = last_stage.lexical_array.find_lexical_array(index) new_stage = last_stage.select_stage(lexical_item_token) self.stages.append(new_stage) - if self.autotf(index) == None: - print('Derivation failed') - print('') + if self.autotf(index,debug=debug) == None: + if debug == True: + print('Derivation failed') + print('') return - def automerge(self, idx1, idx2): + def automerge(self, idx1, idx2, debug = False): last_stage = self.stages[-1] try: new_stage = last_stage.merge_stage(idx1, idx2) except InteractionError: - print('Derivation failed') + if debug == True: + print('Derivation failed') return self.stages.append(new_stage) #print('Stage: ',len(self.stages)) index = max([x.idx for x in last_stage.workspace.w]) - if self.autotf(index) == None: - print('Derivation failed') - print('') + if self.autotf(index,debug=debug) == None: + if debug == True: + print('Derivation failed') + print('') return - def autoderive(self,sentence): + def autoderive(self,sentence,debug = False): self.sentence_to_parse = sentence - self.autoselect(0) + self.autoselect(0,debug=debug) -def word_to_lex(lexicon,word): +def word_to_lex(lexicon_list,word): """ Matches a word with its lexical items Return None if word is not in lexicon """ lexical_item = None - for x in lexicon.lex: + for x in lexicon_list: if (list(x.phon))[0].label == word: lexical_item = x return lexical_item -def phrase_to_list(lexicon, phrase): +def phrase_to_list(lexicon_list, phrase): """ Param: phrase (str) Return: list of lexical_item """ phrase = phrase.split() - new_list = [word_to_lex(lexicon, w) for w in phrase] + new_list = [word_to_lex(lexicon_list, word) for word in phrase] new_list.reverse() return new_list @@ -195,29 +207,51 @@ def add_functional_categories(word_list,functional_list): words_with_fc.append(word) return word_list -def get_all_posible_item_lists(lexicon): +def get_possible_lexicons(lexicon): duplicates = [] for item1 in lexicon.lex: - phon1 = list(item1.phon)[0].label - category1 = list(item1.syn)[0].label + phon1 = [ph.label for ph in item1.phon if isinstance(ph,Phon_Feature)][0] + category1 = [c.label for c in item1.syn if isinstance(c,Cat_Feature)][0] for item2 in lexicon.lex: - phon2 = list(item2.phon)[0].label - category2 = list(item2.syn)[0].label - if phon1 == phon2 and category1 == category2: - duplicates.append([item1,item2]) - - all_possibilities = list(itertools.product(*duplicates)) + phon2 = [ph.label for ph in item2.phon if isinstance(ph,Phon_Feature)][0] + category2 = [c.label for c in item2.syn if isinstance(c,Cat_Feature)][0] + if phon1 == phon2 and category1 == category2 and item1 != item2: + if not any(item1 in list for list in duplicates): + duplicates.append([item1,item2]) + all_fc_possibilities = list(itertools.product(*duplicates)) + lexicon_list = [item for item in lexicon.lex if not any(item in list for list in duplicates)] + all_possibilities = [] + for possibility in all_fc_possibilities: + new_list = [item for item in possibility] + [item for item in lexicon_list] + all_possibilities.append(new_list) return all_possibilities - -def parse(sentence,filename="lexicon.xml"): +def parse(sentence,filename="lexicon.xml",debug=False): sentence = ' '.join(((re.sub("[\.\,\!\?\:\;\-\=¿¡\|\(\)#\[\]\"]", "", sentence).lower()).split())) lexicon = Lexicon(filename) + possible_lexicons = get_possible_lexicons(lexicon) ug = UniversalGrammar(set(), set(), set()) i_lang = ILanguage(lexicon, ug) - word_list = phrase_to_list(lexicon, sentence) - functional_list = [item for item in lexicon.lex if (list(item.phon))[0].label.startswith('[')] - word_list = add_functional_categories(word_list,functional_list) - deriv = Derivation(i_lang, word_list=word_list) - deriv.autoderive(sentence) - #del deriv + debug = debug + debug_list = [] + for i in range(len(possible_lexicons)): + word_list = phrase_to_list(possible_lexicons[i], sentence) + print('1:',len(word_list)) + functional_list = [item for item in possible_lexicons[i] if (list(item.phon))[0].label.startswith('[')] + word_list.extend(functional_list) + #word_list = add_functional_categories(word_list,functional_list) + print('2:',len(word_list)) + deriv = Derivation(i_lang, word_list=word_list) + debug_list.append(functional_list) + return(debug_list) + deriv.autoderive(sentence, debug) + #return(debug_list) + +a = parse('el perro llegó') +for i in a: + print('') + for z in i: + category1 = [c.label for c in z.syn if isinstance(c,Cat_Feature)] + trigger2 = [t.label for t in z.syn if isinstance(t,Trigger_Feature)] + print(category1) + print(trigger2) \ No newline at end of file diff --git a/main.py b/main.py index 814bda1..131b410 100644 --- a/main.py +++ b/main.py @@ -38,7 +38,6 @@ def main(): if user_input == '1': sentence = input(f'Insert a sentence to parse:{new_line}') try: - #agregar preprocesamiento parse(sentence,filename) except AssertionError: print(f"{new_line}[ERROR] Some words are not in the lexicon{new_line}") From 2def01b1652deb7823fa15c9a0fce80109aac314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 31 Jul 2022 10:55:07 -0300 Subject: [PATCH 18/34] =?UTF-8?q?Agrego=20traducci=C3=B3n=20al=20ingl?= =?UTF-8?q?=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/lexicon_en.xml | 77 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/data/lexicon_en.xml b/data/lexicon_en.xml index c530ae7..8bd4476 100644 --- a/data/lexicon_en.xml +++ b/data/lexicon_en.xml @@ -1,31 +1,22 @@ - K8 - - N - - none - - - runs + run V - N none - comió + ate V D - D none - cayó + arrived V D @@ -33,34 +24,84 @@ none - - ladra + + gave V D + P none - manzanas + bone - D + N none - el + the D N none + + + to + + P + D + + none + + + dog + + N + + none - perro + owner N none + + [] + + v + /V + D + + none + + + [] + + v + /V + D/ + + none + + + [] + + T + /v + + none + + + [] + + C + /T + + none + \ No newline at end of file From 10157f9f0726ae3f27d177a5810dde72f074da23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 31 Jul 2022 10:55:28 -0300 Subject: [PATCH 19/34] Agrego stage-by-stage view --- main.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 131b410..69a6db7 100644 --- a/main.py +++ b/main.py @@ -32,13 +32,15 @@ def manual_derivation(filename=filename): def main(): global filename + debug = False + enable = 'Enable' while True: new_line = "\n" - user_input = input(f'Select an option and press enter:{new_line*2}1 - Parser{new_line}2 - Manual derivation{new_line}3 - Change default grammar{new_line}4 - Quit{new_line*2}') + user_input = input(f'Select an option and press enter:{new_line*2}1 - Parser{new_line}2 - Manual derivation{new_line}3 - Change default grammar{new_line}4 - {enable} stage-by-stage view{new_line}5 - Quit{new_line*2}') if user_input == '1': - sentence = input(f'Insert a sentence to parse:{new_line}') + sentence = input(f'{new_line}Insert a sentence to parse:{new_line*2}') try: - parse(sentence,filename) + parse(sentence,filename,debug) except AssertionError: print(f"{new_line}[ERROR] Some words are not in the lexicon{new_line}") elif user_input == '2': @@ -53,6 +55,12 @@ def main(): except: print(f'{new_line}[ERROR] Insert a valid option{new_line}') elif user_input == '4': + debug = not debug + if debug == False: + enable = "Enable" + else: + enable = 'Disable' + elif user_input == '5': break else: print('Select a valid option (1-4)') From 538edfa04b4dcc58fa03f6f63eae1f45df09f0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 31 Jul 2022 10:55:53 -0300 Subject: [PATCH 20/34] =?UTF-8?q?Corrijo=20funci=C3=B3n=20parse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- derivations/parser.py | 105 +++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/derivations/parser.py b/derivations/parser.py index 046d93a..4d4e40f 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -1,10 +1,10 @@ import re -import itertools from structures import * from derivations import derivation class Derivation(derivation.Derivation): + successfull_derivation = None sentence_to_parse = '' transferred_sentence = [] @@ -21,14 +21,14 @@ def transfer(self,so): if y.is_final(x,W) == False: y = empty_so - if type(x) == LexicalItemToken and type(y) == LexicalItemToken: + if isinstance(x,LexicalItemToken) and isinstance(y,LexicalItemToken): if len(x.triggers) != 0: # if head self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) self.transferred_sentence.append(list(y.lexical_item.phon)[0].label) else: # if complement self.transferred_sentence.append(list(y.lexical_item.phon)[0].label) self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) - elif type(x) == SyntacticObjectSet and type(y) == SyntacticObjectSet: + elif isinstance(x,SyntacticObjectSet) and isinstance(y,SyntacticObjectSet): if len(x.triggers) != 0: # if head self.transfer(y) self.transfer(x) @@ -36,16 +36,16 @@ def transfer(self,so): self.transfer(x) self.transfer(y) else: - if type(x) == LexicalItemToken and type(y) == SyntacticObjectSet: + if isinstance(x,LexicalItemToken) and isinstance(y,SyntacticObjectSet): self.transferred_sentence.append(list(x.lexical_item.phon)[0].label) self.transfer(y) - elif type(y) == LexicalItemToken and type(x) == SyntacticObjectSet: + elif isinstance(y,LexicalItemToken) and isinstance(x,SyntacticObjectSet): self.transferred_sentence.append(list(y.lexical_item.phon)[0].label) self.transfer(x) def autotf(self, index, debug = False): if debug == True: - self.print_derivation() + self.print_derivation() x = self.stages[-1].workspace.find_workspace(index) # get x triggers = [trigger for trigger in x.triggers] trigger_labels = [trigger.label for trigger in triggers] @@ -58,33 +58,38 @@ def autotf(self, index, debug = False): self.transfer(x) self.transferred_sentence = [word for word in self.transferred_sentence if word != '' and not word.startswith('[')] self.transferred_sentence = ' '.join(self.transferred_sentence) + if debug == True: + print('Transferred sentence:',self.transferred_sentence) if self.transferred_sentence == self.sentence_to_parse: - self.print_derivation() - print("Successfull parsing!") - print('') + if debug == True: + self.print_derivation() + print("Successfull parsing!") + print('') + self.successfull_derivation = self return True else: return None # Rule 2 - if len(triggers) > 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and type(x) != SyntacticObjectSet: + if len(triggers) > 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and isinstance(x,SyntacticObjectSet): return None # Rule 3 - Internal merge if len(triggers) == 1 and triggers[0].label[-1] == '/' and len(self.stages[-1].workspace.w) == 1 and isinstance(x, SyntacticObjectSet): + #x.lexical_item.copy_features() triggers[0].label = triggers[0].label[0:-1] if debug == True: print('Aplying rule 3: internal merge (X,Y)') print('') for y in x.syntactic_object_set: if y.category.label == triggers[0].label: - self.automerge(index,y.idx) + self.automerge(index,y.idx, debug=debug) return True elif isinstance(y, SyntacticObjectSet): for z in y.syntactic_object_set: if z.category.label == triggers[0].label: y = z - self.automerge(index,y.idx) + self.automerge(index,y.idx,debug=debug) return True return None @@ -100,11 +105,11 @@ def autotf(self, index, debug = False): for trigger in li.triggers: if trigger.label.startswith("/"): trigger.label = re.sub("/","",trigger.label) - self.autoselect(li.idx) + self.autoselect(li.idx,debug) return True list_of_idxs = [y.idx for y in self.stages[-1].lexical_array.the_list] - self.autoselect(min(list_of_idxs)) + self.autoselect(min(list_of_idxs),debug) return True # Rule 5 - External merge (Y,X) @@ -115,7 +120,7 @@ def autotf(self, index, debug = False): for y in self.stages[-1].workspace.w: triggers_y = [trigger.label for trigger in y.triggers] if len(triggers_y) > 0 and x.category.label in triggers_y: - self.automerge(y.idx,index) + self.automerge(y.idx,index,debug=debug) return True # Rule 7 if len(self.stages[-1].lexical_array.the_list) > 0: @@ -123,7 +128,7 @@ def autotf(self, index, debug = False): print('Aplying rule 7: select(Z)') print('') index_z = min([y.idx for y in self.stages[-1].lexical_array.the_list]) - self.autoselect(index_z) + self.autoselect(index_z,debug) return True return None @@ -134,7 +139,7 @@ def autotf(self, index, debug = False): print('') for y in self.stages[-1].workspace.w: if y.category.label in trigger_labels: - self.automerge(index,y.idx) + self.automerge(index,y.idx,debug=debug) return True return None @@ -143,10 +148,16 @@ def autotf(self, index, debug = False): if debug == True: print('Aplying rule 8: external merge(Z,Y)') print('') - self.automerge(index,index-1) - return True - - def autoselect(self, index, debug=False): + z = self.stages[-1].workspace.find_workspace(index-1) + if z in self.stages[-1].workspace.w: + self.automerge(index,index-1,debug=debug) + return True + else: + z_idx = max([so.idx for so in self.stages[-1].workspace.w if so!=x]) + self.automerge(index,z_idx,debug=debug) + return True + + def autoselect(self, index, debug): last_stage = self.stages[-1] lexical_item_token = last_stage.lexical_array.find_lexical_array(index) new_stage = last_stage.select_stage(lexical_item_token) @@ -157,7 +168,7 @@ def autoselect(self, index, debug=False): print('') return - def automerge(self, idx1, idx2, debug = False): + def automerge(self, idx1, idx2, debug): last_stage = self.stages[-1] try: new_stage = last_stage.merge_stage(idx1, idx2) @@ -174,9 +185,9 @@ def automerge(self, idx1, idx2, debug = False): print('') return - def autoderive(self,sentence,debug = False): + def autoderive(self, sentence, debug): self.sentence_to_parse = sentence - self.autoselect(0,debug=debug) + self.autoselect(0, debug) def word_to_lex(lexicon_list,word): """ Matches a word with its lexical items @@ -198,6 +209,8 @@ def phrase_to_list(lexicon_list, phrase): def add_functional_categories(word_list,functional_list): words_with_fc = [] for word in word_list: + if word == None: + raise AssertionError category = [c.label for c in word.syn if isinstance(c,Cat_Feature)] for fcw in functional_list: triggers = [re.sub("/","",t.label) for t in fcw.syn if isinstance(t,Trigger_Feature) and t.label.startswith('/')] @@ -208,10 +221,18 @@ def add_functional_categories(word_list,functional_list): return word_list def get_possible_lexicons(lexicon): + import itertools duplicates = [] for item1 in lexicon.lex: phon1 = [ph.label for ph in item1.phon if isinstance(ph,Phon_Feature)][0] category1 = [c.label for c in item1.syn if isinstance(c,Cat_Feature)][0] + for duplicate in duplicates: + for item3 in duplicate: + phon3 = [ph.label for ph in item3.phon if isinstance(ph,Phon_Feature)][0] + category3 = [c.label for c in item3.syn if isinstance(c,Cat_Feature)][0] + if phon1 == phon3 and phon1 == category3 and item1 not in duplicate: + duplicate.append(item1) + pass for item2 in lexicon.lex: phon2 = [ph.label for ph in item2.phon if isinstance(ph,Phon_Feature)][0] category2 = [c.label for c in item2.syn if isinstance(c,Cat_Feature)][0] @@ -226,32 +247,30 @@ def get_possible_lexicons(lexicon): all_possibilities.append(new_list) return all_possibilities -def parse(sentence,filename="lexicon.xml",debug=False): +def parse(sentence,filename="lexicon.xml",debug=True): sentence = ' '.join(((re.sub("[\.\,\!\?\:\;\-\=¿¡\|\(\)#\[\]\"]", "", sentence).lower()).split())) lexicon = Lexicon(filename) possible_lexicons = get_possible_lexicons(lexicon) ug = UniversalGrammar(set(), set(), set()) i_lang = ILanguage(lexicon, ug) - debug = debug - debug_list = [] + successfull_derivations = [] for i in range(len(possible_lexicons)): word_list = phrase_to_list(possible_lexicons[i], sentence) - print('1:',len(word_list)) functional_list = [item for item in possible_lexicons[i] if (list(item.phon))[0].label.startswith('[')] - word_list.extend(functional_list) - #word_list = add_functional_categories(word_list,functional_list) - print('2:',len(word_list)) + word_list = add_functional_categories(word_list,functional_list) deriv = Derivation(i_lang, word_list=word_list) - debug_list.append(functional_list) - return(debug_list) deriv.autoderive(sentence, debug) - #return(debug_list) - -a = parse('el perro llegó') -for i in a: - print('') - for z in i: - category1 = [c.label for c in z.syn if isinstance(c,Cat_Feature)] - trigger2 = [t.label for t in z.syn if isinstance(t,Trigger_Feature)] - print(category1) - print(trigger2) \ No newline at end of file + if deriv.successfull_derivation != None: + successfull_derivations.append(deriv.successfull_derivation) + + if len(successfull_derivations) > 0: + print('') + print(len(successfull_derivations),"derivation(s)") + print('') + for derivation in successfull_derivations: + derivation.print_derivation() + print('') + else: + print('') + print('Sentence is not derivable.') + print('') \ No newline at end of file From 698512e27181e184c2c93a7baca8e08dd9736f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 31 Jul 2022 10:56:21 -0300 Subject: [PATCH 21/34] Deepcopy al lexicalitem --- structures/syntactic_objects.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/structures/syntactic_objects.py b/structures/syntactic_objects.py index bba7487..c396827 100644 --- a/structures/syntactic_objects.py +++ b/structures/syntactic_objects.py @@ -275,9 +275,10 @@ class LexicalItemToken(SyntacticObject): """ def __init__(self, lexical_item: LexicalItem, idx: int): super(LexicalItemToken, self).__init__(idx) - self.lexical_item = lexical_item - self.triggers = { f for f in self.lexical_item.syn if type(f) is Trigger_Feature } - cat_features = { f for f in self.lexical_item.syn if type(f) is Cat_Feature } + import copy + self.lexical_item = copy.deepcopy(lexical_item) + self.triggers = { copy.deepcopy(f) for f in self.lexical_item.syn if type(f) is Trigger_Feature } + cat_features = { copy.deepcopy(f) for f in self.lexical_item.syn if type(f) is Cat_Feature } assert len(cat_features) == 1 (category, ) = cat_features self.category = category From 048a56d80f7674c3cd172ea2540561d7ef477f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= Date: Sun, 31 Jul 2022 10:56:40 -0300 Subject: [PATCH 22/34] =?UTF-8?q?Categor=C3=ADas=20funcionales?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/lexicon.xml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/data/lexicon.xml b/data/lexicon.xml index 673122a..31f1900 100644 --- a/data/lexicon.xml +++ b/data/lexicon.xml @@ -24,6 +24,15 @@ none + + entregó + + V + D + P + + none + hueso @@ -38,6 +47,14 @@ N none + + + al + + P + N + + none perro @@ -45,6 +62,13 @@ N none + + + dueño + + N + + none [] From ec44c3791b67a8b16f1884fbb34d43eabad9c5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Sun, 31 Jul 2022 16:30:45 -0300 Subject: [PATCH 23/34] =?UTF-8?q?Actualizo=20documentaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67d4de4..c451570 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ -# minimalism -Implementation of A Formalization of Minimalist Syntax (Collins & Stabler, 2016) +# Parser minimalista +Este es un parser basado en la implementación hecha por Alex Warstadt del formalismo introducido en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. + +## Modo de uso +Para utilizar este programa es necesario ejecutar el archivo `main.py`, ubicado en la carpeta principal del repositorio. Ejecutado el archivo, aparecerá un menú de cinco opciones: + +**1. Parser** *(Permite introducir la oración a parsear)*
+**2. Manual derivation** *(Permite generar la derivación manualmente, utilizando el mecanismo original de Warstadt)*
+**3. Change grammar** *(Permite seleccionar una gramática distinta)*
+**4. Enable/disable stage-by-stage view** *(Cuando está activado, muestra el paso a paso de una derivación, incluyendo las reglas que fueron aplicadas)*
+**5. Quit** *(Finaliza el programa)* + +### La gramática +Por defecto, el Lexicon de la gramática se genera a partir del archivo `lexicon.xml` de la carpeta `data`. Este es un documento en formato XML que organiza el léxico como un árbol de nodos. A cada ítem le corresponde un elemento con la etiqueta ``, dentro del cual se insertan nuevos elementos con una etiqueta distinta para cada rasgo: la etiqueta `` corresponde a los rasgos fonológicos, `` a los sintácticos, y `` a los semánticos (aunque estos últimos no son utilizados en la implementación actual). Por ejemplo, la entrada léxica correspondiente a un verbo como "corrió" sería la siguiente: +``` + + corrió + + V + + none + + + comió + + V + D + + none + +``` +Los rasgos sintácticos se subdividen en rasgos categoriales (`Cat_Feature`) y de selección (`Trigger_Feature`). Los primeros se insertan dentro de la etiqueta ``; y los segundos, dentro de ``. From 7b4d16b3723b88902b936732c2b3d90ca035a7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Sun, 31 Jul 2022 17:02:29 -0300 Subject: [PATCH 24/34] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c451570..ed2c8af 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Parser minimalista -Este es un parser basado en la implementación hecha por Alex Warstadt del formalismo introducido en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. +Este es un parser basado en la implementación hecha por Alex Warstadt del formalismo descrito en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. ## Modo de uso Para utilizar este programa es necesario ejecutar el archivo `main.py`, ubicado en la carpeta principal del repositorio. Ejecutado el archivo, aparecerá un menú de cinco opciones: From de13a90ef8e2d5be049b34af9f85d4b769373995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Sun, 31 Jul 2022 17:47:05 -0300 Subject: [PATCH 25/34] Update README.md --- README.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ed2c8af..c326713 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,11 @@ Para utilizar este programa es necesario ejecutar el archivo `main.py`, ubicado **5. Quit** *(Finaliza el programa)* ### La gramática -Por defecto, el Lexicon de la gramática se genera a partir del archivo `lexicon.xml` de la carpeta `data`. Este es un documento en formato XML que organiza el léxico como un árbol de nodos. A cada ítem le corresponde un elemento con la etiqueta ``, dentro del cual se insertan nuevos elementos con una etiqueta distinta para cada rasgo: la etiqueta `` corresponde a los rasgos fonológicos, `` a los sintácticos, y `` a los semánticos (aunque estos últimos no son utilizados en la implementación actual). Por ejemplo, la entrada léxica correspondiente a un verbo como "corrió" sería la siguiente: +Por defecto, el Lexicon de la gramática se genera a partir del archivo `lexicon.xml` de la carpeta `data`. La elección de este archivo puede ser modificada mediante la opción 3 del menú principal.
+Las gramáticas están definidas en formato XML, que organiza el léxico como un árbol de nodos. A cada ítem le corresponde un elemento con la etiqueta ``, dentro del cual se insertan nuevos elementos con una etiqueta distinta para cada rasgo: la etiqueta `` corresponde a los rasgos fonológicos, `` a los sintácticos, y `` a los semánticos (aunque estos últimos no son utilizados en la implementación actual). Por ejemplo, la entrada léxica correspondiente a un verbo como *corrió* sería la siguiente: ``` - corrió + llegó V @@ -25,8 +26,25 @@ Por defecto, el Lexicon de la gramática se genera a partir del archivo `lexicon V D + D/ none ``` -Los rasgos sintácticos se subdividen en rasgos categoriales (`Cat_Feature`) y de selección (`Trigger_Feature`). Los primeros se insertan dentro de la etiqueta ``; y los segundos, dentro de ``. +Los rasgos sintácticos se subdividen en rasgos categoriales (`Cat_Feature`) y de selección (`Trigger_Feature`). Los primeros se insertan dentro de la etiqueta ``; y los segundos, dentro de ``.
+#### Merge interno +Los rasgos de selección pueden ir acompañados de una **barra al final** (`D/`). Este operador le indica a la gramática que el ítem léxico en cuestión se somete a merge interno junto a otro ítem con la categoría declarada (en este caso, *llegó* primero se combina mediante merge externo con un determinante, y luego se combina con este -u otro- determinante mediante merge interno, lo que en la práctica supone que el D se "mueva" o se copie a la posición de especificador del SV). Este operador hace que el merge interno sólo pueda efectuarse con ciertos ítems léxicos, y una vez que los demás rasgos de selección ya fueron cotejados. Fue necesario añadir esta restricción, que no aparece en la implementación de Warstadt, con el fin de subsanar el hecho de que los rasgos de selección (y todos los rasgos en general) carecen de orden. +#### Categorías funcionales +Para indicar que un item léxico es una categoría funcional se necesitan dos cosas: 1) el contenido del rasgo fonológico debe aparecer entre corchetes, y 2) la categoría del primer elemento con el que se combina debe estar precedida por una barra. La **barra al comienzo** (`/V`) permite que el algoritmo sepa en qué momento de la derivación debe seleccionarse la categoría funcional. Por ejemplo, así quedaría la entrada léxica de un *v* que se pudiera combinar con *llegó*: +``` + + [] + + v + /V + D/ + + none + +``` +Dada esta gramática, una oración como *El perro llegó* generaría los siguientes *stages*: una vez satisfechos los rasgos de selección del *V*, el *v* será seleccionado, luego se combinará por merge externo con el *V*, y finalmente el *v* se combinará por merge interno con el *D*. From e759ae89d96426f4602759d9fac59b8e972dd018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Sun, 31 Jul 2022 18:02:45 -0300 Subject: [PATCH 26/34] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c326713..312bd53 100644 --- a/README.md +++ b/README.md @@ -47,4 +47,9 @@ Para indicar que un item léxico es una categoría funcional se necesitan dos co none
``` -Dada esta gramática, una oración como *El perro llegó* generaría los siguientes *stages*: una vez satisfechos los rasgos de selección del *V*, el *v* será seleccionado, luego se combinará por merge externo con el *V*, y finalmente el *v* se combinará por merge interno con el *D*. +Dada esta gramática, una oración como *El perro llegó* atravesaría las siguientes etapas: una vez satisfechos los rasgos de selección del *V*, el *v* es seleccionado, luego se combina por merge externo con el *V*, y finalmente se combinar por merge interno con el *D*, dando lugar a la siguiente estructura: + +![](https://img001.prntscr.com/file/img001/YnnsxD8ASmy8ea0zR2ilIQ.png) +Como se ve, es posible definir una gramática de tal modo que dos o más categorías funcionales se combinen entre sí. +
También es posible definir dos ítems con los mismos ragos fonológicos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. +### Implementación From 39cbd27eab8913184f92371e301fd1e0e8e176ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Sun, 31 Jul 2022 18:03:09 -0300 Subject: [PATCH 27/34] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 312bd53..609ef75 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Para indicar que un item léxico es una categoría funcional se necesitan dos co Dada esta gramática, una oración como *El perro llegó* atravesaría las siguientes etapas: una vez satisfechos los rasgos de selección del *V*, el *v* es seleccionado, luego se combina por merge externo con el *V*, y finalmente se combinar por merge interno con el *D*, dando lugar a la siguiente estructura: ![](https://img001.prntscr.com/file/img001/YnnsxD8ASmy8ea0zR2ilIQ.png) + Como se ve, es posible definir una gramática de tal modo que dos o más categorías funcionales se combinen entre sí.
También es posible definir dos ítems con los mismos ragos fonológicos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. ### Implementación From 6c9bd27b28b268f17ad4a4b370bf6628999e5d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Sun, 31 Jul 2022 18:04:20 -0300 Subject: [PATCH 28/34] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 609ef75..464ce14 100644 --- a/README.md +++ b/README.md @@ -52,5 +52,7 @@ Dada esta gramática, una oración como *El perro llegó* atravesaría las sigui ![](https://img001.prntscr.com/file/img001/YnnsxD8ASmy8ea0zR2ilIQ.png) Como se ve, es posible definir una gramática de tal modo que dos o más categorías funcionales se combinen entre sí. -
También es posible definir dos ítems con los mismos ragos fonológicos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. +
También es posible definir dos ítems con los mismos rasgos fonológicos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. ### Implementación +### Ventajas +### Problemas From c95b44ad97bf42d41427f25824a7c6a88f677c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Sun, 31 Jul 2022 18:12:43 -0300 Subject: [PATCH 29/34] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 464ce14..7ea0081 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Parser minimalista -Este es un parser basado en la implementación hecha por Alex Warstadt del formalismo descrito en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. +Este es un parser basado en la implementación de Alex Warstadt del formalismo descrito en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. ## Modo de uso -Para utilizar este programa es necesario ejecutar el archivo `main.py`, ubicado en la carpeta principal del repositorio. Ejecutado el archivo, aparecerá un menú de cinco opciones: +Para utilizar el programa es necesario ejecutar el archivo `main.py`, ubicado en la carpeta principal del repositorio. Ejecutado el archivo, el usuario verá un menú de cinco opciones: **1. Parser** *(Permite introducir la oración a parsear)*
**2. Manual derivation** *(Permite generar la derivación manualmente, utilizando el mecanismo original de Warstadt)*
@@ -52,7 +52,8 @@ Dada esta gramática, una oración como *El perro llegó* atravesaría las sigui ![](https://img001.prntscr.com/file/img001/YnnsxD8ASmy8ea0zR2ilIQ.png) Como se ve, es posible definir una gramática de tal modo que dos o más categorías funcionales se combinen entre sí. -
También es posible definir dos ítems con los mismos rasgos fonológicos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. +
También es posible declarar dos ítems con los mismos rasgos fonológicos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. ### Implementación +El programa utiliza un algoritmo bottom-up que, a grandes rasgos, se basa en las siguientes reglas: ### Ventajas ### Problemas From f405bc0194e75b84ef79523dc5681949cb93bad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Mon, 1 Aug 2022 19:43:00 -0300 Subject: [PATCH 30/34] =?UTF-8?q?Modifico=20numeraci=C3=B3n=20de=20reglas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- derivations/parser.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/derivations/parser.py b/derivations/parser.py index 4d4e40f..14cabd8 100644 --- a/derivations/parser.py +++ b/derivations/parser.py @@ -70,16 +70,15 @@ def autotf(self, index, debug = False): else: return None - # Rule 2 if len(triggers) > 1 and len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) == 0 and isinstance(x,SyntacticObjectSet): return None - # Rule 3 - Internal merge + # Rule 2 - Internal merge if len(triggers) == 1 and triggers[0].label[-1] == '/' and len(self.stages[-1].workspace.w) == 1 and isinstance(x, SyntacticObjectSet): #x.lexical_item.copy_features() triggers[0].label = triggers[0].label[0:-1] if debug == True: - print('Aplying rule 3: internal merge (X,Y)') + print('Aplying rule 2: internal merge (X,Y)') print('') for y in x.syntactic_object_set: if y.category.label == triggers[0].label: @@ -93,10 +92,10 @@ def autotf(self, index, debug = False): return True return None - # Rule 4 - Select + # Rule 3 - Select if len(self.stages[-1].workspace.w) == 1 and len(self.stages[-1].lexical_array.the_list) > 0: if debug == True: - print('Aplying rule 4: select(Y)') + print('Aplying rule 3: select(Y)') print('') for li in self.stages[-1].lexical_array.the_list: if (list(li.lexical_item.phon)[0]).label.startswith('[') and len(x.triggers) == 0: @@ -122,10 +121,10 @@ def autotf(self, index, debug = False): if len(triggers_y) > 0 and x.category.label in triggers_y: self.automerge(y.idx,index,debug=debug) return True - # Rule 7 + # Rule 4 if len(self.stages[-1].lexical_array.the_list) > 0: if debug == True: - print('Aplying rule 7: select(Z)') + print('Aplying rule 4: select(Z)') print('') index_z = min([y.idx for y in self.stages[-1].lexical_array.the_list]) self.autoselect(index_z,debug) @@ -143,10 +142,10 @@ def autotf(self, index, debug = False): return True return None - # Rule 8 - External merge + # Rule 7 - External merge if len(x.triggers) > 0 and len(self.stages[-1].workspace.w) == 3: if debug == True: - print('Aplying rule 8: external merge(Z,Y)') + print('Aplying rule 7: external merge(Z,Y)') print('') z = self.stages[-1].workspace.find_workspace(index-1) if z in self.stages[-1].workspace.w: @@ -273,4 +272,4 @@ def parse(sentence,filename="lexicon.xml",debug=True): else: print('') print('Sentence is not derivable.') - print('') \ No newline at end of file + print('') From 5ee5afc8c9839c4eb3c46ed586ca72609ff3b66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Mon, 1 Aug 2022 20:11:50 -0300 Subject: [PATCH 31/34] Delete rules.pdf --- derivations/rules.pdf | Bin 34650 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 derivations/rules.pdf diff --git a/derivations/rules.pdf b/derivations/rules.pdf deleted file mode 100644 index 3dbf14f8a6f11d236a2861851f794665842b08fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34650 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
m<-5^=Q0v=J7I24XZ7DJ9C- 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 W*2M8KG((tA6X)f%*zIl}Z^aKZ@-yrJ From 7f5a3fb82696d600c1b60aaf0c424d83d7260339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Mon, 1 Aug 2022 21:16:36 -0300 Subject: [PATCH 32/34] Update README.md --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7ea0081..f20c990 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Parser minimalista -Este es un parser basado en la implementación de Alex Warstadt del formalismo descrito en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. +Este es un parser basado en la implementación de Alex Warstadt del formalismo descrito en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. + +Actualmente es capaz de reconocer oraciones transitivas (*el perro comió el hueso/the dog ate the bone*), oraciones inergativas (*el perro corrió/the dog run*) y oraciones inacusativas (*el perro llegó*/*the dog arrived*) ## Modo de uso Para utilizar el programa es necesario ejecutar el archivo `main.py`, ubicado en la carpeta principal del repositorio. Ejecutado el archivo, el usuario verá un menú de cinco opciones: @@ -12,7 +14,7 @@ Para utilizar el programa es necesario ejecutar el archivo `main.py`, ubicado en ### La gramática Por defecto, el Lexicon de la gramática se genera a partir del archivo `lexicon.xml` de la carpeta `data`. La elección de este archivo puede ser modificada mediante la opción 3 del menú principal.
-Las gramáticas están definidas en formato XML, que organiza el léxico como un árbol de nodos. A cada ítem le corresponde un elemento con la etiqueta ``, dentro del cual se insertan nuevos elementos con una etiqueta distinta para cada rasgo: la etiqueta `` corresponde a los rasgos fonológicos, `` a los sintácticos, y `` a los semánticos (aunque estos últimos no son utilizados en la implementación actual). Por ejemplo, la entrada léxica correspondiente a un verbo como *corrió* sería la siguiente: +Las gramáticas están definidas en formato XML, que organiza el léxico como un árbol de nodos. A cada ítem le corresponde un elemento con la etiqueta ``, dentro del cual se insertan nuevos elementos con una etiqueta distinta para cada rasgo: la etiqueta `` corresponde a los rasgos fonético, `` a los sintácticos, y `` a los semánticos (aunque estos últimos no son utilizados en la implementación actual). Por ejemplo, la entrada léxica correspondiente a un verbo como *corrió* sería la siguiente: ``` llegó @@ -35,7 +37,7 @@ Los rasgos sintácticos se subdividen en rasgos categoriales (`Cat_Feature`) y d #### Merge interno Los rasgos de selección pueden ir acompañados de una **barra al final** (`D/`). Este operador le indica a la gramática que el ítem léxico en cuestión se somete a merge interno junto a otro ítem con la categoría declarada (en este caso, *llegó* primero se combina mediante merge externo con un determinante, y luego se combina con este -u otro- determinante mediante merge interno, lo que en la práctica supone que el D se "mueva" o se copie a la posición de especificador del SV). Este operador hace que el merge interno sólo pueda efectuarse con ciertos ítems léxicos, y una vez que los demás rasgos de selección ya fueron cotejados. Fue necesario añadir esta restricción, que no aparece en la implementación de Warstadt, con el fin de subsanar el hecho de que los rasgos de selección (y todos los rasgos en general) carecen de orden. #### Categorías funcionales -Para indicar que un item léxico es una categoría funcional se necesitan dos cosas: 1) el contenido del rasgo fonológico debe aparecer entre corchetes, y 2) la categoría del primer elemento con el que se combina debe estar precedida por una barra. La **barra al comienzo** (`/V`) permite que el algoritmo sepa en qué momento de la derivación debe seleccionarse la categoría funcional. Por ejemplo, así quedaría la entrada léxica de un *v* que se pudiera combinar con *llegó*: +Para indicar que un item léxico es una categoría funcional se necesitan dos cosas: 1) el contenido del rasgo fonético debe aparecer entre corchetes, y 2) la categoría del primer elemento con el que se combina debe estar precedida por una barra. La **barra al comienzo** (`/V`) permite que el algoritmo sepa en qué momento de la derivación debe seleccionarse la categoría funcional. Por ejemplo, así quedaría la entrada léxica de un *v* que se pudiera combinar con *llegó*: ``` [] @@ -47,13 +49,48 @@ Para indicar que un item léxico es una categoría funcional se necesitan dos co none ``` -Dada esta gramática, una oración como *El perro llegó* atravesaría las siguientes etapas: una vez satisfechos los rasgos de selección del *V*, el *v* es seleccionado, luego se combina por merge externo con el *V*, y finalmente se combinar por merge interno con el *D*, dando lugar a la siguiente estructura: +Dada esta gramática, una oración como *El perro llegó* atravesaría las siguientes etapas: una vez satisfechos los rasgos de selección del *V*, el *v* es seleccionado, luego se combina por merge externo con el *V*, y finalmente se combina por merge interno con el *D*, dando lugar a la siguiente estructura: ![](https://img001.prntscr.com/file/img001/YnnsxD8ASmy8ea0zR2ilIQ.png) Como se ve, es posible definir una gramática de tal modo que dos o más categorías funcionales se combinen entre sí. -
También es posible declarar dos ítems con los mismos rasgos fonológicos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. +
También es posible declarar dos ítems con los mismos rasgos fonéticos y categoriales, pero diferentes rasgos de selección. En este caso, el programa generará dos lexical arrays distintos, e imprimirá todas las derivaciones exitosas. ### Implementación -El programa utiliza un algoritmo bottom-up que, a grandes rasgos, se basa en las siguientes reglas: -### Ventajas -### Problemas +#### Reglas +El programa utiliza un algoritmo bottom-up que se basa en las siguientes reglas (utilizamos el nombre de las variables tal como aparecen en la función `autotf()` del módulo `derivations/parser`. + +| N° | If len(x.triggers) | & len(workspace.w) | & len(lexical_array.the_list) | then: +| ------------- |:-------------:| -------- | --------- | ------- | +| 0 | | | | select(x) +| 1 | 0 | 1 | 0 | *success* +| 2| = 1 (con /) | = 1 | | *internal* merge(x,y) +| 3| | = 1 | > 0 | select(y) +| 4| 0 | = 2 | > 0 | select(z) +| 5| = 0 | = 2 & len(y.triggers) > 0 | | merge(y,x) +| 6| = 1 | = 2 | | merge(x,y) +| 7| = 1 | = 3 | | merge(z,y) + +La numeración de las reglas indica precedencia en cuanto a su aplicación. *x* es el elemento a analizar en cada etapa de la derivación, mientras que *y* es un segundo elemento que puede pertenecer al lexical array (en la regla 2, *y* es el elemento con el índice inmediatamente superior a *x*, salvo cuando se trata de una categoría funcional) o al workspace, e incluso estar contenido en *x* (en el caso del merge interno). El primer valor que toma *x* es el lexical item token correspondiente a la última palabra de la oración (i.e., que coincide con sus rasgos fonéticos), el cual aparece indexado en el lexical array con el número 0. +La derivación falla cuando su último estado no coincide con ninguna de las posibilidades ilustradas en la tabla, o cuando falla el merge (si el rasgo de selección de *x* no coincide con el de *y*, o si coincide pero *y* tiene rasgos de selección todavía sin satisfacer). + +#### Transfer +La operación Transfer, tal como se encuentra definida en el trabajo de Collins-Stabler, interviene en el pasaje entre la estructura sintáctica y las formas fonética (TransferPF) y semántica (TransferLF). Nuestro programa implementa una versión simplificada de TransferPF, que se aplica a toda derivación exitosa mediante la función recursiva `transfer()`. Este algoritmo opera de la siguiente manera: + +* Si *x* e *y* son ítems léxicos (i.e., objetos sintácticos simples), se añaden a la lista `transferred_sentece` sus respectivos rasgos `Phon`, en este orden: primero el elemento con rasgos de selección (es decir, el núcleo) y luego el elemento seleccionado por aquél (que será el complemento). +* Si *x* es núcleo pero, al mismo tiempo, un conjunto de objetos sintácticos (i.e., un objeto sintáctico complejo), entonces se añade a la lista en primer lugar el rasgo `Phon` de *y* (especificador), en caso de que éste sea un item léxico, y luego se aplica nuevamente `transfer()` sobre *x*. En cambio, si *y* también es un objeto complejo, entonces se aplica `transfer()` primeramente, a *y*, y luego a *x*. + +Mediante estas reglas el programa obtiene la forma lineal de la estructura sintáctica generada, y luego chequea si coincide con la de la oración introducida por el usuario. En caso afirmativo, la derivación es exitosa. + +#### Categorías funcionales +Las categorías funcionales se añaden mediante la función `add_functional_categories()`. Sencillamente, aquí el algoritmo revisa la lista de ítems y evalúa si para cada elemento (sea funcional o léxico) existe una categoría funcional que lo seleccione. En caso afirmativo, se añade en último lugar a la lista con la que se elaborará el lexical array. +#### Manejo de ítems duplicados +Cuando dos o más entradas léxicas coinciden en sus rasgos fonéticos y categoriales, la función `get_possible_lexicons()` genera todas las combinaciones posibles de ítems, de tal modo que en cada combinación no haya más que un ítem con dichos rasgos. Estas listas son recogidas luego por la función `parse()`, que produce tantas derivaciones como listas haya. Finalmente se imprimen todas las derivaciones exitosas. En `data/lexicon.xml` hay dos `v` con distintos rasgos, uno para la oración inacusativa (con merge interno) y otro para las oraciones transitiva e inergativa. + +### Problemas conocidos +La mayoría de los escollos que presenta este parser se deben a su condición de parser bottom-up, lo que implica recurrir a la forma fonética (de la oración-input) para reconstruir la estructura sintáctica: un camino inverso al que proponen las gramáticas generativas. Y si bien la implementación de *transfer* subsana desde el punto de vista teórico esta limitación, lo cierto es que la estructura sintáctica sigue estando atada a la linealidad de la frase, en la medida en que los dos primeros elementos (empezando desde la derecha) siempre intentarán combinarse entre sí, o a lo sumo se seleccionará un tercero (regla 4). Esto vuelve imposible el reconocimiento, por ejemplo, de -por ejemplo- oraciones interrogativas o focalizadas donde el elemento sintáctico de la derecha (el primero en combinarse) se realiza luego en el margen izquierdo de la oración. Y asimismo dificulta el análisis de oraciones ditransitivas (como *el perro entregó el hueso al dueño*, en las que el OD ocupa la posición de especificador y el OI, la de complemento. En nuestra implementación, en cambio, estas posiciones aparecen invertidas. + +![](https://img001.prntscr.com/file/img001/yzdTEc4TQW-G-MTzhEvQsQ.png) + +Nótese que `12,V` es un objeto complejo, y que por lo tanto en `13,V`, `10,P` es especificador. Esto supone que la oración generada por `transfer()` será *el perro al dueño entregó el hueso*. A su vez, un merge interno entre `12,V` y `13,V` resulta imposible, ya que el primer objeto tiene rasgos de selección sin satisfacer. El objeto `14,V` sí podría combinarse con `13,V`, pero sólo después del merge externo con `15,D`, por lo que el sintagma verbal quedaría por encima del argumento externo. + +Tal vez con otro lexicon, o añadiendo nuevos operadores (como una categoría inicial) y seleccionando al mismo tiempo todos los elementos (es decir, mediante un algoritmo que ya no atienda al orden lineal de la frase), podrían salvarse de estos inconvenientes. Otra alternativa sería implementar un parser top-down capaz de generar todas las oraciones posibles de una gramática, y luego evaluar si la oración ingresada por el usuario coincide con alguna de ellas. Sólo que no está claro que el formalismo de Collins-Stabler sea compatible con un algoritmo de este tipo, que además requeriría una modificación más profunda de la implementación de Warstardt. From 7c811b574b14f5e43c5fdf5d8c3a5a8f4b1be578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Tue, 2 Aug 2022 18:47:17 -0300 Subject: [PATCH 33/34] Update README.md --- README.md | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f20c990..b1b2e82 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ # Parser minimalista Este es un parser basado en la implementación de Alex Warstadt del formalismo descrito en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. -Actualmente es capaz de reconocer oraciones transitivas (*el perro comió el hueso/the dog ate the bone*), oraciones inergativas (*el perro corrió/the dog run*) y oraciones inacusativas (*el perro llegó*/*the dog arrived*) +El parser es capaz de reconocer oraciones transitivas (*el perro comió el hueso/the dog ate the bone*), oraciones inergativas (*el perro corrió/the dog run*) y oraciones inacusativas (*el perro llegó*/*the dog arrived*) ## Modo de uso -Para utilizar el programa es necesario ejecutar el archivo `main.py`, ubicado en la carpeta principal del repositorio. Ejecutado el archivo, el usuario verá un menú de cinco opciones: +Para utilizar el programa es necesario ejecutar el archivo `main.py`, ubicado en la carpeta principal del repositorio. Hecho esto, el usuario verá un menú de cinco opciones: **1. Parser** *(Permite introducir la oración a parsear)*
-**2. Manual derivation** *(Permite generar la derivación manualmente, utilizando el mecanismo original de Warstadt)*
+**2. Manual derivation** *(Permite generar la derivación manualmente, como en la implementación de Warstadt)*
**3. Change grammar** *(Permite seleccionar una gramática distinta)*
-**4. Enable/disable stage-by-stage view** *(Cuando está activado, muestra el paso a paso de una derivación, incluyendo las reglas que fueron aplicadas)*
+**4. Enable/disable stage-by-stage view** *(Cuando está activado, muestra el paso a paso de una derivación, incluyendo las reglas aplicadas)*
**5. Quit** *(Finaliza el programa)* ### La gramática Por defecto, el Lexicon de la gramática se genera a partir del archivo `lexicon.xml` de la carpeta `data`. La elección de este archivo puede ser modificada mediante la opción 3 del menú principal.
-Las gramáticas están definidas en formato XML, que organiza el léxico como un árbol de nodos. A cada ítem le corresponde un elemento con la etiqueta ``, dentro del cual se insertan nuevos elementos con una etiqueta distinta para cada rasgo: la etiqueta `` corresponde a los rasgos fonético, `` a los sintácticos, y `` a los semánticos (aunque estos últimos no son utilizados en la implementación actual). Por ejemplo, la entrada léxica correspondiente a un verbo como *corrió* sería la siguiente: +Las gramáticas están definidas en el formato XML, que organiza el léxico como un árbol de nodos. A cada ítem le corresponde un elemento con la etiqueta ``, dentro del cual se insertan nuevos elementos con una etiqueta distinta para cada rasgo: la etiqueta `` corresponde a los rasgos fonético, `` a los sintácticos, y `` a los semánticos (aunque estos últimos no son utilizados en la implementación actual). Por ejemplo, la entrada léxica correspondiente a un verbo como *corrió* sería la siguiente: ``` llegó @@ -35,7 +35,7 @@ Las gramáticas están definidas en formato XML, que organiza el léxico como un ``` Los rasgos sintácticos se subdividen en rasgos categoriales (`Cat_Feature`) y de selección (`Trigger_Feature`). Los primeros se insertan dentro de la etiqueta ``; y los segundos, dentro de ``.
#### Merge interno -Los rasgos de selección pueden ir acompañados de una **barra al final** (`D/`). Este operador le indica a la gramática que el ítem léxico en cuestión se somete a merge interno junto a otro ítem con la categoría declarada (en este caso, *llegó* primero se combina mediante merge externo con un determinante, y luego se combina con este -u otro- determinante mediante merge interno, lo que en la práctica supone que el D se "mueva" o se copie a la posición de especificador del SV). Este operador hace que el merge interno sólo pueda efectuarse con ciertos ítems léxicos, y una vez que los demás rasgos de selección ya fueron cotejados. Fue necesario añadir esta restricción, que no aparece en la implementación de Warstadt, con el fin de subsanar el hecho de que los rasgos de selección (y todos los rasgos en general) carecen de orden. +Los rasgos de selección pueden ir acompañados de una **barra al final** (`D/`). Este operador le indica a la gramática que el ítem léxico en cuestión se somete a merge interno junto a otro ítem de la categoría declarada (en este caso, *llegó* primero se combina mediante merge externo con un determinante, y luego se combina con este determinante mediante merge interno, lo que en la práctica supone que el D se "mueva" o más bien se copie en la posición de especificador del SV). Este operador hace que el merge interno sólo pueda efectuarse con ciertos ítems léxicos, y una vez que los demás rasgos de selección ya fueron cotejados. Fue necesario añadir esta restricción, que no aparece en la implementación original, con el fin de subsanar el hecho de que los rasgos de selección (y todos los rasgos) constituyen un conjunto, y por lo tanto no están ordenados. #### Categorías funcionales Para indicar que un item léxico es una categoría funcional se necesitan dos cosas: 1) el contenido del rasgo fonético debe aparecer entre corchetes, y 2) la categoría del primer elemento con el que se combina debe estar precedida por una barra. La **barra al comienzo** (`/V`) permite que el algoritmo sepa en qué momento de la derivación debe seleccionarse la categoría funcional. Por ejemplo, así quedaría la entrada léxica de un *v* que se pudiera combinar con *llegó*: ``` @@ -49,7 +49,7 @@ Para indicar que un item léxico es una categoría funcional se necesitan dos co none
``` -Dada esta gramática, una oración como *El perro llegó* atravesaría las siguientes etapas: una vez satisfechos los rasgos de selección del *V*, el *v* es seleccionado, luego se combina por merge externo con el *V*, y finalmente se combina por merge interno con el *D*, dando lugar a la siguiente estructura: +Dada esta gramática, una oración como *El perro llegó* pasaría por las siguientes etapas: una vez satisfechos los rasgos de selección del *V*, el *v* es seleccionado, luego se combina por merge externo con el *V*, y finalmente se combina por merge interno con el *D*, dando lugar a la siguiente estructura: ![](https://img001.prntscr.com/file/img001/YnnsxD8ASmy8ea0zR2ilIQ.png) @@ -70,27 +70,38 @@ El programa utiliza un algoritmo bottom-up que se basa en las siguientes reglas | 6| = 1 | = 2 | | merge(x,y) | 7| = 1 | = 3 | | merge(z,y) -La numeración de las reglas indica precedencia en cuanto a su aplicación. *x* es el elemento a analizar en cada etapa de la derivación, mientras que *y* es un segundo elemento que puede pertenecer al lexical array (en la regla 2, *y* es el elemento con el índice inmediatamente superior a *x*, salvo cuando se trata de una categoría funcional) o al workspace, e incluso estar contenido en *x* (en el caso del merge interno). El primer valor que toma *x* es el lexical item token correspondiente a la última palabra de la oración (i.e., que coincide con sus rasgos fonéticos), el cual aparece indexado en el lexical array con el número 0. +La numeración de las reglas indica precedencia en cuanto a su aplicación. *x* es el elemento a analizar en cada etapa de la derivación, mientras que *y* es un segundo elemento que puede pertenecer al lexical array (en la regla 2, *y* es el elemento con el índice inmediatamente superior a *x*, salvo cuando se trata de una categoría funcional) o al workspace, e incluso estar contenido en *x* (en el caso del merge interno). El primer valor que toma *x* es el *lexical item token* correspondiente a la última palabra de la oración (i.e., que coincide con sus rasgos fonéticos), el cual aparece indexado en el *lexical array* con el número 0 (`x.idx = 0`). La derivación falla cuando su último estado no coincide con ninguna de las posibilidades ilustradas en la tabla, o cuando falla el merge (si el rasgo de selección de *x* no coincide con el de *y*, o si coincide pero *y* tiene rasgos de selección todavía sin satisfacer). #### Transfer La operación Transfer, tal como se encuentra definida en el trabajo de Collins-Stabler, interviene en el pasaje entre la estructura sintáctica y las formas fonética (TransferPF) y semántica (TransferLF). Nuestro programa implementa una versión simplificada de TransferPF, que se aplica a toda derivación exitosa mediante la función recursiva `transfer()`. Este algoritmo opera de la siguiente manera: -* Si *x* e *y* son ítems léxicos (i.e., objetos sintácticos simples), se añaden a la lista `transferred_sentece` sus respectivos rasgos `Phon`, en este orden: primero el elemento con rasgos de selección (es decir, el núcleo) y luego el elemento seleccionado por aquél (que será el complemento). -* Si *x* es núcleo pero, al mismo tiempo, un conjunto de objetos sintácticos (i.e., un objeto sintáctico complejo), entonces se añade a la lista en primer lugar el rasgo `Phon` de *y* (especificador), en caso de que éste sea un item léxico, y luego se aplica nuevamente `transfer()` sobre *x*. En cambio, si *y* también es un objeto complejo, entonces se aplica `transfer()` primeramente, a *y*, y luego a *x*. +* Si *x* e *y* son ítems léxicos (i.e., objetos sintácticos simples), se añaden a la lista `transferred_sentence` sus respectivos rasgos `Phon`, en este orden: primero el elemento con rasgos de selección (es decir, el núcleo) y luego el elemento seleccionado por aquél (que será el complemento). +* Si *x* es núcleo pero, al mismo tiempo, un conjunto de objetos sintácticos (i.e., un objeto sintáctico complejo), entonces se añade a la lista en primer lugar el rasgo `Phon` de *y* (especificador), en caso de que éste sea un item léxico, y luego se aplica nuevamente `transfer()` sobre *x*. En cambio, si *y* también es un objeto complejo, entonces se aplica `transfer()` primeramente a *y*, y luego a *x*. -Mediante estas reglas el programa obtiene la forma lineal de la estructura sintáctica generada, y luego chequea si coincide con la de la oración introducida por el usuario. En caso afirmativo, la derivación es exitosa. +Mediante estas reglas el programa obtiene la forma lineal de la estructura sintáctica generada, y luego evalúa si coincide con la de la oración introducida por el usuario. En caso afirmativo, la derivación es exitosa. #### Categorías funcionales -Las categorías funcionales se añaden mediante la función `add_functional_categories()`. Sencillamente, aquí el algoritmo revisa la lista de ítems y evalúa si para cada elemento (sea funcional o léxico) existe una categoría funcional que lo seleccione. En caso afirmativo, se añade en último lugar a la lista con la que se elaborará el lexical array. +Las categorías funcionales se añaden mediante la función `add_functional_categories()`. Sencillamente, aquí el algoritmo revisa la lista de ítems y evalúa si para cada elemento (sea funcional o léxico) existe una categoría funcional que lo seleccione. En caso afirmativo, se añade en último lugar a la lista con la que se elaborará el *lexical array*. #### Manejo de ítems duplicados Cuando dos o más entradas léxicas coinciden en sus rasgos fonéticos y categoriales, la función `get_possible_lexicons()` genera todas las combinaciones posibles de ítems, de tal modo que en cada combinación no haya más que un ítem con dichos rasgos. Estas listas son recogidas luego por la función `parse()`, que produce tantas derivaciones como listas haya. Finalmente se imprimen todas las derivaciones exitosas. En `data/lexicon.xml` hay dos `v` con distintos rasgos, uno para la oración inacusativa (con merge interno) y otro para las oraciones transitiva e inergativa. ### Problemas conocidos -La mayoría de los escollos que presenta este parser se deben a su condición de parser bottom-up, lo que implica recurrir a la forma fonética (de la oración-input) para reconstruir la estructura sintáctica: un camino inverso al que proponen las gramáticas generativas. Y si bien la implementación de *transfer* subsana desde el punto de vista teórico esta limitación, lo cierto es que la estructura sintáctica sigue estando atada a la linealidad de la frase, en la medida en que los dos primeros elementos (empezando desde la derecha) siempre intentarán combinarse entre sí, o a lo sumo se seleccionará un tercero (regla 4). Esto vuelve imposible el reconocimiento, por ejemplo, de -por ejemplo- oraciones interrogativas o focalizadas donde el elemento sintáctico de la derecha (el primero en combinarse) se realiza luego en el margen izquierdo de la oración. Y asimismo dificulta el análisis de oraciones ditransitivas (como *el perro entregó el hueso al dueño*, en las que el OD ocupa la posición de especificador y el OI, la de complemento. En nuestra implementación, en cambio, estas posiciones aparecen invertidas. +La mayoría de los escollos que presenta este parser se deben a que utiliza la forma fonética (de la oración-input) para reconstruir la estructura sintáctica: un camino inverso al que proponen las gramáticas generativas. Y si bien la implementación de *transfer* subsana desde el punto de vista teórico esta limitación, lo cierto es que la estructura sintáctica se mantiene atada a la linealidad de la frase, en la medida en que los dos primeros elementos (empezando desde la derecha) siempre intentarán combinarse entre sí, o a lo sumo se seleccionará un tercero (regla 4). Esto vuelve imposible el reconocimiento de -por ejemplo- oraciones interrogativas o focalizadas donde el elemento sintáctico de la derecha (el primero en combinarse) se realiza luego en el margen izquierdo de la oración. Y asimismo dificulta el análisis de oraciones ditransitivas (como *el perro entregó el hueso al dueño*, en las que el OD ocuparía la posición de especificador y el OI, la de complemento. En nuestra implementación, en cambio, estas posiciones aparecen invertidas, debido al orden en que se da el merge. ![](https://img001.prntscr.com/file/img001/yzdTEc4TQW-G-MTzhEvQsQ.png) -Nótese que `12,V` es un objeto complejo, y que por lo tanto en `13,V`, `10,P` es especificador. Esto supone que la oración generada por `transfer()` será *el perro al dueño entregó el hueso*. A su vez, un merge interno entre `12,V` y `13,V` resulta imposible, ya que el primer objeto tiene rasgos de selección sin satisfacer. El objeto `14,V` sí podría combinarse con `13,V`, pero sólo después del merge externo con `15,D`, por lo que el sintagma verbal quedaría por encima del argumento externo. +Nótese que `12,V` es un objeto complejo, y que por lo tanto en `13,V`, `10,P` es especificador. Esto supone que la oración generada por `transfer()` será *el perro al dueño entregó el hueso*. A su vez, un merge interno entre `12,V` y `13,V` resulta imposible, ya que el primer objeto tiene rasgos de selección sin satisfacer. El objeto `14,V` sí podría combinarse con `13,V`, pero sólo después del merge externo con `15,D`, por lo que el sintagma verbal quedaría por encima del argumento externo. De todos modos, un Lexicon con rasgos de selección y/o categorías funcionales distintas podría llegar a dar cuenta de esta oración sin necesidad de modificar el código. -Tal vez con otro lexicon, o añadiendo nuevos operadores (como una categoría inicial) y seleccionando al mismo tiempo todos los elementos (es decir, mediante un algoritmo que ya no atienda al orden lineal de la frase), podrían salvarse de estos inconvenientes. Otra alternativa sería implementar un parser top-down capaz de generar todas las oraciones posibles de una gramática, y luego evaluar si la oración ingresada por el usuario coincide con alguna de ellas. Sólo que no está claro que el formalismo de Collins-Stabler sea compatible con un algoritmo de este tipo, que además requeriría una modificación más profunda de la implementación de Warstardt. +### A futuro +Elegimos el parser bottom-up porque es el que mejor se adapta a la implementación y el formalismo originales. Tal vez sería posible mejorarlo mediante la adición de nuevos operadores (como una o varias categorías iniciales), y permitiendo que se combinen elementos independientemente del orden lineal. + +Otra alternativa sería recurrir a un parser top-down capaz de generar todas las oraciones posibles de una gramática, y luego evaluar si la oración ingresada por el usuario coincide con alguna de ellas. Para esto, podríamos seguir el mecanismo descrito por [Shieber et al. (2005)](https://arxiv.org/abs/cmp-lg/9404008): +1. Initialize the chart to the empty set of items and the agenda to the axioms of the deduction system. +2. Repeat the following steps until the agenda is exhausted: + * a) Select an item from the agenda, called the trigger item, and remove it. + * b) Add the trigger item to the chart, if the item is not already in the chart. + * c) If the trigger item was added to the chart, generate all items that can be derived from the trigger item and any items in the chart by one application of a rule of inference, and add these generated items to the agenda. +3. If a goal item is in the chart, the goal is proved, i.e., the string is recognized, otherwise it is not. + +Queda por ver cuán compatible sería esta solución con la propuesta de Collins-Stabler. From 26f3a53524fa614de9ff791299909fc047260204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Facundo=20Calabr=C3=B3?= <87214888+fcalabrow@users.noreply.github.com> Date: Tue, 2 Aug 2022 18:49:30 -0300 Subject: [PATCH 34/34] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1b2e82..88191fe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Parser minimalista -Este es un parser basado en la implementación de Alex Warstadt del formalismo descrito en "A Formalization of Minimalist Syntax" por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. +Este es un parser basado en la implementación de Alex Warstadt del formalismo descrito en ["A Formalization of Minimalist Syntax"](https://onlinelibrary.wiley.com/doi/abs/10.1111/synt.12117) por Chris Collins y Edward Stabler (2016). Su objetivo es determinar si las oraciones introducidas por el usuario son o no gramaticales, y devolver en caso positivo una estructura sintáctica. El parser es capaz de reconocer oraciones transitivas (*el perro comió el hueso/the dog ate the bone*), oraciones inergativas (*el perro corrió/the dog run*) y oraciones inacusativas (*el perro llegó*/*the dog arrived*)