Braxen compounds
Extract compounds from Braxen
BRAXEN_LOCATION = "/Users/joregan/Playing/braxen/dict/braxen-sv.tsv"
from pathlib import Path
BRAXEN_PATH = Path(BRAXEN_LOCATION)
def has_boundary(phones):
boundaries = ["|", "~", "-"]
for phone in phones:
if phone in boundaries:
return True
return False
def clean_phones(phones):
boundaries = ["|", "~", "-", "."]
out_phones = []
for phone in phones:
if phone not in boundaries:
out_phones.append(phone)
return out_phones
errata = {
"10-killarna": (11, ".", "-"), # 825641
"2-dokumentär": (8, ".", "-"), # 825341
"70-talisternas": (11, ".", "-"), # 806484
}
reverse = {}
with open(BRAXEN_LOCATION) as inf:
for line in inf.readlines():
line = line.strip()
if line.startswith("#"):
continue
parts = line.split("\t")
word = parts[0]
phone_str = parts[1]
if word in errata:
mod = errata[word]
if phone_str[mod[0]] == mod[1]:
chars = list(phone_str)
chars[mod[0]] = mod[2]
phone_str = "".join(chars)
phones = phone_str.split(" ")
if has_boundary(phones):
continue
if word.startswith("-"):
continue
if word.startswith("\ufb01"):
# fi ligature
continue
if not phone_str in reverse:
reverse[phone_str] = []
if word not in reverse[phone_str]:
reverse[phone_str].append(word)
import json
with open("braxen-simple-reverse.json", "w") as outf:
json.dump(reverse, outf, indent=4)
LANG_FIXES = """
playback 376738 eng
playboy 376739 eng
play-off 782183 eng
playa 376734 spa
Halloweens 880274 eng
återvändaren 837944 swe
temporomandibular 718339 swe
# (maybe lat? definitely not eng with that pronunciation)
командитното 831898 bul
Štépnička 869924 cze
Štépničkas 869925 cze
Škoda 733908 cze
Bandsåg 739294 swe
# also 'VB PRT AKT' -> NN UTR SIN IND NOM (?)
Gomez 634927 spa
Gražinytė 877254 lit
Gražinytės 877255 lit
"""
# wget w|get 732342 swe|eng
# väggsquat vägg|squat 828971 swe|eng
# personlighetsoro personlighet|soro 878836 swe|ara
# påklädningsapraxi påklädnings|apraxi 839252 swe|lat
def split_list(input, delimiters):
output = []
current = []
for item in input:
if item in delimiters:
if current:
output.append(current)
current = []
else:
current.append(item)
if current:
output.append(current)
return output
assert split_list('n \'o l | n "o l - m ,a rt . rs ex n'.split(" "), ["|", "~", "-"]) == [['n', "'o", 'l'], ['n', '"o', 'l'], ['m', ',a', 'rt', '.', 'rs', 'ex', 'n']]
$ grep '^[0-9]-[0-9]-' ~/Playing/braxen/dict/braxen-sv.tsv |awk -F'\t' '{print $1}'|awk -F'-' '{print $3}'|sort|uniq
1
åringar
årsåldern
förlust
förlusten
förluster
försvar
ledning
ledningen
mål
målen
målet
målskytt
målskytten
match
matchen
matcher
reducering
reduceringen
seger
segern
skola
smällen
underläge
underlägen
underläget
vinsten
compounds = {}
hyphenated = {}
already_split = {}
first_only = {}
second_only = {}
def resolve_parts(parts, word, reverse):
"""Try to greedily consume `word` using phone-parts; backtrack on failure.
Returns list of word-parts covering the whole word, or None."""
def rec(i, rest):
if i == len(parts):
return [] if rest == "" else None
part_str = " ".join(parts[i])
for cand in reverse.get(part_str, []):
if rest.startswith(cand):
tail = rec(i + 1, rest[len(cand):])
if tail is not None:
return [cand] + tail
return None
return rec(0, word)
with open(BRAXEN_LOCATION) as inf:
for line in inf.readlines():
line = line.strip()
if line.startswith("#"):
continue
parts_raw = line.split("\t")
word = parts_raw[0]
phone_str = parts_raw[1]
phones = phone_str.split(" ")
if not has_boundary(phones):
continue
if word.startswith("-"):
continue
if word.startswith("\ufb01"): # fi ligature
continue
if word[0].isdigit():
continue
parts = split_list(phones, ["|", "~", "-"])
if "-" in word:
word_parts = word.split("-")
if len(word_parts) == len(parts):
hyphenated[word] = word_parts
continue
if " " in word:
word_parts = word.split(" ")
if len(word_parts) == len(parts):
already_split[word] = word_parts
continue
# Full mapping: every phone-part maps to a word-part covering the whole word.
resolved = resolve_parts(parts, word, reverse)
if resolved is not None:
compounds[word] = resolved
continue
# Two-part fallback: only meaningful when there are exactly two phone-parts.
if len(parts) == 2:
first_str = " ".join(parts[0])
second_str = " ".join(parts[1])
first_match = next(
(w for w in reverse.get(first_str, []) if word.startswith(w)),
None,
)
second_match = next(
(w for w in reverse.get(second_str, []) if word.endswith(w)),
None,
)
if first_match is not None:
first_only[word] = [first_match, word[len(first_match):]]
if second_match is not None:
second_only[word] = [word[:len(word) - len(second_match)], second_match]
len(hyphenated), len(already_split), len(compounds)
with open("braxen-compounds1.json", "w") as outf:
json.dump(compounds, outf, indent=4)
with open("braxen-hyphenated1.json", "w") as outf:
json.dump(hyphenated, outf, indent=4)
with open("braxen-already-split1.json", "w") as outf:
json.dump(already_split, outf, indent=4)
with open("braxen-first-only1.json", "w") as outf:
json.dump(first_only, outf, indent=4)
with open("braxen-second-only1.json", "w") as outf:
json.dump(second_only, outf, indent=4)