phimuemue
phimuemue

Reputation: 35983

OCaml: Pattern matching on strings of variable length

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?

Not-so-nice alternatives already considered:

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

Answers (2)

Jeffrey Scofield
Jeffrey Scofield

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

Marth
Marth

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

Related Questions