~chambln/steno

6d5333f57ac9187f9c12665b0a600ac07dcf57b8 — Gregory Chamberlain 2 months ago ba29077
Rewrite in python3
1 files changed, 109 insertions(+), 132 deletions(-)

M steno
M steno => steno +109 -132
@@ 1,137 1,114 @@
#!/bin/sh -f
# steno - machine shorthand
# Copyright (C) 2020  Gregory Chamberlain <greg@cosine.blue>
#!/usr/bin/env python3

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
from sys import stdin

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

SHAPES='
{
MACHINE = (
    # Onset.
    sub(/zk/, "f", $1)
    sub(/sg/, "v", $1)

    sub(/sj/, "ʃ", $1)

    sub(/zgd/, "m", $1)
    sub(/zg/, "n", $1)

    sub(/kg/, "h", $1)

    sub(/kt/, "p", $1)
    sub(/gd/, "b", $1)

    sub(/gt/, "θ", $1)
    sub(/kd/, "ð", $1)

    sub(/td/, "l", $1)
    sub(/rj/, "w", $1)

    sub(/tj/, "tʃ", $1)
    sub(/dj/, "dʒ", $1)
    (
        [
            ('s', 'q'), ('z', 'a'),
            ('k', 'w'), ('g', 's'),
            ('t', 'e'), ('d', 'd'),
            ('r', 'r'), ('j', 'f')
        ],
        {
            "sg": "v",
            "sj": "ʃ",
            "zk": "f",
            "zg": "n",
            "zgd": "m",
            "zj": "ʒ",
            "kg": "h",
            "kt": "p",
            "kd": "ð",
            "gt": "θ",
            "gd": "b",
            "td": "l",
            "tj": "tʃ",
            "dj": "dʒ",
            "rj": "w",
        }
    ),

    # Nucleus.
    sub(/a\*ɪ/, "aɪə", $2)
    sub(/e\*ɪ/, "eɪə", $2)
    sub(/ae\*ɪ/, "ɔɪə", $2)
    sub(/\*ɪ/, "ɪə", $2)
    sub(/aeɪʊ/, "ɔ", $2)
    sub(/aeɪ/, "ɔɪ", $2)
    sub(/aeʊ/, "əʊ", $2)
    sub(/aɪʊ/, "ɑ", $2)
    sub(/eɪʊ/, "ɜ", $2)
    sub(/ae/, "ɒ", $2)
    sub(/eʊ/, "u", $2)
    sub(/ɪʊ/, "i", $2)

    # Coda
    sub(/ln/, "s", $3)
    sub(/ng/, "ŋ", $3)
    sub(/nk/, "ŋk", $3)
    sub(/tdk/, "mp", $3)
    sub(/td/, "m", $3)
    sub(/tk/, "p", $3)
    sub(/dg/, "b", $3)
    sub(/dkg/, "dʒ", $3)
    sub(/kg/, "ʃ", $3)
    sub(/kz/, "f", $3)
    sub(/gs/, "v", $3)

    # Remove hyphens
    sub(/-/, "")

    print $1 $2 $3
}
'

keyboard() {
    while IFS=' ,()' read -r word _ _ _ _ _ keysym _; do
        case $word in
            KeyPress|KeyRelease) event=$word ;;
            state) printf '%s\n' "$event $keysym" ;;
        esac
    done
}

papertape() {
    while IFS=' ' read -r event keysym; do
        case $event in
        KeyPress)
            set -- "$@" "$keysym"
            case $keysym in Escape) set --; esac
            for key in [0-9] q a w s e d r f \
                       c v [tgyh] n m \
                       u j i k o l p semicolon bracketleft apostrophe; do
                case " $* " in
                    *' '$key' '*) printf %s "$key";;
                esac
                printf :
            done
            printf '\n'
            ;;
        KeyRelease)
            for heldkey; do
                shift
                [ _"$heldkey" = _"$keysym" ] && break
                set -- "$@" "$heldkey"
            done
            [ $# -eq 0 ] && printf '\n'
        esac
    done
}

transliterate() {
    while IFS=: read num s z k g t d r j a e star I U l n t_ d_ k_ g_ s_ z_ t__ d__; do
        lhs=${num:+'#'}${s:+s}${z:+z}${k:+k}${g:+g}${t:+t}${d:+d}${r:+r}${j:+j}
        mid=${a:+a}${e:+e}${star:+'*'}${I:+ɪ}${U:+ʊ}
        rhs=${l:+l}${n:+n}${t_:+t}${d_:+d}${k_:+k}${g_:+g}${s_:+s}${z_:+z}${t__:+t}${d__:+d}
        [ "$lhs$mid$rhs" ] && printf %s "${lhs:--} ${mid:-ə} ${rhs:--}"
        printf '\n'
    done
}

overwrite() {
    while IFS= read -r line; do
        printf '\n'
        [ "$line" ] && printf '\033[A\033[K%s' "$line"
    done
}

if [ $# -eq 0 ] && [ -t 0 ]; then
    trap 'xset r on' EXIT INT
    xset r off
    xev -event keyboard
else
    cat "$@"
fi | keyboard | papertape  | transliterate | awk -W interactive "$SHAPES" | overwrite
printf '\n'
    (
        [
            ('a', 'c'), ('e', 'v'),
            ('*', 'tgyh'),
            ('I', 'n'), ('U', 'm')
        ],
        {
            "a*ɪ": "aɪə",
            "e*ɪ": "eɪə",
            "ae*ɪ": "ɔɪə",
            "*ɪ": "ɪə",
            "aeɪʊ": "ɔ",
            "aeɪ": "ɔɪ",
            "aeʊ": "əʊ",
            "aɪʊ": "ɑ",
            "eɪʊ": "ɜ",
            "ae": "ɒ",
            "eʊ": "u",
            "ɪʊ": "i",
        }
    ),

    # Coda.
    (
        [
            ('l', 'u'), ('n', 'j'),
            ('t', 'i'), ('d', 'k'),
            ('k', 'o'), ('g', 'l'),
            ('s', 'p'), ('z', ['semicolon']),
            ('t', ['bracketleft']), ('d', ['apostrophe'])
        ],
        {
            "ln": "s",
            "ng": "ŋ",
            "nk": "ŋk",
            "tdk": "mp",
            "td": "m",
            "tk": "p",
            "dg": "b",
            "dkg": "dʒ",
            "kg": "ʃ",
            "kz": "f",
            "gs": "v",
        }
    )
)

def read_chords(lines):
    def read_xev(lines):
        for line in lines:
            words = line.split()
            if words and words[0] in ("KeyPress", "KeyRelease"):
                event = words[0]
            elif len(words) >= 5 and words[4].strip("(),") == "keysym":
                keysym = words[6].strip("(),")
                yield event, keysym

    released, pressed = [], []
    for event, keysym in read_xev(lines):
        if event == "KeyPress":
            pressed += [keysym]
        else:
            pressed = [k for k in pressed if k != keysym]
            released += [keysym]
        if released and not pressed:
            yield released
            released = []

def transcribe(chord):
    return [
        [c for c, ks in keys if any([k in chord for k in ks])]
        for keys, clusters in MACHINE
    ]

def main():
    strokes = (transcribe(chord) for chord in read_chords(stdin))
    for stroke in strokes:
        print(stroke)

if __name__ == '__main__':
    main()