Java - how to implement application console parser

2.1k Views Asked by At

So I'm trying to write an application that has an in-program console interface (I.e. you don't have to run the program from the console with a different command each time), and I want to have a parser object that parses commands/options that come in from the user. The structure is similar to this -

ArrayList<String> example = new ArrayList<>(); 

/* PARSING */
ConsoleParser parser = new ConsoleParser();

Scanner input = new Scanner(System.in);
String parserArgs = input.nextLine(); 

while (parserArgs != "quit") 
{
     execute(parser.parse(parserArgs));
     parserArgs = input.nextLine();
}

So the idea is to have a console (within the application), where I can type commands like 'add x' or 'contains x' which would then be assigned to 'parserArgs.' Then the command string would be passed to the ConsoleParser where it would be dissected and searched for valid commands. If the command is valid (and has necessary options/arguments), the parse() method of ConsoleParser would somehow return the method (or name of method) to main, along with any arguments that method needs. So if I want to add the string "foo" to my ArrayList, then at the console I could type 'add foo' and that would be passed to the parser, which would then return to main some kind of instruction that the add() method of ArrayList needs to be called on 'example' with the argument 'foo.' I Know this could be easily done with an arraylist, but I just use it here for simplicity.

1

There are 1 best solutions below

2
On BEST ANSWER

From your question I'm not sure if you want a simple solution or an elegant one. Here is a sketch how an elegant solution could look like.

You define an Functional Interface, i.e. a Interface that has only one method, that's returned by your Parser.

like:

// I the Input type
// R the result type
public interface Fn<I,R> {

  R apply (I input);
}

And a second one with the input that only provides an execute Method

public interface Cmd<R> {

  R execute ();
}  

You define your Console Commands

public class CliCommand<A> {
  // constructor 
  public CliCommand(final String name, final Fn<String, A> parseFunct) 
  {
           // TODO
  }
} 

   // the parse function 
  public A parse(String arg) throws CliParseException {
      // TODO
  }
}

The Add Command

 public class Add implements Cmd<Integer> { 
    public Add(Integer i) {
    }

 }

And the Parse Function for the Add

 public class ParseAdd implements Fn<String, Cmd<Integer>> { 
     public Cmd<Integer> apply(String option) {
       // parse the input and return the command with the arguments in there 
       // if (option == "add"  and args exist )
       // try parse args
       // i = Integer.parse(substring);

       return new Add(i); 
     }
 }

Then the ConsoleParser

public class ConsoleParser<A> {
  public static <A> ConsoleParser<A> cli(CliCommand <A> command) {
    ...
  }
  public ConsoleParser <A> or (CliCommand <A> command) {
    ...
  }

  public A parse(String arg) throws CliParseException {
     // 
  }

}

After that your Program can be written like

ConsoleParser<Cmd<Object>> parser = cli("add",new ParseAdd()) 
   .or(...)
   .or("quit", new ParseQuit();

Scanner input = new Scanner(System.in);
String parserArgs = input.nextLine(); 

while (true) 
{
    try {
       parser.parse(parserArgs).execute();
    } catch (CliParseException e) {
       // handle it somehow
    }
    parserArgs = input.nextLine();   
}

In this example the Add is too simple, I think you actually want to add String to a list or two Numbers so the actual ParseAdd Method needs some context (like an already existing List) as in my simple example.