from collections import Counter class SyntaxModule: def __init__(self): # --- Aggettivi attributivi INGLESI --- self.adj_attrr_en = { "good", "new", "first", "last", "long", "great", "little", "own", "other", "old", "right", "big", "high", "different", "small", "large", "next", "early", "young", "important", "few", "public", "bad", "same", "able", "black", "white", "green", "blue", "red", "serious", "happy", "strong", "special", "weak", "wide", "beautiful", "nice", "smart", "tall", "major", "real", "basic", "full", "free", "perfect", "dark", "light", "hard", "clear", "simple", "cold", "warm", "rich", "strange", "sure", "deep", "local", "quick", "cheap", "direct", "tight", "open", "empty", "fine", "short", "dry", "loud", "safe", "calm", "dead", "mad", "sharp", "hot", "raw", "narrow", "round", "smooth", "soft", "heavy", "solid", "pure", "honest", "brave", "gentle", "wild", "shy", "bold", "bright", "fresh", "sweet", "bitter", "sour", "thick", "thin", "flat", "low", "poor", "quiet", "dangerous", "sad", "glad", "angry", "kind", "cruel", "stupid", "wise", "foolish", "proud", "humble", "funny", "normal", "familiar", "common", "rare", "possible", "impossible", "necessary", "uncertain", "confident", "hidden", "visible", "vague", "excited", "bored", "interested", "worried", "nervous", "relaxed", "tense", "helpful", "harmful", "useful", "useless", "valuable", "worthless", "trivial", "actual", "potential", "specific", "general", "particular", "universal", "individual", "collective", "personal", "main", "primary", "secondary", "unique", "typical", "standard", "advanced", "modern", "traditional", "contemporary", "recent", "current", "past", "future", "temporary", "permanent", "brief", "intermediate", "initial", "final", "external", "internal", "global", "national", "international", "central", "minor", "significant", "essential", "critical", "fundamental", "key", "core", "active", "passive", "indirect", "positive", "negative", "neutral", "absolute", "relative", "total", "partial", "complete", "incomplete", "complex", "theoretical", "practical", "empirical", "systematic", "random", "broad", "limited", "extensive", "intensive", "substantial", "minimal", "maximum", "minimum", "average", "natural", "artificial", "original", "conventional", "innovative", "experimental", "alternative", "tertiary", "preliminary", "ultimate", "concrete", "abstract", "mental", "physical", "emotional", "intellectual", "cultural", "social", "economic", "political", "scientific", "technical", "professional", "commercial", "industrial", "agricultural", "medical", "legal", "financial", "educational", "environmental", "historical", "geographical", "mathematical", "linguistic", "psychological", "philosophical", "artistic", "literary", "musical", "digital", "analog", "electronic", "mechanical", "nuclear", "biological", "chemical", "organic", "inorganic", "static", "dynamic", "stable", "unstable", "consistent", "inconsistent", "reliable", "unreliable", "valid", "invalid", "accurate", "inaccurate", "precise", "imprecise", "certain", "definite", "indefinite", "probable", "improbable", "likely", "unlikely", "unnecessary", "nonessential", "mandatory", "optional", "compulsory", "voluntary", "peripheral", "marginal", "trivial", "insignificant", "strategic", "tactical", "operational", "administrative", "executive", "legislative", "judicial", "regulatory", "preventive", "corrective", "proactive", "reactive", "adaptive", "responsive", "interactive", "collaborative", "competitive", "cooperative", "constructive", "destructive", "productive", "unproductive", "effective", "ineffective", "efficient", "inefficient", "sustainable", "unsustainable", "viable", "unviable", "progressive", "regressive", "radical", "conservative", "liberal", "moderate", "extreme", "mainstream", } # --- Aggettivi attributivi ITALIANI --- self.adj_attrr_it = { # Qualità fisiche "grande", "piccolo", "lungo", "corto", "alto", "basso", "largo", "stretto", "grosso", "sottile", "spesso", "piatto", "rotondo", "quadrato", "dritto", "curvo", "pesante", "leggero", "duro", "morbido", "liscio", "ruvido", "caldo", "freddo", "bollente", "gelido", "tiepido", # Colori "nero", "bianco", "rosso", "verde", "blu", "giallo", "arancione", "viola", "rosa", "grigio", "marrone", "azzurro", "dorato", "argentato", "scuro", "chiaro", "pallido", "vivace", # Qualità morali / caratteriali "buono", "cattivo", "onesto", "disonesto", "coraggioso", "vigliacco", "gentile", "crudele", "generoso", "avaro", "giusto", "ingiusto", "fedele", "leale", "sleale", "umile", "arrogante", "paziente", "impaziente", "saggio", "stolto", "prudente", "temerario", "sincero", "falso", "ingenuo", "furbo", # Qualità intellettuali "intelligente", "stupido", "brillante", "mediocre", "capace", "incapace", "abile", "inabile", "esperto", "inesperto", "colto", "ignorante", "istruito", # Qualità estetiche "bello", "brutto", "elegante", "goffo", "raffinato", "rozzo", "attraente", "affascinante", "noioso", "interessante", "banale", "originale", "creativo", # Dimensione temporale "nuovo", "vecchio", "antico", "moderno", "recente", "passato", "futuro", "attuale", "contemporaneo", "storico", "tradizionale", "obsoleto", "innovativo", "eterno", "temporaneo", "definitivo", # Dimensione spaziale / relazionale "vicino", "lontano", "centrale", "periferico", "interno", "esterno", "locale", "globale", "nazionale", "internazionale", "regionale", "urbano", "rurale", "pubblico", "privato", # Quantità / grado "intero", "parziale", "completo", "incompleto", "totale", "principale", "secondario", "unico", "raro", "comune", "frequente", "scarso", "abbondante", "sufficiente", "insufficiente", "massimo", "minimo", "medio", "normale", "eccezionale", # Relazione e categoria "simile", "diverso", "uguale", "opposto", "specifico", "generico", "particolare", "generale", "tipico", "atipico", "naturale", "artificiale", "reale", "virtuale", "concreto", "astratto", "positivo", "negativo", "neutro", "relativo", "assoluto", # Stato emotivo "felice", "triste", "arrabbiato", "calmo", "ansioso", "sereno", "agitato", "tranquillo", "nervoso", "rilassato", "allegro", "malinconico", "entusiasta", "deluso", "soddisfatto", "insoddisfatto", # Dominio tecnico / accademico "scientifico", "tecnico", "culturale", "sociale", "economico", "politico", "giuridico", "medico", "biologico", "chimico", "fisico", "matematico", "linguistico", "letterario", "artistico", "filosofico", "psicologico", "geografico", "professionale", "accademico", "scolastico", "educativo", # Modalità epistemica "certo", "incerto", "probabile", "improbabile", "possibile", "impossibile", "necessario", "inutile", "fondamentale", "marginale", "importante", "irrilevante", "significativo", "trascurabile", "evidente", "oscuro", "ambiguo", "definito", "vago", } def get_node_depth(self, token): depth = 1 curr = token while curr.head != curr: depth += 1 curr = curr.head return depth def analyze(self, doc, sentences, lang="en"): # Seleziona il set aggettivi in base alla lingua active_adj = self.adj_attrr_it if lang == "it" else self.adj_attrr_en # Tag per i tempi verbali: l'italiano usa tag UD diversi da Penn Treebank if lang == "it": present_tags = {"Pres"} # valori di Tense nella morfologia UD past_tags = {"Past"} part_tags = {"Part"} # VerbForm=Part else: present_tags = {"VB", "VBP", "VBZ"} past_tags = {"VBD"} part_tags = {"VBN", "VBG"} num_sents = len(sentences) if sentences else 1 num_tokens = len(doc) if len(doc) > 0 else 1 total_punct_pairs = 0 total_svo_inversions = 0 total_non_comp_subs = 0 for sent in sentences: punct_count = sum(1 for t in sent if t.text in (",", ";")) total_punct_pairs += punct_count // 2 for token in sent: if token.pos_ in ("VERB", "AUX"): for child in token.children: if child.dep_ in ("nsubj", "nsubjpass", "csubj", "csubjpass", "expl") and child.i > token.i: total_svo_inversions += 1 elif child.dep_ in ("obj", "dobj", "pobj", "dative", "ccomp", "xcomp", "acomp") and child.i < token.i: total_svo_inversions += 1 if token.dep_ in ("advcl", "acl", "relcl", "acl:relcl", "advcl:relcl"): total_non_comp_subs += 1 # Verbi e tempi verbs = [t for t in doc if t.pos_ in ("VERB", "AUX")] if lang == "it": # Legge i feature morfologici UD present = sum(1 for v in verbs if v.morph.get("Tense") == ["Pres"]) past = sum(1 for v in verbs if v.morph.get("Tense") == ["Past"]) parts = sum(1 for v in verbs if v.morph.get("VerbForm") == ["Part"]) else: present = sum(1 for v in verbs if v.tag_ in present_tags) past = sum(1 for v in verbs if v.tag_ in past_tags) parts = sum(1 for v in verbs if v.tag_ in part_tags) tenses = [v.tag_ for v in verbs] stability = (Counter(tenses).most_common(1)[0][1] / len(verbs)) if verbs else 0 sub_labels = {"csubj", "ccomp", "parataxis", "advcl", "acl", "xcomp", "advcl:relcl", "csub:pass", "csubj:outer", "acl:relcl"} nsub = sum(1 for t in doc if t.dep_ in sub_labels) total_root_dist = sum(abs(t.i - t.sent.root.i) for t in doc) rel_clauses = sum(1 for t in doc if t.dep_ in ["relcl", "acl:relcl", "advcl:relcl"]) adj_count = sum(1 for t in doc if t.pos_ == "ADJ") attr_adjs = sum(1 for t in doc if t.lemma_.lower() in active_adj and t.pos_ == "ADJ") nouns = sum(1 for t in doc if t.pos_ == "NOUN") mods = sum(1 for t in doc if t.dep_ in ("amod", "nmod", "poss", "nummod", "prep", "case", "mark") and t.head.pos_ in ("NOUN", "PROPN")) return { "avg_sent_len": round(num_tokens / num_sents, 2), "root_dist": round(total_root_dist / num_tokens, 2), "sub_ratio": round(nsub / num_sents, 4), "present_ratio": round(present / len(verbs), 4) if verbs else 0, "past_ratio": round(past / len(verbs), 4) if verbs else 0, "participle_ratio": round(parts / len(verbs), 4) if verbs else 0, "rel_clauses_per_sent": round(rel_clauses / num_sents, 4), "attr_adjs_freq": round(attr_adjs / num_tokens, 4), "attr_adjs_ratio": round(attr_adjs / adj_count, 4) if adj_count > 0 else 0, "adj_x_sent": round(adj_count / num_sents, 4), "adj_count": round(adj_count, 4), "mod_per_noun": round(mods / nouns, 4) if nouns else 0, "punct_pairs_per_sent": round(total_punct_pairs / num_sents, 4), "svo_inversions_per_sent": round(total_svo_inversions / num_sents, 4), "non_comp_sub_per_sent": round(total_non_comp_subs / num_sents, 4), }