Fixed ACIP->TMW vowels like 'I etc.

Fixed ACIP->Unicode/TMW for BDE, which should be B-DE, not B+DE, because the former is legal Tibetan.

The ACIP->EWTS subroutine has improved.

TMW->Wylie and TMW->ACIP are improved in error cases.

TMW->ACIP has friendly embedded error messages now.
This commit is contained in:
dchandler 2003-09-12 05:06:37 +00:00
parent 16817d0b8e
commit 115d0e0e6c
14 changed files with 689 additions and 472 deletions

View file

@ -446,12 +446,18 @@ public class ACIPConverter {
if (!lastGuyWasNonPunct
|| (null != lastGuy
&& (lpl = lastGuy.get(lastGuy.size() - 1)).size() == 1
&& lpl.get(0).getLeft().equals("G")
&& // it's (G . anything)
// followed by some number
// of spaces (at least one,
// this one) and then a
// comma:
// "GU ," and "KU ," each have
// tshegs, but "GI ," and "KI
// ," each have a Tibetan
// space.
&& ((lpl.get(0).getLeft().equals("G")
|| lpl.get(0).getLeft().equals("K"))
&& (lpl.get(0).getRight().indexOf('U') < 0))
&&
// it's (G . anything)
// followed by some number of
// spaces (at least one, this
// one) and then a comma:
peekaheadFindsSpacesAndComma(scan, i+1))) {
if (null != writer) {
unicode = " ";

View file

@ -21,6 +21,7 @@ package org.thdl.tib.text.ttt;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.List;
import org.thdl.tib.text.DuffCode;
@ -30,7 +31,7 @@ import org.thdl.tib.text.TibTextUtils;
/** Canonizes some facts regarding the ACIP transcription system.
* @author David Chandler */
class ACIPRules {
public class ACIPRules {
/** {Ksh}, the longest consonant, has 3 characters, so this is
* three. */
public static int MAX_CONSONANT_LENGTH = 3;
@ -66,7 +67,7 @@ class ACIPRules {
// DLC I'm on my own with 'O and 'E and 'OO and 'EE, but
// GANG'O appears and I wonder... so here they are. It's
// consistent with 'I and 'A and 'U, at least: all the vowels
// may appear as K'vowel.
// may appear as K'vowel. DLC FIMXE: ask.
acipVowels.add(baseVowels[i][0]);
acipVowels.add('\'' + baseVowels[i][0]);
@ -140,6 +141,43 @@ class ACIPRules {
return consonants.contains(acip);
}
private static HashMap wylieToACIP = null;
/** Returns the ACIP transliteration corresponding to the THDL
Extended Wylie <em>atom</em> EWTS, or null if EWTS is not
recognized. */
public static String getACIPForEWTS(String EWTS) {
getWylieForACIPConsonant(null);
getWylieForACIPOther(null);
getWylieForACIPVowel(null);
String ans = (String)wylieToACIP.get(EWTS);
if (null == ans) {
StringBuffer finalAns = new StringBuffer(EWTS.length());
StringTokenizer sTok = new StringTokenizer(EWTS, "-+", true);
while (sTok.hasMoreTokens()) {
String part, tok = sTok.nextToken();
if (tok.equals("-") || tok.equals("+"))
part = tok;
else
part = (String)wylieToACIP.get(tok);
if (null == part) return null;
finalAns.append(part);
}
return finalAns.toString();
}
return ans;
}
/** Registers acip->wylie mappings in toWylie; registers
wylie->acip mappings in {@link #wylieToACIP}. */
private static void putMapping(HashMap toWylie, String ACIP, String EWTS) {
toWylie.put(ACIP, EWTS);
if (null == wylieToACIP) {
wylieToACIP = new HashMap(75);
wylieToACIP.put("_", " "); // oddball.
}
wylieToACIP.put(EWTS, ACIP);
}
private static HashMap acipConsonant2wylie = null;
/** Returns the EWTS corresponding to the given ACIP consonant
* (without the "A" vowel). Returns null if there is no such
@ -149,52 +187,52 @@ class ACIPRules {
acipConsonant2wylie = new HashMap(37);
// oddball:
acipConsonant2wylie.put("V", "w");
putMapping(acipConsonant2wylie, "V", "w");
// more oddballs:
acipConsonant2wylie.put("DH", "d+h");
acipConsonant2wylie.put("BH", "b+h");
acipConsonant2wylie.put("dH", "D+h");
acipConsonant2wylie.put("DZH", "dz+h");
acipConsonant2wylie.put("Ksh", "k+Sh");
acipConsonant2wylie.put("GH", "g+h");
putMapping(acipConsonant2wylie, "DH", "d+h");
putMapping(acipConsonant2wylie, "BH", "b+h");
putMapping(acipConsonant2wylie, "dH", "D+h");
putMapping(acipConsonant2wylie, "DZH", "dz+h");
putMapping(acipConsonant2wylie, "Ksh", "k+Sh");
putMapping(acipConsonant2wylie, "GH", "g+h");
acipConsonant2wylie.put("K", "k");
acipConsonant2wylie.put("KH", "kh");
acipConsonant2wylie.put("G", "g");
acipConsonant2wylie.put("NG", "ng");
acipConsonant2wylie.put("C", "c");
acipConsonant2wylie.put("CH", "ch");
acipConsonant2wylie.put("J", "j");
acipConsonant2wylie.put("NY", "ny");
acipConsonant2wylie.put("T", "t");
acipConsonant2wylie.put("TH", "th");
acipConsonant2wylie.put("D", "d");
acipConsonant2wylie.put("N", "n");
acipConsonant2wylie.put("P", "p");
acipConsonant2wylie.put("PH", "ph");
acipConsonant2wylie.put("B", "b");
acipConsonant2wylie.put("M", "m");
acipConsonant2wylie.put("TZ", "ts");
acipConsonant2wylie.put("TS", "tsh");
acipConsonant2wylie.put("DZ", "dz");
acipConsonant2wylie.put("W", "w");
acipConsonant2wylie.put("ZH", "zh");
acipConsonant2wylie.put("Z", "z");
acipConsonant2wylie.put("'", "'");
acipConsonant2wylie.put("Y", "y");
acipConsonant2wylie.put("R", "r");
acipConsonant2wylie.put("L", "l");
acipConsonant2wylie.put("SH", "sh");
acipConsonant2wylie.put("S", "s");
acipConsonant2wylie.put("H", "h");
acipConsonant2wylie.put("A", "a");
acipConsonant2wylie.put("t", "T");
acipConsonant2wylie.put("th", "Th");
acipConsonant2wylie.put("d", "D");
acipConsonant2wylie.put("n", "N");
acipConsonant2wylie.put("sh", "Sh");
putMapping(acipConsonant2wylie, "K", "k");
putMapping(acipConsonant2wylie, "KH", "kh");
putMapping(acipConsonant2wylie, "G", "g");
putMapping(acipConsonant2wylie, "NG", "ng");
putMapping(acipConsonant2wylie, "C", "c");
putMapping(acipConsonant2wylie, "CH", "ch");
putMapping(acipConsonant2wylie, "J", "j");
putMapping(acipConsonant2wylie, "NY", "ny");
putMapping(acipConsonant2wylie, "T", "t");
putMapping(acipConsonant2wylie, "TH", "th");
putMapping(acipConsonant2wylie, "D", "d");
putMapping(acipConsonant2wylie, "N", "n");
putMapping(acipConsonant2wylie, "P", "p");
putMapping(acipConsonant2wylie, "PH", "ph");
putMapping(acipConsonant2wylie, "B", "b");
putMapping(acipConsonant2wylie, "M", "m");
putMapping(acipConsonant2wylie, "TZ", "ts");
putMapping(acipConsonant2wylie, "TS", "tsh");
putMapping(acipConsonant2wylie, "DZ", "dz");
putMapping(acipConsonant2wylie, "W", "w");
putMapping(acipConsonant2wylie, "ZH", "zh");
putMapping(acipConsonant2wylie, "Z", "z");
putMapping(acipConsonant2wylie, "'", "'");
putMapping(acipConsonant2wylie, "Y", "y");
putMapping(acipConsonant2wylie, "R", "r");
putMapping(acipConsonant2wylie, "L", "l");
putMapping(acipConsonant2wylie, "SH", "sh");
putMapping(acipConsonant2wylie, "S", "s");
putMapping(acipConsonant2wylie, "H", "h");
putMapping(acipConsonant2wylie, "A", "a");
putMapping(acipConsonant2wylie, "t", "T");
putMapping(acipConsonant2wylie, "th", "Th");
putMapping(acipConsonant2wylie, "d", "D");
putMapping(acipConsonant2wylie, "n", "N");
putMapping(acipConsonant2wylie, "sh", "Sh");
}
return (String)acipConsonant2wylie.get(acip);
}
@ -207,14 +245,14 @@ class ACIPRules {
acipVowel2wylie = new HashMap(baseVowels.length * 4);
for (int i = 0; i < baseVowels.length; i++) {
acipVowel2wylie.put(baseVowels[i][0], baseVowels[i][1]);
acipVowel2wylie.put('\'' + baseVowels[i][0], baseVowels[i][2]);
acipVowel2wylie.put(baseVowels[i][0] + 'm', baseVowels[i][1] + 'M');
acipVowel2wylie.put('\'' + baseVowels[i][0] + 'm', baseVowels[i][2] + 'M');
acipVowel2wylie.put(baseVowels[i][0] + ':', baseVowels[i][1] + 'H');
acipVowel2wylie.put('\'' + baseVowels[i][0] + ':', baseVowels[i][2] + 'H');
acipVowel2wylie.put(baseVowels[i][0] + "m:", baseVowels[i][1] + "MH");
acipVowel2wylie.put('\'' + baseVowels[i][0] + "m:", baseVowels[i][2] + "MH");
putMapping(acipVowel2wylie, baseVowels[i][0], baseVowels[i][1]);
putMapping(acipVowel2wylie, '\'' + baseVowels[i][0], baseVowels[i][2]);
putMapping(acipVowel2wylie, baseVowels[i][0] + 'm', baseVowels[i][1] + 'M');
putMapping(acipVowel2wylie, '\'' + baseVowels[i][0] + 'm', baseVowels[i][2] + 'M');
putMapping(acipVowel2wylie, baseVowels[i][0] + ':', baseVowels[i][1] + 'H');
putMapping(acipVowel2wylie, '\'' + baseVowels[i][0] + ':', baseVowels[i][2] + 'H');
putMapping(acipVowel2wylie, baseVowels[i][0] + "m:", baseVowels[i][1] + "MH");
putMapping(acipVowel2wylie, '\'' + baseVowels[i][0] + "m:", baseVowels[i][2] + "MH");
}
}
return (String)acipVowel2wylie.get(acip);
@ -228,27 +266,27 @@ class ACIPRules {
acipOther2wylie = new HashMap(20);
// DLC FIXME: check all these again.
acipOther2wylie.put(",", "/");
acipOther2wylie.put(" ", " ");
acipOther2wylie.put(".", "*");
acipOther2wylie.put("|", "|");
acipOther2wylie.put("`", "!");
acipOther2wylie.put(";", ";");
acipOther2wylie.put("*", "@");
acipOther2wylie.put("#", "@#");
acipOther2wylie.put("%", "~X");
acipOther2wylie.put("&", "&");
putMapping(acipOther2wylie, ",", "/");
putMapping(acipOther2wylie, " ", " ");
putMapping(acipOther2wylie, ".", "*");
putMapping(acipOther2wylie, "|", "|");
putMapping(acipOther2wylie, "`", "!");
putMapping(acipOther2wylie, ";", ";");
putMapping(acipOther2wylie, "*", "@");
putMapping(acipOther2wylie, "#", "@#");
putMapping(acipOther2wylie, "%", "~X");
putMapping(acipOther2wylie, "&", "&");
acipOther2wylie.put("0", "0");
acipOther2wylie.put("1", "1");
acipOther2wylie.put("2", "2");
acipOther2wylie.put("3", "3");
acipOther2wylie.put("4", "4");
acipOther2wylie.put("5", "5");
acipOther2wylie.put("6", "6");
acipOther2wylie.put("7", "7");
acipOther2wylie.put("8", "8");
acipOther2wylie.put("9", "9");
putMapping(acipOther2wylie, "0", "0");
putMapping(acipOther2wylie, "1", "1");
putMapping(acipOther2wylie, "2", "2");
putMapping(acipOther2wylie, "3", "3");
putMapping(acipOther2wylie, "4", "4");
putMapping(acipOther2wylie, "5", "5");
putMapping(acipOther2wylie, "6", "6");
putMapping(acipOther2wylie, "7", "7");
putMapping(acipOther2wylie, "8", "8");
putMapping(acipOther2wylie, "9", "9");
}
return (String)acipOther2wylie.get(acip);
}
@ -465,39 +503,52 @@ class ACIPRules {
/** Gets the duffcodes for vowel, such that they look good with
* the stack with hash key hashKey, and appends them to r. */
static void getDuffForACIPVowel(ArrayList r, DuffCode preceding, String vowel) {
static void getDuffForACIPVowel(ArrayList duff, DuffCode preceding, String vowel) {
if (null == vowel) return;
if (null == getWylieForACIPVowel(vowel)) // FIXME: expensive assertion! Use assert.
throw new IllegalArgumentException("Vowel " + vowel + " isn't in the small set of vowels we handle correctly.");
// Order matters here.
boolean context_added[] = new boolean[] { false };
if (vowel.startsWith("A")) {
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.WYLIE_aVOWEL);
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.WYLIE_aVOWEL, context_added);
} else if (vowel.indexOf("'U") >= 0) {
TibTextUtils.getVowel(r, preceding, "U");
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.U_VOWEL, context_added);
} else if (vowel.indexOf("'I") >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.I_VOWEL, context_added);
} else {
if (vowel.indexOf('\'') >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.A_VOWEL);
if (vowel.indexOf("EE") >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.ai_VOWEL);
else if (vowel.indexOf('E') >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.e_VOWEL);
if (vowel.indexOf("OO") >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.au_VOWEL);
else if (vowel.indexOf('O') >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.o_VOWEL);
if (vowel.indexOf('I') >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.i_VOWEL);
if (vowel.indexOf('U') >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.u_VOWEL);
if (vowel.indexOf('i') >= 0)
TibTextUtils.getVowel(r, preceding, THDLWylieConstants.reverse_i_VOWEL);
if (vowel.indexOf('\'') >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.A_VOWEL, context_added);
}
if (vowel.indexOf("EE") >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.ai_VOWEL, context_added);
} else if (vowel.indexOf('E') >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.e_VOWEL, context_added);
}
if (vowel.indexOf("OO") >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.au_VOWEL, context_added);
} else if (vowel.indexOf('O') >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.o_VOWEL, context_added);
}
if (vowel.indexOf('I') >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.i_VOWEL, context_added);
}
if (vowel.indexOf('U') >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.u_VOWEL, context_added);
}
if (vowel.indexOf('i') >= 0) {
TibTextUtils.getVowel(duff, preceding, THDLWylieConstants.reverse_i_VOWEL, context_added);
}
}
// DLC FIXME: Use TMW9.61, the "o'i" special combination, when appropriate.
if (vowel.indexOf('m') >= 0)
r.add(TibetanMachineWeb.getGlyph("M"));
if (vowel.indexOf('m') >= 0) {
DuffCode last = (DuffCode)duff.get(duff.size() - 1);
duff.remove(duff.size() - 1);
TibTextUtils.getBindu(duff, last);
}
if (vowel.indexOf(':') >= 0)
r.add(TibetanMachineWeb.getGlyph("H"));
duff.add(TibetanMachineWeb.getGlyph("H"));
}
}

View file

@ -1,3 +1,4 @@
// DLC NOW: KAsh ->Ksh here! optionally!
/*
The contents of this file are subject to the THDL Open Community License
Version 1.0 (the "License"); you may not use this file except in compliance

View file

@ -340,6 +340,22 @@ tstHelper("KA'", "[(K . A), (' . )]",
new String[] { },
"{G+G}{YE}{S}");
// DLC FIXME: warn about BDE vs. B+DE. color such differently. Maybe an inputter saw B+DE and typed in BDE, not thinking.
tstHelper("BDE", "{B}{DE}",
new String[] { "{B}{DE}", "{B+DE}" },
new String[] { "{B}{DE}" },
"{B}{DE}");
tstHelper("SHR'I", "{SH}{R'I}",
null,
null,
"{SH+R'I}");
// DLC FIXME: test EWTS {pouM}
// DLC FIXME: do TMW->ACIP->TMW->ACIP round-trip.
tstHelper("DRUG", "{D}{RU}{G}",
new String[] { "{D}{RU}{G}", "{D+RU}{G}" },
new String[] { "{D+RU}{G}" },
@ -7302,6 +7318,7 @@ tstHelper("ZUR");
"\u0f04\u0f05\u0f04\u0f05\u0f05\u0f67\u0f74\u0f7e\u0f7f\u0f0b\u0f42\u0fa2\u0f7d\u0f0b\u0f42\u0fb2\u0f7c\u0f08\u0f11\u0f0c\u0f0d");
uhelp("*#HUm: K+DHA GRO`;.,",
"\u0f04\u0f05\u0f04\u0f05\u0f05\u0f67\u0f74\u0f7e\u0f7f\u0f0b[#ERROR CONVERTING ACIP DOCUMENT: THE TSHEG BAR (\"SYLLABLE\") K+DHA IS ESSENTIALLY NOTHING.]\u0f0b\u0f42\u0fb2\u0f7c\u0f08\u0f11\u0f0c\u0f0d");
// DLC FIXME: the file ACIP_SHRI should be made into an ACIP->TMW automated test case
}
/** Tests some more tsheg bars, these from Dr. Lacey's critical

View file

@ -197,32 +197,42 @@ class TParseTree {
* stack can take every prefix, which is not the case in
* reality */
public TStackListList getUniqueParse(boolean noPrefixTests) {
TStackListList allLegalParses = new TStackListList(2); // save memory
// For Sanskrit+Tibetan:
TStackListList allNonillegalParses = new TStackListList(2); // save memory
// For Tibetan only:
TStackListList allStrictlyLegalParses = new TStackListList(2); // save memory
TStackListList legalParsesWithVowelOnRoot = new TStackListList(1);
ParseIterator pi = getParseIterator();
while (pi.hasNext()) {
TStackList sl = pi.next();
BoolPair bpa = sl.isLegalTshegBar(noPrefixTests);
if (bpa.isLegal) {
if (bpa.isLegalAndHasAVowelOnRoot)
BoolTriple bt = sl.isLegalTshegBar(noPrefixTests);
if (bt.isLegal) {
if (bt.isLegalAndHasAVowelOnRoot)
legalParsesWithVowelOnRoot.add(sl);
allLegalParses.add(sl);
if (!bt.isLegalButSanskrit)
allStrictlyLegalParses.add(sl);
allNonillegalParses.add(sl);
}
}
if (legalParsesWithVowelOnRoot.size() == 1)
return legalParsesWithVowelOnRoot;
else {
if (allStrictlyLegalParses.size() == 1)
return allStrictlyLegalParses;
if (allStrictlyLegalParses.size() > 2)
throw new Error("can this happen?");
if (legalParsesWithVowelOnRoot.size() == 2) {
if (legalParsesWithVowelOnRoot.get(0).size() != 1 + legalParsesWithVowelOnRoot.get(1).size())
throw new Error("Something other than the G-YA vs. GYA case appeared. Sorry for your trouble! " + legalParsesWithVowelOnRoot.get(0) + " ;; " + legalParsesWithVowelOnRoot.get(1));
return new TStackListList(legalParsesWithVowelOnRoot.get(1));
}
if (allLegalParses.size() == 2) {
if (allLegalParses.get(0).size() != 1 + allLegalParses.get(1).size())
throw new Error("Something other than the G-YA vs. GYA case appeared. Sorry for your trouble! " + allLegalParses.get(0) + " ;; " + allLegalParses.get(1));
return new TStackListList(allLegalParses.get(1));
if (allNonillegalParses.size() == 2) {
if (allNonillegalParses.get(0).size() != 1 + allNonillegalParses.get(1).size())
throw new Error("Something other than the G-YA vs. GYA case appeared. Sorry for your trouble! " + allNonillegalParses.get(0) + " ;; " + allNonillegalParses.get(1));
return new TStackListList(allNonillegalParses.get(1));
}
return allLegalParses;
return allNonillegalParses;
}
}

View file

@ -121,16 +121,16 @@ class TStackList {
* happen. */
public ListIterator listIterator() { return al.listIterator(); }
/** Returns a pair with {@link BoolPair#isLegal} true if and only
* if this list of stacks is a legal tsheg bar by the rules of
* Tibetan syntax (sometimes called rules of spelling). If this
* is legal, then {@link BoolPair#isLegalAndHasAVowelOnRoot} will
* be true if and only if there is an explicit {A} vowel on the
* root stack.
/** Returns a pair with {@link BoolTriple#isLegal} true if and
* only if this list of stacks is a legal tsheg bar by the rules
* of Tibetan syntax (sometimes called rules of spelling). If
* this is legal, then {@link
* BoolTriple#isLegalAndHasAVowelOnRoot} will be true if and only
* if there is an explicit {A} vowel on the root stack.
* @param noPrefixTests true if you want to pretend that every
* stack can take every prefix, which is not the case in
* reality */
public BoolPair isLegalTshegBar(boolean noPrefixTests) {
public BoolTriple isLegalTshegBar(boolean noPrefixTests) {
// DLC handle PADMA and other Tibetanized Sanskrit fellows consistently. Right now we only treat single-stack Sanskrit guys as legal.
TTGCList tgcList = new TTGCList(this);
@ -162,7 +162,9 @@ class TStackList {
}
}
}
return new BoolPair(isLegal, isLegalAndHasAVowelOnRoot);
return new BoolTriple(isLegal,
(candidateType == "single-sanskrit-gc"),
isLegalAndHasAVowelOnRoot);
}
private static final boolean ddebug = false;
@ -232,11 +234,15 @@ class TStackList {
}
/** Too simple to comment. */
class BoolPair {
class BoolTriple {
boolean isLegal;
boolean isLegalButSanskrit; // some subset are legal but legal Sanskrit -- the single sanskrit stacks are this way, such as B+DE.
boolean isLegalAndHasAVowelOnRoot;
BoolPair(boolean isLegal, boolean isLegalAndHasAVowelOnRoot) {
BoolTriple(boolean isLegal,
boolean isLegalButSanskrit,
boolean isLegalAndHasAVowelOnRoot) {
this.isLegal = isLegal;
this.isLegalButSanskrit = isLegalButSanskrit;
this.isLegalAndHasAVowelOnRoot = isLegalAndHasAVowelOnRoot;
}
}