My code is a follows (this code 100% works, it just doesn't use recursive descent)
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
public class FileParser {
private static Scanner scan;
private static String getLabel(String labelFile) {
int i;
labelFile.trim();
for (i = 0; i < labelFile.length(); i++) {
char charVal = labelFile.charAt(i);
boolean bool = Character.isLetter(charVal);
if (!bool) {
break;
}
}
return labelFile.substring(0, i);
}
public static void main(String[] args) {
try {
File inputFile = new File("X:/Path/To/File/Input.txt");
String mainStr, mainLabel;
scan = new Scanner(inputFile);
if (scan.hasNextLine()) {
mainStr = scan.nextLine().trim();
mainLabel = getLabel(mainStr);
if (!mainLabel.equalsIgnoreCase("Window")) {
System.out.println("Incorrect Input file should start with WINDOW");
return;
}
mainStr = mainStr.substring(mainLabel.length()).trim();
JFrame mainFrame = (JFrame) addCompnt(mainStr, mainLabel);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setVisible(true);
} else {
System.out.println("Error");
}
} catch (FileNotFoundException fnfe) {
System.out.println("File not found");
fnfe.toString();
} catch (Exception unk) {
System.out.println("Unknown Error");
unk.toString();
}
}
private static ArrayList<Integer> getArray(String arrayStr) throws Exception {
int x, y;
ArrayList<Integer> arrayInt = new ArrayList<Integer>();
for (x = 0; x < arrayStr.length(); x++) {
for (y = x; y < arrayStr.length() && Character.isDigit(arrayStr.charAt(y)); y++);
if (x != y) {
arrayInt.add(Integer.parseInt(arrayStr.substring(x, y)));
}
x = y;
}
return arrayInt;
}
private static Component addCompnt(String compntStr, String compntLabel) throws Exception {
String inputStr;
if (compntLabel.equalsIgnoreCase("Window")) {
compntStr = compntStr.trim();
JFrame wndwFrame;
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('"'));
wndwFrame = new JFrame(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
} else {
wndwFrame = new JFrame("Missing Window Title");
}
if (compntStr.charAt(0) == '(') {
inputStr = compntStr.substring(0, compntStr.indexOf(')') + 1);
compntStr = compntStr.substring(inputStr.length()).trim();
ArrayList<Integer> intStr = getArray(inputStr);
if (intStr.size() == 2) {
wndwFrame.setSize(intStr.get(0), intStr.get(1));
}
}
inputStr = getLabel(compntStr);
compntStr = compntStr.substring(inputStr.length()).trim();
JPanel loPanel = new JPanel();
if (inputStr.equalsIgnoreCase("Layout")) {
inputStr = getLabel(compntStr);
compntStr = compntStr.substring(inputStr.length()).trim();
if (inputStr.equalsIgnoreCase("Flow")) {
FlowLayout fLayout = new FlowLayout();
loPanel.setLayout(fLayout);
}
if (inputStr.equalsIgnoreCase("Grid")) {
if (compntStr.charAt(0) == '(') {
inputStr = compntStr.substring(0, compntStr.indexOf(')') + 1);
compntStr = compntStr.substring(inputStr.length()).trim();
ArrayList<Integer> intStr = getArray(inputStr);
GridLayout gLayout;
if (intStr.size() == 2) {
gLayout = new GridLayout(intStr.get(0), intStr.get(1));
loPanel.setLayout(gLayout);
} else if (intStr.size() == 4) {
gLayout = new GridLayout(intStr.get(0), intStr.get(1), intStr.get(2), intStr.get(3));
loPanel.setLayout(gLayout);
}
}
}
}
while (true) {
if (scan.hasNextLine()) {
compntStr = scan.nextLine().trim();
inputStr = getLabel(compntStr);
if (inputStr.equalsIgnoreCase("end")) {
break;
} else {
Component nestCompnt = addCompnt(compntStr.substring(inputStr.length()), inputStr);
if (nestCompnt != null) {
if (nestCompnt.getClass() == wndwFrame.getClass()) {
System.out.println("Window can't be nested inside");
} else {
loPanel.add(nestCompnt);
}
}
}
} else {
break;
}
}
wndwFrame.add(loPanel);
return wndwFrame;
}
if (compntLabel.equalsIgnoreCase("Button")) {
compntStr = compntStr.trim();
JButton widgetBttn;
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('\"'));
widgetBttn = new JButton(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
} else {
widgetBttn = new JButton("Missing Button Label");
}
return widgetBttn;
}
if (compntLabel.equalsIgnoreCase("Group")) {
compntStr = compntStr.trim();
JRadioButton grpBttn;
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('\"'));
grpBttn = new JRadioButton(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
} else {
grpBttn = new JRadioButton("Missing Group Label");
}
return grpBttn;
}
if (compntLabel.equalsIgnoreCase("Label")) {
compntStr = compntStr.trim();
JLabel widgetLabel;
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('\"'));
widgetLabel = new JLabel(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
} else {
widgetLabel = new JLabel("Missing Label");
}
return widgetLabel;
}
if (compntLabel.equalsIgnoreCase("Panel")) {
compntStr = compntStr.trim();
JPanel compntPnl = new JPanel();
inputStr = getLabel(compntStr);
compntStr = compntStr.substring(inputStr.length()).trim();
if (inputStr.equalsIgnoreCase("Layout")) {
inputStr = getLabel(compntStr);
compntStr = compntStr.substring(inputStr.length()).trim();
if (inputStr.equalsIgnoreCase("Flow")) {
FlowLayout panelFlow = new FlowLayout();
compntPnl.setLayout(panelFlow);
}
if (inputStr.equalsIgnoreCase("Grid")) {
if (compntStr.charAt(0) == '(') {
inputStr = compntStr.substring(0, compntStr.indexOf(')') + 1);
compntStr = compntStr.substring(inputStr.length()).trim();
ArrayList<Integer> gridStrg = getArray(inputStr);
GridLayout gLayout;
if (gridStrg.size() == 2) {
gLayout = new GridLayout(gridStrg.get(0), gridStrg.get(1));
compntPnl.setLayout(gLayout);
} else if (gridStrg.size() == 4) {
gLayout = new GridLayout(gridStrg.get(0), gridStrg.get(1), gridStrg.get(2),
gridStrg.get(3));
compntPnl.setLayout(gLayout);
}
}
}
}
while (true) {
if (scan.hasNextLine()) {
compntStr = scan.nextLine().trim();
inputStr = getLabel(compntStr);
if (inputStr.equalsIgnoreCase("End")) {
break;
} else {
Component errCompnt = addCompnt(compntStr.substring(inputStr.length()), inputStr);
if (errCompnt != null) {
if (errCompnt.getClass() == new JFrame().getClass()) {
System.out.println("Panel Layout Error");
} else {
compntPnl.add(errCompnt);
}
}
}
} else {
System.out.println("Error in Panel Layout Nesting");
break;
}
}
return compntPnl;
}
if (compntLabel.equalsIgnoreCase("Textfield")) {
compntStr = compntStr.trim();
ArrayList<Integer> textStr = getArray(compntStr);
JTextField txtFld = new JTextField(textStr.get(0));
return txtFld;
}
if (compntLabel.equalsIgnoreCase("Radio")) {
compntStr = compntStr.trim();
JRadioButton rdoBttn;
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('\"'));
rdoBttn = new JRadioButton(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
} else {
rdoBttn = new JRadioButton("Missing Radio Label");
}
return rdoBttn;
}
return null;
}
}
As stated above, this code works, it doesn't use recursive descent. How can I accomplish that?
Input must follow this form:

Blue text are tokens (title cases are keywords) Red non-terminal Black punctuation are BNF meta symbols
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 produce this:

Important to note, this is not a calculator!
I have been trying to implement switch/case for this but I am struggling. Most of the widgets follow the same process.
if (compntLabel.equalsIgnoreCase("Button")) {
compntStr = compntStr.trim();
JButton widgetBttn;
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('\"'));
widgetBttn = new JButton(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
} else {
widgetBttn = new JButton("Missing widget Label");
}
return widgetBttn;
For my switch case statement I was trying to do something like this:
private static String inputToken(String s) {
JButton widgetBttn;
String compntStr; //if only this worked
switch (s.toUpperCase()) {
case "Button":
case "Group":
case "Label":
case "Panel":
compntStr = compntStr.trim();
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('\"'));
widgetBttn = new JButton(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
break;
case "Textfield":
if (compntLabel.equalsIgnoreCase("Textfield")) {// widget ::= Textfield NUMBER ';'
compntStr = compntStr.trim();
ArrayList<Integer> textStr = getArray(compntStr);
JTextField txtFld = new JTextField(textStr.get(0));
break;
case "Radio":
JRadioButton rdoBttn;
if (compntStr.charAt(0) == '"') {
compntStr = compntStr.substring(1);
inputStr = compntStr.substring(0, compntStr.indexOf('\"'));
rdoBttn = new JRadioButton(inputStr);
compntStr = compntStr.substring(compntStr.indexOf('"') + 1).trim();
break;
default:
System.out.println("Missing widget label");
break;
}
return widgetBttn;
}
}
But I also know this doesn't work.
Parsing is not only about reading a correct input successfully, but also finding syntax errors in incorrect input. I did not examine your code closely, but I don't see a stack. There's some wonderful computer science that says you can't correctly parse the grammar you've been given without one (because a
Panelis awidgetthat includes otherwidgets, which may bePanels and yada yada...). It's mathematically impossible. RD parsers use the runtime stack, but it's also possible to use an explicit one in a slightly different coding of the parser, but then it's not RD.From an LL(1) grammar, a recursive descent parser is almost a direct translation. Happily you have been given an LL(1) grammar!
By far the cleanest organization for a parser like this is to separate the task of scanning from parsing. Scanning is transforming the input into a stream of "tokens." For your language, the following is usable:
With this, a reasonable scanner interface looks like this:
The start of the test input you described (which incidentally has a syntax error; the final semi needs to be a dot instead), looks like this:
With a working scanner, you can forget about all the details of identifying tokens. That makes the parser a much simpler beast. A reasonable interface looks like this:
Here are some translations of grammar rules to recursive descent methods to get the idea:
Using these as patterns, you should be able to work out the rest. The key idea is that each method is responsible for matching exactly the tokens that the corresponding grammar rule defines as correct.
Now this parser actually does nothing except verify correctness of the input by gobbling it and looking for "end of input." The next step in making it useful is to add "action" code that does what you need as each rule is matched. In your case, I guess you'll be positioning Swing widgets.
Enjoy this problem! Parsing languages with computer programs is fun and historic. The theory and practice of language parsing was one of the early great successes of computer science.