Sindbad~EG File Manager

Current Path : /usr/home/beeson/public_html/dynamicgeometry/org/dynamicgeometry/diagrammer/
Upload File :
Current File : /usr/home/beeson/public_html/dynamicgeometry/org/dynamicgeometry/diagrammer/Parser.java

/**
 *	CS 240
 *	Author: Nhan Vu (and some parts by Thang Dao)
 *  12.19.06 M. Beeson made constructor and parse() public
 *  12.20.06 Beeson eliminated private copy of isPrimitiveCommand and made it handle final semicolon.
 *  12.22.06 Beeson changed SymbolTable.IsPrimitiveCommand to Scripts.IsPrimitiveCommand
 *  1.10.07  Beeson removed hard-coded list of scripts in favor of Euclid--then put them back
 */

package org.dynamicgeometry.diagrammer;

import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.ArrayList;
import org.dynamicgeometry.scripts.*;

public class Parser extends Object {
    private Hashtable<String, ArrayList<String>> commandNameToArgumentList;
    private String errorMsg;
    private int errorColumn;

    public Parser() {
        //commandNameToArgumentList = new Hashtable<String, ArrayList<String>>();
        initHashTable();
    }

    //For testing
    Parser(Hashtable<String, ArrayList<String>> h) {
		commandNameToArgumentList = h;
	}

	/**************************************************************************
	This shall parse the line in the forms of " Label = CommandName ( Arg ,
	MoreArg ) " or " CommandName ( Arg , MoreArg ) ", where Label shall begin
	with exactly one upper/lowercase letter followed by 0 or more digits
		CommandName shall be
		Arg shall be 1 or more comma separated argument list
		' ' shall be 0 or more tabs or spaces
	and return null if the parse was unsuccessful, or if the argument list does
	not match the CommandName looked up from the hash table; otherwise return a
	Command object. If Label is missing, create Command object with null Label.
	Also accepts one semicolon at the end.
	**************************************************************************/
    public Command parse( String line ) {
		Command cmd = null;

        if (line == null || line.length() == 0) {
			setErrorMsg( "Parsed string is null or empty" );
            return null;
        }
        int i = line.indexOf("=");
        if (i == 0 || i == (line.length() - 1)) {
			setErrorMsg( "Incorrect location of equal sign" );
            return null;
        }

        if (i != -1) {//this command has label
            String label = line.substring(0, i).trim();

            if (!isValidLabel(label)) {
				{ setErrorMsg("Parser encountered invalid label");
				  return null;
				}
			}
            cmd = parseNoLabel(line.substring(i + 1));
            if (cmd != null) {
				cmd.setLabel(label);
			}
        }
        else
            cmd = parseNoLabel(line);

        return cmd;
    }

	Command parseNoLabel(String line) {
		Command cmd = null;
        String parsedLine;
        String x = line.trim();
        if(x.charAt(x.length()-1) == ';')
           parsedLine = x.substring(0,x.length()-1);
        else
           parsedLine = x;
        int i, j, k;
        int balance;

        if (parsedLine.length() == 0) {
            setErrorMsg( "Parsed string is null or empty" );
            return null;
        }
        if(parsedLine.startsWith("return")) {  // added by Beeson
          // it's OK to have no parenthesis, but only a variable or an and-delimited list of variables can follow "return".
          // No parentheses can come after "return".
          int count = 0;
          String[] parts = parsedLine.split(" +");
          for(i=1;i<parts.length;i++)
              { if(parts[i].equals("and"))
                   continue;
                if(!isValidLabel(parts[i]))
                   { setErrorMsg("Only variables can follow return, not " + parts[i]);
                     return null;
                   }
                ++count;
              }
          String[] args = new String[count];
          count = 0;
          for(i=1;i<parts.length;i++)
              { if(parts[i].equals("and"))
                    continue;
                args[count] = parts[i];
                ++count;
              }
          return new Command(null, "return", args);
        }

        //Step 1: Check parentheses in general
        balance = 0;
        for (i = 0; i < parsedLine.length(); i++) {
            if (parsedLine.charAt(i) == '(')
                balance++;
            if (parsedLine.charAt(i) == ')')
                balance--;
            if (balance < 0) {
                setErrorMsg ("Parentheses are not properly nested");
                return null;
            }
        }
        if (balance != 0) {
            setErrorMsg ("Parentheses are not properly nested");
            return null;
        }

        //Step 2: Find the argument list and command name
        i = parsedLine.indexOf("(");
        j = parsedLine.lastIndexOf(")");

        if (i == -1 || j != (parsedLine.length() - 1)) {//no '(' or ')' is not at the end
            setErrorMsg ("Parsed string has incorrect format");
            return null;
        }
        String commandName = parsedLine.substring(0, i).trim();

        if (!isValidCommandName(commandName)) {
			setErrorMsg ("Command name has incorrect format");
			return null;
		}

        //Parse argument list
        String argumentList = parsedLine.substring(i + 1, j).trim();
        i = 0;//i is starting point
        j = 0;//j is ending point
        k = 0;//k is number of arguments
        balance = 0;//balance = number of '(' - number of ')' so far
        //# of arguments cannot exceed argumentList.length()
        String arguments[] = new String[argumentList.length()];
        while (j < argumentList.length()) {
            if (argumentList.charAt(j) == ',') {
                if (balance == 0) {
                    arguments[k] = argumentList.substring(i, j).trim();
                    k++;
                    i = j + 1;
                    j = i;
                }
                else
                    j++;
            }
            else {
                if (argumentList.charAt(j) == '(')
                    balance++;
                else if (argumentList.charAt(j) == ')')
                    balance--;
                j++;
            }
        }
        //The last token
        arguments[k] = argumentList.substring(i, j).trim();
        k++;
 		String[] correctArgs = new String[k];

		//Check if arguments are valid
		for (i = 0; i < k; i++) {
			if (!isValidArgument(arguments[i])) {
				setErrorMsg( "One argument has incorrect format" );
				errorColumn = i + 1;
				return null;
			}
			else correctArgs[i] = arguments[i];
		}

        //* After commandNameToArgumentList is properly hard-coded, un-comment
        // this block
        if( !Scripts.isPrimitiveCommand( commandName ) ) {
			if (commandNameToArgumentList.get(commandName) == null) {
				setErrorMsg("Command name not found in Hashtable");
				return null;
			}

			if (k != commandNameToArgumentList.get(commandName).size()) {
				setErrorMsg("Number of arguments in argument list is incorrect");
				return null;
			}
        	//*/
		}

		//At this point, everything is OK
		//for( int z=0; z<correctArgs.length; z++ ) System.out.println( "		" + correctArgs[z] );
		cmd = new Command ( null, commandName, correctArgs );

		return cmd;
	}

	private boolean isValidCommandName( String cmdName ) {
		return true;
	}


	/**
	 *	Added by Thang Dao (with Nhan Vu's permission)
	 *  Check validity of label. label is valid if it starts with letter followed by 0 or more digits
	 *  Return true if format of label is valid, otherwise return false.
	 *  @param label		check validity of this label
	 *  @return true		label is in valid format
	 *	 	   false	label is in invalid format
	 */
    private boolean isValidLabel( String label ) {
		/* first character is not letter */
        if( !Character.isLetter( label.charAt(0) )) {
        	setErrorMsg( "First character of Label is not an alphabet" );
        	return false;
		}

		/* if any character from position 1+ is not digit, return false */
        for( int i = 1; i < label.length(); i++ )
            if( !Character.isDigit( label.charAt(i) ) ) {
                setErrorMsg( "Label contains character different than digits after the 1st alphabet" );
                return false;
			}

        return true;
    }

	/**
	 * Check the validity of argument
	 * The format of argument is the same as that of label, so this method simply
	 * returns what is returned by isValidLabel
	 */
    private boolean isValidArgument( String arg ) {
		return isValidLabel(arg);
    }

	/**
	 * This method initializes the Hashtable commandNameToArgumentList
	 * It hard-codes the Hashtable using commands defined in class Scripts
	 */
    private void initHashTable() {
		commandNameToArgumentList = new Hashtable<String, ArrayList<String>>();
		//for(String u : Scripts.Euclid)
		 //  addOneCommand(u);
		addOneCommand("EquilateralTriangle");
		addOneCommand("CopySegment");
		addOneCommand("CopySegmentOntoLine");
		addOneCommand("CopySegmentOntoLine2");
		addOneCommand("BisectAngle");
		addOneCommand("BisectAngle2");
		addOneCommand("BisectSegment");
		addOneCommand("BisectSegment2");
		addOneCommand("ErectPerpendicular");
		addOneCommand("DropPerpendicular");
		addOneCommand("TriangleFromSegments");
		addOneCommand("CircleFromCenterAndRadius");
		//addOneCommand("CopyTriangle");
		addOneCommand("CopyAngleToSegment");
		addOneCommand("CopyAngleToSegment2");
		addOneCommand("ParallelThroughPoint");
		addOneCommand("ParallelogramEqualToTriangle");
		//addOneCommand("ParallelogramOfGivenWidthEqualToTriangle");
		//addOneCommand("Parallelogram2OfGivenWidthEqualToTriangle");
		//addOneCommand("ParallelogramEqualToConvexPolygon");
		//addOneCommand("SquareOnGivenSide");
		//addOneCommand("SquareOnGivenSide2");
		//addOneCommand("CutSegmentInGoldenSection");
		//addOneCommand("SquareEqualToConvexPolygon");
		//addOneCommand("FindCenterOfCircle");
		//addOneCommand("TangentToCircle");
		//addOneCommand("CircleFromArc");
		//addOneCommand("BisectCircumference");
		//addOneCommand("GivenSegmentAndAngleConstructCircle");
		//addOneCommand("GivenCircleAndAngleConstructArc");
		//addOneCommand("FitSegmentInCircle");
		//addOneCommand("TriangleInCircle");
		//addOneCommand("TriangleAroundCircle");
		//addOneCommand("CircleInTriangle");
		//addOneCommand("CircleAroundTriangle");
		//addOneCommand("SquareInCircle");
		//addOneCommand("SquareAroundCircle");
		//addOneCommand("CircleInSquare");
		//addOneCommand("CircleAroundSquare");
		//addOneCommand("ConstructGoldenTriangle");
		//addOneCommand("PentagonInCircle");
		//addOneCommand("PentagonAroundCircle");
		//addOneCommand("CircleInPentagon");
		//addOneCommand("CircleAroundPentagon");
		//addOneCommand("HexagonInCircle");
		//addOneCommand("FifteenGonInCircle");
		//addOneCommand("DivideSegmentIntoParts");
		//addOneCommand("SegmentOnLine");
		//addOneCommand("ThirdProportional");
		//addOneCommand("FourthProportional");
		//addOneCommand("MeanProportional");
		//addOneCommand("SimilarConvexPolygonWithGivenSide");
	}

	private void addOneCommand (String commandName) {
		try {
			ArrayList<String> list = new ArrayList<String>();
			String[] parameterNames = Scripts.getParameterNames( "org.dynamicgeometry.scripts." + commandName);
			for (int i = 0; i < parameterNames.length; i++)
				list.add(parameterNames[i]);
			commandNameToArgumentList.put(commandName, list);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

    private void setErrorMsg( String _errorMsg ) { errorMsg = _errorMsg; }
    private void setErrorColumn( int _errorColumn ) { errorColumn = _errorColumn; }
    public String getErrorMsg() { return errorMsg; }
    public int getErrorColumn() { return errorColumn; }

}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists