parse an user input

Discussion in 'Programmer's Corner' started by bug13, Jan 30, 2019.

  1. bug13

    Thread Starter Senior Member

    Feb 13, 2012
    1,639
    62
    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 (Text):
    1.  
    2. // buffer with user input
    3. uint8_t *buff;
    4.  
    5. // command only
    6. if parse(buff, "command_1"){
    7.   // do stuff
    8. }
    9.  
    10. // command with params
    11. if parse(buff, "command_2"){
    12.   // some magical code to make buff point to after "command_1"
    13.   if (parse(buff, "params_1"){
    14.     // do stuff
    15.   }
    16. }
    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 (Text):
    1.  
    2. // buffer with user input
    3. uint8_t *buff;
    4.  
    5. // hopefully something like this??
    6. void cli_parse("variable arguments", function _callback);
    7.  
    8. // then I can use this like this
    9.  
    10. // command only
    11. cli_parse(buff, "cmd_1", cmd_1_callback);
    12. // command with parameter
    13. cli_parse(buff, "cmd_2", "param_2", cmd_2_callback);
     
  2. MrChips

    Moderator

    Oct 2, 2009
    18,451
    5,849
    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.
     
  3. bug13

    Thread Starter Senior Member

    Feb 13, 2012
    1,639
    62
    one command with 2 parameters are more than enough for me. For example:
    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?
     
  4. djsfantasi

    AAC Fanatic!

    Apr 11, 2010
    4,793
    1,812
    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 (Text):
    1. Size=2
    2. Function=1,First Value=10,Second Value=20
    3. Function=2,Only Value=99
    4.  
    Pseudo code looks like this:
    Code (Text):
    1. ParseInput(*Size);
    2.  
    3. for (int I=0;I<Size;I++) {
    4.     ParseInput(*Fcn,*Parm1,*Parm2);
    5.     switch case(Fcn)
    6.         case 1;
    7.         {}
    8. //...
     
  5. MrChips

    Moderator

    Oct 2, 2009
    18,451
    5,849
    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.
     
    bug13 likes this.
  6. 402DF855

    Active Member

    Feb 9, 2013
    55
    16
    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.
    Code (C):
    1.  
    2. BOOL ProcessUserInputLine(const CString &line)
    3. {
    4.       CStringArray elements;
    5.       BreakUpLineBy( line, elements, " \t:;,"); // To simplify specify a space for separation.
    6.       if (lines.GetCount()<1)
    7.             return FALSE;
    8.       CString command = elements[0];
    9.       command.MakeUpper();
    10.       if (command == "POKE") {
    11.             if (elements.GetCount()!=3)
    12.                   return FALSE;
    13.             uint32 location = atoi( elements[1]  ); /////  Want hex addresses?  Don't use atoi.
    14.             uint8 value = atoi( elements[2] );
    15.             Poke( location, value );
    16.             return TRUE;
    17.       }
    18.       if (command == "PEEK") {
    19.             if (elements.GetCount()!=2)
    20.                   return FALSE;
    21.              //...et cetera
    22.       }
    23.      // other commands
    24. }
    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.
     
    bug13 likes this.
  7. bug13

    Thread Starter Senior Member

    Feb 13, 2012
    1,639
    62
    Why do you do this? what is this for?
     
  8. MrChips

    Moderator

    Oct 2, 2009
    18,451
    5,849
    He is doing decimal to integer conversion, otherwise known as atoi( ) function.
     
  9. djsfantasi

    AAC Fanatic!

    Apr 11, 2010
    4,793
    1,812
    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.
     
  10. MrChips

    Moderator

    Oct 2, 2009
    18,451
    5,849
    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.
     
    bug13 likes this.
Loading...