-2
\$\begingroup\$

I have written a program that parses this code but it doesn't use recursive descent. So I am starting over and need help.

Just so everyone is on the same page: Using recursive descent it needs to parse an input file and generate the GUI that it defines, using a supplied lexical analyzer. The grammar for this is defined below:

Grammer for this language

The red symbols are nonterminals, blue are tokens and punctuation is BNF meta symbols. UPPERCASE tokens are keywords. The character literals are punctuation tokens.

In the production for...

  • window the string is the name that is to appear on the top border of the window and the two numbers are the width and height of the window
  • layout_type that defines the grid layout, the first two numbers represent the number of rows and columns, and the optional next two the horizontal and vertical gaps
  • widget that defines a...
    • button, the string is the name of the button
    • label, the string is the text that is to be placed in the label
    • text field, the number is the width of the text field
  • radio_button, the string is the label of the button

Using recursive descent, I need to create a Java program that will parse the following input file

Window "Calculator" (200, 200) Layout Flow:
 Textfield 20;
 Panel Layout Grid(4, 3, 5, 5):
   Button "7";
   Button "8";
   Button "9";
   Button "4";
   Button "5";
   Button "6";
   Button "1";
   Button "2";
   Button "3";
   Label "";
   Button "0";
 End;
End;

The output should look like this:(this isn't a calculator the file just calls it that)

Output

OKAY, like I said I have written code that does this unfortunately it doesn't use recursive descent to do it. Right now what I have is looking bad so if anyone can help me out I would greatly appreciate it.

Currently getting an error at line 34 (comment next to it has the exact error)

package project99;

import java.io.*;


class Lexer {

    private static final int KEYWORDS = 11;
    private StreamTokenizer tokenizer;
    private String punctuation = ",:;.()";

    private Token[] punctuationTokens = { Token.COMMA, Token.COLON, Token.SEMICOLON, 
            Token.PERIOD, Token.LEFT_PAREN, Token.RIGHT_PAREN
            };

    public Lexer(String fileName) throws FileNotFoundException {
        tokenizer = new StreamTokenizer(new FileReader(fileName));
        tokenizer.ordinaryChar('.');
        tokenizer.quoteChar('"');
    }

    public Token getNextToken() throws SyntaxError, IOException {
        int token = tokenizer.nextToken();
        switch (token) {
        case StreamTokenizer.TT_NUMBER:
            return Token.NUMBER;            
        case StreamTokenizer.TT_WORD:
            for (Token aToken : Token.values()) {
                if (aToken.ordinal() == KEYWORDS)
                    break;              
                if (aToken.name().equals(tokenizer.sval.toLowerCase()))
                    return aToken;          
            } 
            throw new SyntaxError(lineNo(), "Invalid token" + getLexeme());//at project99.Lexer.getNextToken(Lexer.java:34)
        case StreamTokenizer.TT_EOF:
            return Token.EOF;
        case '"':
            return Token.STRING;
        default:
            for (int i = 0; i < punctuation.length(); i++)
                if (token == punctuation.charAt(i))
                    return punctuationTokens[i];
        }
        return Token.EOF;
    }

    public String getLexeme() {
        return tokenizer.sval;
    }

    public double getValue() {
        return tokenizer.nval;
    }

    public int lineNo() {
        return tokenizer.lineno();
    }
}

Currently getting error at line one (comment has exact error)

package project99;//project99.SyntaxError: Line: 1 Invalid token Window

class SyntaxError extends Exception {
    private static final long serialVersionUID = 1L;
    public SyntaxError(int line, String description) {
        super("Line: " + line + " " + description);
    }
}
enum Token {
    BUTTON, END, FLOW, GRID, GROUP, LABEL, LAYOUT, PANEL, RADIO, TEXTFIELD, WINDOW, COMMA, COLON, SEMICOLON, PERIOD,
    LEFT_PAREN, RIGHT_PAREN, STRING, NUMBER, EOF;
};

Currently getting errors at line 27 and 35 (comments next to the line for exact error)

package project99;

import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.io.*;
import javax.swing.*;



public class FileParser2 { 
    Token token; 
    Token currentLevel; 
    Lexer Lexer;
    String textGUI; 
    JFrame windowFrame; 
    JPanel panel; 
    ButtonGroup radioGroup;
    JFileChooser fileChooser; 
    JRadioButton radioButton;
     
    public FileParser2() { 
        try { 
            fileChooser = new JFileChooser("."); 
            if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
                Lexer = new Lexer(fileChooser.getSelectedFile().toString());
            } 
            token = Lexer.getNextToken();//at project99.FileParser2.<init>(FileParser2.java:27)
            this.parseGUI(); 
            } catch (SyntaxError | IOException e) {
                e.printStackTrace(); 
            } 
        }
     
    public static void main(String[] args) {
        new FileParser2();//at project99.FileParser2.main(FileParser2.java:35)
    }

    public boolean parseGUI() throws SyntaxError, IOException {
        int width;
        int height;

        if (token == Token.WINDOW) {
            currentLevel = Token.WINDOW;
            windowFrame = new JFrame();
            token = Lexer.getNextToken();
            if (token == Token.STRING) {
                windowFrame.setTitle(Lexer.getLexeme());
                token = Lexer.getNextToken();
                if (token == Token.LEFT_PAREN) {
                    token = Lexer.getNextToken();
                    if (token == Token.NUMBER) {
                        width = (int) Lexer.getValue();
                        token = Lexer.getNextToken();
                        if (token == Token.COMMA) {
                            token = Lexer.getNextToken();
                            if (token == Token.NUMBER) {
                                height = (int) Lexer.getValue();
                                token = Lexer.getNextToken();
                                if (token == Token.RIGHT_PAREN) {
                                    windowFrame.setSize(width, height);
                                    token = Lexer.getNextToken();
                                    if (this.getLayout()) {
                                        if (this.getWidgets()) {
                                            if (token == Token.END) {
                                                token = Lexer.getNextToken();
                                                if (token == Token.PERIOD) {
                                                    windowFrame.setVisible(true);
                                                    return true;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private boolean getLayout() throws SyntaxError, IOException {
        if (token == Token.LAYOUT) {
            token = Lexer.getNextToken();
            if (this.getLayoutType()) {
                if (token == Token.COLON) {
                    token = Lexer.getNextToken();
                    return true;
                }
            }
        }
        return false;
    }

    private boolean getLayoutType() throws SyntaxError, IOException {
        int rows;
        int columns;
        int columnGap;
        int rowGap;

        if (token == Token.FLOW) {
            if (currentLevel == Token.WINDOW) {
                windowFrame.setLayout(new FlowLayout());
            } else {
                panel.setLayout(new FlowLayout());
            }
            token = Lexer.getNextToken();
            return true;
        } else if (token == Token.GRID) {
            token = Lexer.getNextToken();
            if (token == Token.LEFT_PAREN) {
                token = Lexer.getNextToken();
                if (token == Token.NUMBER) {
                    rows = (int) Lexer.getValue();
                    token = Lexer.getNextToken();
                    if (token == Token.COMMA) {
                        token = Lexer.getNextToken();
                        if (token == Token.NUMBER) {
                            columns = (int) Lexer.getValue();
                            token = Lexer.getNextToken();
                            if (token == Token.RIGHT_PAREN) {
                                if (currentLevel == Token.WINDOW) {
                                    windowFrame.setLayout(new GridLayout(rows, columns));
                                } else {
                                    panel.setLayout(new GridLayout(rows, columns));
                                }
                                token = Lexer.getNextToken();
                                return true;
                            } else if (token == Token.COMMA) {
                                token = Lexer.getNextToken();
                                if (token == Token.NUMBER) {
                                    columnGap = (int) Lexer.getValue();
                                    token = Lexer.getNextToken();
                                    if (token == Token.COMMA) {
                                        token = Lexer.getNextToken();
                                        if (token == Token.NUMBER) {
                                            rowGap = (int) Lexer.getValue();
                                            token = Lexer.getNextToken();
                                            if (token == Token.RIGHT_PAREN) {
                                                if (currentLevel == Token.WINDOW) {
                                                    windowFrame.setLayout(
                                                            new GridLayout(rows, columns, columnGap, rowGap));
                                                } else {
                                                    panel.setLayout(new GridLayout(rows, columns, columnGap, rowGap));
                                                }
                                                token = Lexer.getNextToken();
                                                return true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private boolean getWidgets() throws SyntaxError, IOException {
        if (this.getWidget()) {
            if (this.getWidgets()) {
                return true;
            }
            return true;
        }
        return false;
    }

    private boolean getWidget() throws SyntaxError, IOException {
        int length;
        if (token == Token.BUTTON) {
            token = Lexer.getNextToken();
            if (token == Token.STRING) {
                textGUI = Lexer.getLexeme();
                token = Lexer.getNextToken();
                if (token == Token.SEMICOLON) {
                    if (currentLevel == Token.WINDOW) {
                        windowFrame.add(new JButton(textGUI));
                    } else {
                        panel.add(new JButton(textGUI));
                    }
                    token = Lexer.getNextToken();
                    return true;
                }
            }
        } else if (token == Token.GROUP) {
            radioGroup = new ButtonGroup();
            token = Lexer.getNextToken();
            if (getRadioButtons()) {
                if (token == Token.END) {
                    token = Lexer.getNextToken();
                    if (token == Token.SEMICOLON) {
                        token = Lexer.getNextToken();
                        return true;
                    }
                }
            }
        } else if (token == Token.LABEL) {
            token = Lexer.getNextToken();
            if (token == Token.STRING) {
                textGUI = Lexer.getLexeme();
                token = Lexer.getNextToken();
                if (token == Token.SEMICOLON) {
                    if (currentLevel == Token.WINDOW) {
                        windowFrame.add(new JLabel(textGUI));
                    } else {
                        panel.add(new JLabel(textGUI));
                    }
                    token = Lexer.getNextToken();
                    return true;
                }
            }
        } else if (token == Token.PANEL) {
            if (currentLevel == Token.WINDOW) {
                windowFrame.add(panel = new JPanel());
            } else {
                panel.add(panel = new JPanel());
            }
            currentLevel = Token.PANEL;
            token = Lexer.getNextToken();
            if (getLayout()) {
                if (getWidgets()) {
                    if (token == Token.END) {
                        token = Lexer.getNextToken();
                        if (token == Token.SEMICOLON) {
                            token = Lexer.getNextToken();
                            return true;
                        }
                    }
                }
            }
        } else if (token == Token.TEXTFIELD) {
            token = Lexer.getNextToken();
            if (token == Token.NUMBER) {
                length = (int) Lexer.getValue();
                token = Lexer.getNextToken();
                if (token == Token.SEMICOLON) {
                    if (currentLevel == Token.WINDOW) {
                        windowFrame.add(new JTextField(length));
                    } else {
                        panel.add(new JTextField(length));
                    }
                    token = Lexer.getNextToken();
                    return true;
                }
            }
        }
        return false;
    }

    private boolean getRadioButtons() throws SyntaxError, IOException {
        if (getRadioButton()) {
            if (getRadioButtons()) {
                return true;
            }
            return true;
        }
        return false;
    }

    private boolean getRadioButton() throws SyntaxError, IOException {
        if (token == Token.RADIO) {
            token = Lexer.getNextToken();
            if (token == Token.STRING) {
                textGUI = Lexer.getLexeme();
                token = Lexer.getNextToken();
                if (token == Token.SEMICOLON) {
                    radioButton = new JRadioButton(textGUI);
                    radioGroup.add(radioButton);
                    if (currentLevel == Token.WINDOW) {
                        windowFrame.add(radioButton);
                    } else {
                        panel.add(radioButton);
                    }
                    token = Lexer.getNextToken();
                    return true;
                }
            }
        }
        return false;
    }
}

Any help will be greatly appreciated!

\$\endgroup\$
4

1 Answer 1

2
\$\begingroup\$

The error occurs due to this section of code (which Java and you already pointed at):

case StreamTokenizer.TT_WORD:
  for (Token aToken : Token.values()) {
    if (aToken.ordinal() == KEYWORDS) break;              
    if (aToken.name().equals(tokenizer.sval.toLowerCase())) return aToken;          
  } 
  throw new SyntaxError(lineNo(), "Invalid token" + getLexeme());

Worth noting is that the constant KEYWORDS stores the value 11 with no further explanation, but from the comparison with the Token's ordinal I assume this is to assert no comma (Token with index 11) exists freely.

The comparison aToken.name().equals(tokenizer.sval.toLowerCase()) fails due to comparing a lowercased window in your test code with WINDOW, the token name, which of course isn't true. To fix this, simply set the token name to lowercase as well:

if (aToken.name().toLowerCase().equals(tokenizer.sval.toLowerCase()))
  return aToken; 

This seems to fix the issue, opening the test file yields no syntax errors.

On a more general note, I would greatly suggest splitting up your recursive descent into methods, which represent the grammatical rules. You could start like this:

private void newScan() throws SyntaxError, IOException {
    gui();
}

private void gui() throws SyntaxError, IOException{
    expectToken(Token.WINDOW);
    expectString();
    expectToken(Token.LEFT_PAREN);
    expectNumber();
    expectToken(Token.COMMA);
    expectNumber();
    expectToken(Token.RIGHT_PAREN);
    layout();
    widgets();
    expectToken(Token.END);
    expectToken(Token.PERIOD);
}

private void layout() throws SyntaxError, IOException {
    expectToken(Token.LAYOUT);
    layoutType();
    expectToken(Token.COLON);
}

// ...

private void expectToken(Token expectedToken) throws SyntaxError, IOException {
    if (token == Token.EOF && expectedToken != Token.EOF) {
        throw new SyntaxError("Unexpected end of file");
    }
    if (token != expectedToken) {
        throw new SyntaxError("Expected " + expectedToken + " but found " + token);
    }
    token = Lexer.getNextToken();
}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Great answer, but keep in mind we don't answer questions on Code Review where the code doesn't work as intended. Stack Overflow handles those question instead. \$\endgroup\$
    – pacmaninbw
    Apr 25, 2022 at 14:44
  • 2
    \$\begingroup\$ @pacmaninbw noted, thanks. \$\endgroup\$
    – hwgn
    Apr 25, 2022 at 15:15
  • \$\begingroup\$ @pacmaninbw thanks...honestly I think code review is where you should go to have your code reviewed and let others help you. It seems like StackOverflow doesn't want to help either. They told me to come here...so Thank you for your help... \$\endgroup\$ Apr 26, 2022 at 4:31
  • \$\begingroup\$ @CoreyHodges SO users often misguide people to this site, which is for working code only. While SO is for non-working code, they do require a minimal, complete, verifiable example (MCVE), so large dumps of entire, multi-hundred-line programs are generally off-topic there as well until they've seen more refinement/focus from the author. \$\endgroup\$
    – ggorlen
    Apr 27, 2022 at 0:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.