/*
 *
 *  cdcl.class - an Java (JDK 1.1) applet to de-mystify C declaration  
 *
 *  RCS:
 *      $Revision$
 *      $Date$
 *
 *  Description:
 *
 *      this is a java applet to unscramble C declarations.
 *      The code to parse C declaration is translated from the C code
 *      described in K&R, 2nd edition, page 123.
 *
 *  Parameters
 *     name        value       description
 *
 *      none
 *
 *  Limitations and Comments:
 *
 *      This applet can not handle declarations with function argument 
 *      types or qualifiers like const,volatile etc.
 *
 *
 *  Development History:
 *      who                  when           why
 *      MA_Muquit@fccc.edu   Aug-03-1996    first cut
 */

import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;

public class cdcl extends Applet    
{
    // consts
    final static char    NAME        ='1';
    final static char    PARENS      ='2';
    final static char    BRACKETS    ='3';
    final static char    EOF         ='\033';
    final static boolean gdebug=false;

    char
        tokentype=NAME;                 // last token type

    String 
        token,                          // last token string
        name,                           // identifier name
        datatype,                       // data type=char,int, etc.
        out;                            // output string
        
    int
        gi=-1;                          // index for getchar()

    String
        istr;                           // c declation

    int
        length;                         // length of the input string

    // GUI stuff
    TextField
        textf;

    TextArea
        texta;

    public boolean
        error_found=false;

    public void init()
    {
        Font
            font;

        font=new Font("Courier",Font.PLAIN,12);
       
        GridBagLayout gridbag=new GridBagLayout();
        this.setLayout(gridbag);
        
        // create the label
        Label lab=new Label("Declaration:");
        constrain(this,lab,
            0,0,1,1,
            GridBagConstraints.BOTH,
            GridBagConstraints.CENTER,
            0.0, 0.0, 0, 0, 0, 0);

        // create the text field
        textf=new TextField(50);
        constrain(this,textf,
            1,0,4,1,
            GridBagConstraints.BOTH,
            GridBagConstraints.CENTER,
            0.0, 0.0, 0, 0, 0, 0);

        textf.setFont(font);
        // create text area
        texta=new TextArea("");
        constrain(this,texta,
            0,1,5,1,
            GridBagConstraints.BOTH,
            GridBagConstraints.CENTER, 
            1.0, 1.0, 2, 2, 2, 2);
        texta.setFont(font);
        texta.setForeground(Color.blue);
        texta.setEditable(false);

    }

    // respond to user actions
    public boolean action(Event evt,Object arg)
    {
        if (evt.target == textf)
        {
            String text=textf.getText();
            if (text.length() > 0)
            {
                Unscramble(text);
                return (true);
            }
        }

        return (true);
    }

    void Unscramble(String str)
    {
        String
            result;
        /*
        ** initialize
        */
        gi=-1; token=""; out=""; name=""; datatype=""; result="";
        istr="" + str;

        length=istr.length();
        Debug("string=" + istr);
        Debug ("length=" + length);

        gettoken();
        datatype += token;
        dcl();
        if (error_found == false)
        {
            result="" + name + " is" + out + datatype;
            SetText(result);
        }
    }

    // getchar() method
    // it just walks through a string return the character until EOF is 
    // reached, EOF is denoted by 033
    // depends on global var: istr,gi and length
    
    public char getchar()
    {
        if (gi >= (length-1))
            return (EOF);

        return (istr.charAt(++gi));
    }


    public void ungetchar()
    {
        if (gi >= 0)
        gi--;
    }


    // method returns the next token
    public char gettoken()
    {
        char
            c;

        token="";

        while ((c=getchar()) == ' ' || c == '\t')
            ;

        if (c == '(')
        {
            SetErrorFlag(false);
            if ((c=getchar()) == ')')
            {
                token="()";
                tokentype=PARENS;
            }
            else
            {
                ungetchar();
                tokentype='(';
            }
        }
        else if (c == '[')
        {
            SetErrorFlag(false);
            while ((c=getchar()) != ']')
            {
                if (c == EOF)
                {
                    SetErrorFlag(true);
                    SetText("Error: no maching ] found");
                    return(c);
                }
                token += c;
            }
            if (c == EOF)
                tokentype=EOF;
            else
                tokentype=BRACKETS;
        }
        else if (isalpha(c) == true)
        {
            SetErrorFlag(false);
            token= "" + c;
            while (isalnum((c=getchar())) == true)
            {
                if (c == EOF)
                {
                    SetErrorFlag(true);
                    SetText("Error: No alpha numeric characters found");
                    break;
                }
                token += c;
            }
            ungetchar();
            tokentype=NAME;
        }
        else
        {
            tokentype=c;
        }
        return (tokentype);
    }

    //
    public void SetErrorFlag(boolean flg)
    {
        if (flg == true)
        {
            error_found=true;
            texta.setForeground(Color.red);
        }
        else
        {
            error_found=false;
            texta.setForeground(Color.blue);
        }
    }


    // set text to text area
    public void SetText(String str)
    {
        texta.setText(str);
    }

    // parse a declarator
    public void dcl()
    {
        int
            ns;
        for (ns=0; gettoken() == '*'; )
            ns++;

        dirdcl();
        while (ns-- > 0)
            out += " pointer to ";
    }


    // parse a direct declarator
    public void dirdcl()
    {
        char
            type;

        SetErrorFlag(false);
        if (tokentype == '(')
        {
            dcl();
            if (tokentype != ')')
            {
                SetErrorFlag(true);
                SetText("Error: Missing )");
                return;
            }
        }
        else if (tokentype == NAME)
        {
            name = "" + token;
        }
        else
        {
            SetErrorFlag(true);
            SetText("Error: expected name or (dcl)");
            return;
        }

        while ((type=gettoken()) == PARENS || type == BRACKETS)
        {
            if (type == EOF)
            {
                SetErrorFlag(true);
                SetText("Error: failed to parse");
                return;
            }
            if (type == PARENS)
            {
                out += " function returning ";
            }
            else
            {
                if (token.length() > 1)
                {
                    out += "" + " array[0..";
                    int a=Integer.parseInt(token)-1;
                    out += a + "]";
                    out += " of ";
                }
                else
                {
                    out += " array of ";
                }
            }
        }
    }


    // check if the character is an alphabet
    public boolean isalpha(char c)
    {
        if (((c >= 'A') && (c <= 'Z'))  ||
            ((c >= 'a') && (c <= 'z')))
        {
            return (true);
        }
        return (false);
    }


    // check is the character is alpha numeric
    // [a-zA-Z0-9]
    public boolean isalnum(char c)
    {
        if (((c >= '0') && (c <= 9))    ||
            ((c >= 'A') && (c <= 'Z'))  ||
            ((c >= 'a') && (c <= 'z')))
        {
            return (true);
        }

        return (false);
    }

    // print Debug info
    public void Debug(String str)
    {
        if (gdebug == true)
        {
            System.out.println(str);
        }
    }

      /*
      ** taken from O'Reilly's Java in a Nutshell book
      ** makes laying out components easier. Java's GUI is horrible IMHO
      ** I code mainly in X/Motif. I also coded in M$ platform, but never
      ** saw anything as ugly as java's GUI methods. Sun better do something
      ** about it.
      */
      public void constrain(Container container, Component component,
                int grid_x, int grid_y,
                int grid_width, int grid_height,
                int fill, int anchor, double weight_x, double weight_y,
                int top, int left, int bottom, int right)
      {
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = grid_x; c.gridy = grid_y;
        c.gridwidth = grid_width; c.gridheight = grid_height;
        c.fill = fill; c.anchor = anchor;
        c.weightx = weight_x; c.weighty = weight_y;
        if (top+bottom+left+right > 0)
          c.insets = new Insets(top, left, bottom, right);

        ((GridBagLayout)container.getLayout()).setConstraints(component, c);
        container.add(component);
      }


}

