parse an user input

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Hi guys

Often I need to parse an user input from an UART and do something accordingly. eg print some internal memory info, print out some events etc...

here is how I do it, it's OK to just do a few commands, but I find it not very good if I want to do say 10 commands and 10 commands with params. It become very difficult to manage the code.
Code:
// buffer with user input 
uint8_t *buff;

// command only
if parse(buff, "command_1"){
  // do stuff
}

// command with params
if parse(buff, "command_2"){
  // some magical code to make buff point to after "command_1"
  if (parse(buff, "params_1"){
    // do stuff
  }
}
I am hoping someone can show me or point me to a better way to do this. Ideally, something like this is a lot easier to read and manage, but I am happy with any suggestions.
Code:
// buffer with user input 
uint8_t *buff;

// hopefully something like this??
void cli_parse("variable arguments", function _callback);

// then I can use this like this

// command only
cli_parse(buff, "cmd_1", cmd_1_callback);
// command with parameter
cli_parse(buff, "cmd_2", "param_2", cmd_2_callback);
 

MrChips

Joined Oct 2, 2009
30,802
What is the maximum number of parameters?
Are all parameters numeric?

When I have to parse a user input, I parse all commands the same way with the same same function regardless of the number of parameters.

The parameters are stored in an array and the parser function returns the number of parameters entered.
It is up to each function to choose how many of the input parameters are to be used.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
What is the maximum number of parameters?
Are all parameters numeric?

When I have to parse a user input, I parse all commands the same way with the same same function regardless of the number of parameters.

The parameters are stored in an array and the parser function returns the number of parameters entered.
It is up to each function to choose how many of the input parameters are to be used.
one command with 2 parameters are more than enough for me. For example:
[cmd], or
[cmd] [param_1], or
[cmd] [param_1] [param_2]

param_1 is usually string
param_2 is usually number
I am not quire sure I understand how you do it. So you parse the user input and store them in array, do you decode it, and store the code in an array instead of the actual user input?
When I have to parse a user input, I parse all commands the same way with the same same function regardless of the number of parameters.

The parameters are stored in an array and the parser function returns the number of parameters entered.
It is up to each function to choose how many of the input parameters are to be used.
 

djsfantasi

Joined Apr 11, 2010
9,163
I do something very similar when reading from a file.

The function reads one line from the input file. By one line, I mean all characters up to a 0x0D (a CR character). It passes parameters by address.

My input consists of 1 to 4 integer values.

I initialize a couple of local variables. One is a parameter count. The others are an integer array of 4 values. The function reads one character at a time from the file.

If it’s a digit, I multiply the value in the array by 10 and add the value of the integer read.

If it’s a comma or a CR, I increment the parameter count by 1. All other characters are just ignored. This latter point allows the file to be self documenting.

If a CR is encountered, the values in the array are returned to the memory locations specified in the call.

The input file can look like the following:
Code:
Size=2
Function=1,First Value=10,Second Value=20
Function=2,Only Value=99
Pseudo code looks like this:
Code:
ParseInput(*Size);

for (int I=0;I<Size;I++) {
    ParseInput(*Fcn,*Parm1,*Parm2);
    switch case(Fcn)
        case 1;
        {}
//...
 

MrChips

Joined Oct 2, 2009
30,802
[cmd]
[cmd] [param_1]
[cmd] [param_1] [param_2]

param_1 is usually string
param_2 is usually number
If this is your typical command structure, I would parse the command into three string variables, one variable each for:
[cmd]
[param_1]
[param_2]

The string will be NUL if there is no [param_2] or [param_1].

From there the [cmd] function can choose if it needs to convert the string param into number.
I would leave the input string untouched and copy the parsed parts into the new string arrays.
 

402DF855

Joined Feb 9, 2013
271
For really complex command processors I use a sequence used in compilers/interpreters: lexical analysis -> syntax analysis -> semantic analysis. For very simple command processors, the first step is to separate the components of the command string. You can decide if you want parameters separated by spaces, commas, both, semicolons, tabs, whatever. Since I prefer C++ I'll use that to give a skeleton in that, but the same sequence works in C, and I can elaborate if desired.
C:
BOOL ProcessUserInputLine(const CString &line)
{
      CStringArray elements;
      BreakUpLineBy( line, elements, " \t:;,"); // To simplify specify a space for separation.
      if (lines.GetCount()<1)
            return FALSE;
      CString command = elements[0];
      command.MakeUpper();
      if (command == "POKE") {
            if (elements.GetCount()!=3)
                  return FALSE;
            uint32 location = atoi( elements[1]  ); /////  Want hex addresses?  Don't use atoi.
            uint8 value = atoi( elements[2] );
            Poke( location, value );
            return TRUE;
      }
      if (command == "PEEK") {
            if (elements.GetCount()!=2)
                  return FALSE;
             //...et cetera
      }
     // other commands
}
You can augment with error messages or with other enhancements. Variable argument for example, if a memory location is Peeked or Poked, the address given would be saved as a default for the next command. If efficiency is important, say running files of commands as fast as possible, more advanced techniques can be used. If the function BreakUpLineBy() isn't clear, I can elaborate. It simply finds instances of the characters of the second string, in the first string as delimiters, and creates the list "elements" accordingly.
 

djsfantasi

Joined Apr 11, 2010
9,163
Why do you do this? what is this for?
It’s to parse the actual integer value.

Assume that your input is “134”. The first character is a “1”, so I set the result to 1 to start.

Reading the next character, I get a “3”. So I set the result to 1x10+3 or 13.

The next character is a “4”. So I set the result to 13x10+4 which is 134.

There are no more characters, so I return the last result of 134.

Each succeeding character represents another decimal digit, so be multiplying the previous result by 10, it shifts the result one decimal place. Then I add the value represented by the current character.
 

MrChips

Joined Oct 2, 2009
30,802
I have written a number of ASM code assemblers for 6502, 6800, 6805, 6809, 68HC11, Atmel AVR, etc.
The techniques used for parsing are the same. Basically, you look for your parameter delimiter, whatever characters you choose, and put the parameters in separate arrays. Or you can delimit the parameters in place and just pass the address of the parameter.
 
Top