Reputation: 35983
I'm trying to write a program that accepts user input (one line at a time) and evaluates this user input in some ways.
I have a function getline : unit -> string
that reads a string from stdin and returns it. The commands have the following format:
#command1 arg1 arg2 arg3
#cmd2 arg1 arg2
#nextcommand arg1 arg2 arg3 arg4
#quit
#exit
Exemplary calls to the commands could then look as follows:
#command1 2 6 3
#cmd2 6 3
#nextcommand a b3 2 3
The main problem is the function evalcommand : string -> unit
that takes a whole line obtained by getline
. I started
let evalcommand command = match command with
| "#quit" | "#exit" -> () (* no problem; both length 4 and no arguments *)
| (* how to capture arguments properly? *)
How can I cope with commands whose lengths are differing (such as e.g. command1
and cmd2
) and that have additional arguments with them?
Using a parser (possibly generated by a parser generator): This was the first thing that came to my mind, but since I only want to support five or six commands, it seemed like an overkill to me.
Checking prefixes of different lengths: Of course, I could just check prefixes of different length of the command string, but that seems to involve lots of boilerplate code.
List representation of strings: I know that I could just transform each String
into a char list
and then do something like
let evalcommand command = match String.to_list with
| ['#';'e';'x';'i';'t'] -> () (* cumbersome for many commands *)
| '#'::'c'::'m'::'d'::'2'::args -> consume_args args (* cumbersome *)
| (* and so on *)
Mikmatch: I also had a look at Mikmatch, but I'd prefer doing it with OCaml's intrinsic features if possible.
Hashtable approach: Of course I can use a hashtable that maps String
s to String list -> unit
(i.e. functions taking a certain amount of strings as argument, returning ()
).
So here's the final question: Is there any elegant way that solves this poblem in a straightforward, easily maintainable way (preferrably with little amount of code)?
Upvotes: 1
Views: 1014
Reputation: 66818
I have written many simple command interpreters as Marth suggests. I think it's an elegant solution to get something going quickly for a simple command line syntax.
If your command line syntax is likely to become a little richer than what you show (if you might need quoted strings, say), you could write your interpreter using just ocamllex. Unless you get really fancy with nested delimiters, your command lines can probably be described by regular expressions.
Upvotes: 2
Reputation: 24802
Not the most elegant solution, but why not simply split on spaces ?
open Str;;
let split_on_spaces s = split (regexp " ") s;;
let evalcmd cmd = match (split_on_spaces cmd) with
| ["#quit"] | ["#exit"] -> ()
(* match on a known number on arguments *)
| ["#command";arg1;arg2;arg3] -> ()
(* unknown number *)
| "#cmd2" :: arg_list -> ()
Upvotes: 4