Skip to content

Commit 10429e8

Browse files
authored
Merge pull request #3 from erickcan/v2.0
V2.0
2 parents cd962c6 + 010f40f commit 10429e8

File tree

11 files changed

+467
-370
lines changed

11 files changed

+467
-370
lines changed

README.md

Lines changed: 86 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2,132 +2,130 @@
22
A command-line sound change applier written in Python.
33

44
## Setup
5-
To begin using this sound change applier, you just need to download the executable (`.exe`) file in the [releases](https://github.com/erickcan/sound-change-applier/releases) tab.
6-
7-
You can also clone this repo and use the `sca.bat` file:
5+
To begin using this sound change applier, you just need to download the executable (`.exe`) file in the [Releases page](https://github.com/erickcan/sound-change-applier/releases) and use it through the command-line prompt:
6+
```
7+
$ sca sca-options
88
```
9+
You can also clone this repo and use the `sca.bat` or `sca.sh` file:
10+
```sh
911
$ git clone https://github.com/erickcan/sound-change-applier.git
12+
$ sca sca-options # if Windows
13+
$ ./sca.sh sca-options # if Linux
1014
```
1115

1216
## Command line
13-
There are two ways of applying sound changes using the command line: `--file-based-sound-change` and `--named-sound-change`.
14-
15-
- `--file-based-sound-change` (or `-f`) applies a set of rules to a set of words, each defined in their own files, and creates a text file with the changed words.
16-
- `--named-sound-change` (or `-n`) applies a named sound change to words passed on the command line, and then prints the words after the change.
17-
18-
### Usage
19-
For `--file-based-sound-change`:<sup name="a1">[[1]](#f1)</sup>
2017
```
21-
$ sca sound-classes-file -f rules-file words-file [--csv-output]
18+
sca (-f rules-file words-file | -n named-rules-json named-rule words)
19+
[--csv-output] [-s sound-classes-json | --no-sound-classes]
2220
```
23-
where:
24-
- `sca` is the sound change applier;
25-
- `sound-classes-file` is a JSON file where sound classes are defined;
26-
- `rules-file` is a text file where the rules to apply are;
27-
- `words-file` is a text file where the words to which the rules will be applied;
28-
- `--csv-output` is an optional argument that, if selected, creates a CSV file with the before and after of the words instead of a text file.
29-
30-
---
31-
32-
For `--named-sound-change`:<sup name="a2">[[2]](#f2)</sup>
33-
```
34-
$ sca sound-classes-file -n named-rules-file named-rule words [--csv-output]
35-
```
36-
where:
37-
- `sca` is the sound change applier;
38-
- `sound-classes-file` is a JSON file where sound classes are defined;
39-
- `named-rules-file` is a JSON file where sound changes and their names are defined;
40-
- `named-rule` is the name of the rule to apply (should be defined in the `named-rules-file` file);
41-
- `words` is a string with the words to change (if more than one word, should be inside quotes and separed by spaces);
42-
- `--csv-output` is an optional argument that, if selected, creates a CSV file with the before and after of the words instead of just printing the words.
21+
### Usage
22+
- `--file-based-sound-change` | `-f` to apply a set of rules to a set of words, each defined in a separate file, creating a text file with the changed words;
23+
- `rules-file`: text file with the rules
24+
- `words-file`: text file with the words
25+
- `--named-sound-change` | `-n` to apply a named sound change to words passed on the command line, and then prints the words after the change;
26+
- `named-rules-json`: JSON file where sound classes are defined
27+
- `named-rule`: name of the rule to apply (defined in `named-rules-json`)
28+
- `words`: words to apply the sound change
29+
- `--csv-output` to create a before and after of the changed words;
30+
- `--sound-classes-file` | `-s` to specify the JSON file where the sound classes are defined (see also [Default sound classes](#default-sound-classes));
31+
- `--no-sound-classes` to not make use of sound classes.
4332

4433
### Examples
4534
Suppose the following files:
46-
47-
> sound_classes.json
48-
```json
49-
{
50-
"V": "aeiou",
51-
"C": "bcdfghjklmnpqrstvwxyz",
52-
"P": "pbtdkg",
53-
"F": "fvsz",
54-
"N": "mn",
55-
"S": "sz"
56-
}
57-
```
5835
> named_rules.json
5936
```json
6037
{
61-
"terminal-devoicing": "[bdgz] -> [ptks] / _#",
62-
"h-dropping": "h -> _ / _",
63-
"z-rhotacization": "z -> r / V_V",
64-
"/æ/-raising": "æ -> eə / _N"
38+
"terminal-devoicing": "[bdgz] > [ptks] / _#",
39+
"h-dropping": "h -> _ / _",
40+
"z-rhotacization": "z -> r / V_V",
41+
"l-vocalization": "l => w / _!V",
42+
"/æ/-raising": "æ => eə / _N",
43+
"th-fronting": "[θð] -> [fv] / _"
6544
}
6645
```
67-
6846
> rules.txt
6947
```
70-
d -> ð / V_V
71-
[ae] -> [ãẽ] / _N
72-
[aou] -> [äöü] / _e
73-
l -> w / _#
74-
s -> z / [bdg]_
48+
d => ð / V_V
49+
[gk] -> _ / #_n
50+
a -> ə / _#
51+
l -> w / _!V
52+
[aou]e>[æøy]/ _
53+
sw > s / _[ou]
54+
[ao]N > [ãõ]/ _
55+
s => z /[bdg]_
56+
e -> _ / CC_#
7557
```
76-
7758
> words.txt
7859
```
79-
ada
60+
aeon
8061
beds
8162
clue
8263
daemon
64+
knowledge
65+
laterals
66+
media
8367
sand
84-
vowel
68+
swords
8569
```
8670

87-
If this were passed to the command line:
71+
If this were passed to the command line,
8872
```
89-
$ sca sound_classes.json -f rules.txt words.txt --csv-output
73+
$ sca -f rules.txt words.txt --csv-output
9074
```
91-
The following file would be created:
92-
75+
the following file would be created:
9376
> sound-change-YYYY-MM-DD-HH-MM-SS.csv
9477
```csv
95-
ada,aða
78+
aeon,æõ
9679
beds,bedz
97-
clue,clüe
98-
daemon,daẽmon
99-
sand,sãnd
100-
vowel,vowew
80+
clue,cly
81+
daemon,dæmõ
82+
knowledge,nowledg
83+
laterals,lateraws
84+
media,meðiə
85+
sand,sãd
86+
swords,sordz
10187
```
10288

103-
And if the following were passed into the command line:
89+
And if the following were passed into the command line,
10490
```
105-
$ sca sound_classes.json -n named_rules.json h-dropping "here he had hallucinated"
91+
$ sca -n named_rules.json h-dropping "here he had hallucinated"
10692
```
107-
It would print:
93+
it would print:
10894
```
109-
ere
110-
e
111-
ad
112-
allucinated
95+
ere e ad allucinated
11396
```
11497

11598
## Rule notation
116-
The rules should be written in the form `x -> y / a_b`, where `x` becomes `y` when `x` is between `a` and `b`.
99+
The rules can be written in any of the following forms:
100+
```
101+
x > y / a_b
102+
x -> y / a_b
103+
x => y / a_b
104+
```
105+
where `x` becomes `y` when `x` is between `a` and `b`. (Spaces between `>`, `->`, `=>` and `/` are insignificant.)
117106

118107
### Symbols
119-
| symbol | meaning | example |
120-
| ------- | ------------- | ----------------------------------------- |
121-
| `#` | word boundary | `_#` : end of word |
122-
| `A..Z` | sound class | `Gt` : class `G` followed by `t` |
123-
| `[xyz]` | ad-hoc class | `u[rl]` : `u` followed by `r` or `l` |
124-
| `_` | everywhere | `ð -> d / _` : `ð` becomes `d` everywhere |
125-
| `_` | sound-eraser | `h -> _` : deletes `h` |
126-
127-
128-
## Dependencies
129-
This project has no dependencies. It only uses modules of the Python Standard Library.
130-
131-
## Footnotes
132-
1. <b name="f1"></b> `$ sca -f rules-file words-file [--csv-output] sound-classes-file` and `$ sca [--csv-output] -f rules-file words-file sound-classes-file` are also valid. [](#a1)
133-
2. <b name="f2"></b> `$ sca -n named-rules-file named-rule words [--csv-output] sound-classes-file` and `$ sca [--csv-output] -n named-rules-file named-rule words` are also valid. [](#a2)
108+
| symbol | meaning | example |
109+
| ------- | ---------------- | ------------------------------------------- |
110+
| `#` | word boundary | `_#`: end of word, `#_`: start of word |
111+
| `A..Z` | sound class | `Gt`: class `G` followed by `t` |
112+
| `[xyz]` | ad-hoc class | `u[rl]`: `u` followed by `r` or `l` |
113+
| `> _` | sound-eraser | `h -> _`: deletes `h` |
114+
| `_` | everywhere, when | `_x`: when followed by `x`, `_`: everywhere |
115+
| `!_` | not proceeded by | `u!_`: when not proceeded by `u` |
116+
| `_!` | not followed by | `_!n`: when not followed by `n` |
117+
118+
## Appendix
119+
120+
### Default sound classes
121+
If neither `--sound-classes-file` nor `--no-sound-classes` are used, the following sound classes are used:
122+
```json
123+
{
124+
"V": "aeiou",
125+
"C": "bcdfghjklmnpqrstvwxyz",
126+
"P": "pbtdkg",
127+
"F": "fvsz",
128+
"N": "mn",
129+
"S": "sz"
130+
}
131+
```

main.py

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,41 @@
1-
import os
21
from sys import argv
3-
from csv import writer
42

5-
from src import *
3+
from src import io
4+
from src import sound_changer as sc
65

76

8-
def before_after_csv(before: list[str], after: list[str], filename: str):
9-
"""Create a CSV file with two columns in the cwd and print that the file was created."""
10-
filename = f"{os.path.join(os.getcwd(), filename)}.csv"
11-
with open(filename, newline="", encoding="utf-8", mode="w") as f:
12-
for w in zip(before, after):
13-
writer(f).writerow(u.strip() for u in w)
7+
def main(args: list[str]):
8+
sca_args = io.ScaArgs(args)
149

15-
created_file(filename)
10+
if sca_args.ndsc is not None:
11+
named_rules = io.json_to_dict(sca_args.ndsc[0])
12+
chosen_rule = named_rules[sca_args.ndsc[1]]
13+
words = sca_args.ndsc[2].split()
1614

15+
phon_rule = sc.PhonRule(chosen_rule, sca_args.sound_classes)
16+
changed_words = list(map(phon_rule.apply, words))
1717

18-
def try_changes_words(rules: list[str], words: list[str], sound_classes: HashableDict) -> list[str]:
19-
"""Try to change words, raise NotPhonRule if any rule is not valid."""
20-
try:
21-
changed_words = changes_words(rules, words, sound_classes)
22-
except NotPhonRule as e:
23-
exit(e)
24-
25-
return changed_words
26-
27-
28-
def main(args):
29-
args_dict = parse_cmd_args(args)
30-
31-
sound_classes = try_open_json(args_dict['sound-classes-file'], "sound-classes-file")
32-
33-
if (nsc := args_dict['named_sound_change']) is not None:
34-
35-
named_rules = try_open_json(nsc[0], "named-rules-file")
36-
chosen_rule = named_rules[nsc[1]]
37-
words = nsc[2].split()
18+
if not sca_args.csv_output:
19+
print(" ".join(changed_words))
3820

39-
changed_words = try_changes_words([chosen_rule], words, sound_classes)
21+
else:
22+
rules = io.read_file(sca_args.fbsc[0])
23+
words = io.read_file(sca_args.fbsc[1])
4024

41-
if args_dict['csv_output'] == True:
42-
filename = make_filename()
43-
before_after_csv(words, changed_words, filename)
25+
phon_rules = sc.PhonRules(rules, sca_args.sound_classes)
26+
changed_words = list(map(phon_rules.apply, words))
4427

45-
else:
46-
for w in changed_words:
47-
print(w)
28+
if not sca_args.csv_output:
29+
filename = f"sound-change-{io.today_str()}"
30+
io.write_txt(filename, changed_words)
4831

49-
if (fbsc := args_dict['file_based_sound_change']) is not None:
32+
if sca_args.csv_output:
33+
filename = f"sound-change-{io.today_str()}"
34+
io.before_after_csv(words, changed_words, filename)
5035

51-
rules = try_open_txt(fbsc[0], "rules-file")
52-
words = try_open_txt(fbsc[1], "words-file")
5336

54-
changed_words = try_changes_words(rules, words, sound_classes)
55-
56-
filename = make_filename()
57-
58-
if args_dict['csv_output'] == True:
59-
before_after_csv(words, changed_words, filename)
60-
61-
else:
62-
filename = f"{os.path.join(os.getcwd(), filename)}.txt"
63-
with open(filename, encoding="utf-8", mode="w") as f:
64-
f.writelines(word + '\n' for word in changed_words)
65-
created_file(filename)
66-
67-
68-
if __name__ == '__main__':
69-
main(argv[1:])
37+
if __name__ == "__main__":
38+
try:
39+
main(argv[1:])
40+
except Exception as e:
41+
exit(f"{type(e).__name__}: {e}")

sca.bat

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
@echo off
2-
python main.py %*
3-
pause
1+
@python main.py %*

sca.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/bash
2+
python3 main.py "$@"

src/__init__.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +0,0 @@
1-
from src.sound_changer import changes_words
2-
from src.cmd_args import parse_cmd_args
3-
from src.small_functions import created_file, make_filename
4-
from src.errs import try_open_json, try_open_txt, NotPhonRule
5-
from src.hash_dict import HashableDict

src/cmd_args.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)