Subject: Pinyin parser

From: [email-masked]
Date: Mon, 12 Nov 90 00:54:27 MST

Below is a short Pinyin parser that doesn't depend on tone numbers for
most cases.  It only parses Mainland Pinyin spellings right now, but I
am working on parsers for Cantonese and Taiwan Pinyin spellings.  It
does not do any spell checking, but this would be very easy to add.

The parser takes a phrase and breaks it into the individual Pinyin.
There are some cases that are ambiguous, like "huan" vs. "hu an" and
"bin an suo" vs. "bi nan suo".  In cases like these, some sort of
divider is needed to get the correct parse; like a tone number, space,
tab, or carriage return.

I am working with a small sample of phrases(about 3000) that have no
tone numbers.

This code is sort of a first draft, so it's VERY messy, but if you
have comments, bug reports, or notice that I missed something, please
send me email.  Please feel free to modify the code for your own use.

BTW, this code was written to be part of a modified X11R4 Athena
AsciiText Widget that has Chinese and Japanese support.  I hope to get
it out soon.

----------------- Code Start -------------------------
/*
 *        Pinyin Parser
 *
 * Copyright (C) 1990 Mark Leisher.
 *
 * Author:   Mark Leisher
 * Internet: [email-masked]
 * Bitnet:   [email-masked]
 *
 * 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 1, or (at your option)
 * any later version.
 *
 * 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.
 *
 * A copy of the GNU General Public License can be obtained from this
 * program's author (send electronic mail to [email-masked]) or from
 * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
 * 02139, USA.
 */

#include <stdio.h>
#include <stdio.h>
#include <ctype.h>

#define isvowel(c) (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')

char *
downcase_string(str)
char *str;
{
    int c;
    char *out = (char *)malloc(strlen(str) + 1),
         *ptr = out;

    while((c = *str++)) *ptr++ = isupper(c) ? tolower(c) : c;
    *ptr = '\0';
    return out;
}

#define INITIAL_ARRAY_SIZE 5
#define ARRAY_INC 5

char **
parse_pinyin(str, n)
char *str;
int *n;
{
    int found = 0, c, len, i = 0, arr_size,
        vcount = 0;
    char *ptr, **out = NULL;

    *n = 0;
    while (*str) {

/* skip initial non-alphanumeric chars */

        while (!isalnum(*str)) str++;
        ptr = str;

        while (!found && (c = *str++)) {
            if (!isalnum(c)) {
                found = 1;
            } else {
                if (isvowel(c))
                  vcount++;
                else
                  vcount = 0;

                if (vcount == 2 && *str != 'i' && *str != 'o' && *str != 'n') {
                    if (isdigit(*str))
                      str++;
                    if (*str == 'r') {
                        if (*(str+1) && !isvowel(*(str+1)))
                          str--;
                    }
                    found = 1;
                    vcount = 0;
                } else {
                    switch(c) {
                      case 'e':
                        if (*str == 'r') {
                            if (str - ptr <= 1)
                              str++;
                            if (isdigit(*str))
                              str++;
                            found = 1;
                        }
                        break;
                      case 'b': case 'w':
                        if (*str == 'o' || *str == 'u') {
                            str++;
                            found = 1;
                        }
                        break;
                      case 'c': case 'r': case 's': case 'z':
                        if (*str == 'i') {
                            str++;
                            found = 1;
                        }
                        break;
                      case 'f': case 'm': case 'p':
                        if (*str == 'u') {
                            str++;
                            found = 1;
                        }
                        break;
                      case 'y':
                        if (*str == 'e') {
                            str++;
                            found = 1;
                        }
                        break;
                      case 'n':
                        if (*str == 'g') {
                            str++;
                            c = *str;
                            if (c && isvowel(c)) {
                                switch(c) {
                                  case 'e':
                                    if (!*(str+1) || *(str+1) != 'r')
                                      str--;
                                    found = 1;
                                    break;
                                  default:
                                    if (c != 'a')
                                      str--;
                                    found = 1;
                                    break;
                                }
                            } else
                              found = 1;
                        } else if (!isvowel(*str) && !isdigit(*str))
                          found = 1;
                        else if (isvowel(*str) && str - ptr > 1) {
                            found = 1;
                        }
                        break;
                    }
                    if (!found && isvowel(c) &&
                        !isvowel(*str) && *str != 'n') {
                        if (isdigit(*str))
                          str++;
                        found = 1;
                    }
                }
            }
        }
        if ( !found ) str--;  /* probably on end of string */
        found = 0;
        len = str - ptr;
        if ( out == NULL ) {
            out = (char **)malloc(INITIAL_ARRAY_SIZE * sizeof(char *));
            arr_size = INITIAL_ARRAY_SIZE;
        } else if ( i == arr_size ) {
            arr_size += ARRAY_INC;
            out = (char **) realloc(out, arr_size * sizeof(char *));
        }
        out[i] = (char *)malloc(len+1);
        strncpy(out[i], ptr, len);
        *(out[i]+len) = '\0';
        i++;
        ptr = str;
    }
    *n = i;
    return(out);
}

main(argc, argv)
int argc;
char **argv;
{
    char *str = argv[1], **pinyin = NULL;
    char buff[50];
    int i, n;

    do {
        scanf("%[^\n]\n", buff);
        pinyin = parse_pinyin(buff, &n);
        for ( i = 0; i < n; i++ )
          printf("%s ", pinyin[i]);
        putchar(10);
        free(pinyin);
        pinyin = NULL;
    } while (!feof(stdin));
}