Close

here's an idea... parsing

A project log for limited-code hacks/ideas

When you're running out of code-space... sometimes yah's gots to hack. Here are some ideas.

esot.ericesot.eric 12/09/2016 at 17:340 Comments

This is just a random-realization while working on my project... maybe it's obvious to everyone in-the-know.

Say you're parsing something, like commands from a terminal-window...

Say you've got a whole bunch of commands, but they mostly all follow the same handful of formats, like Ax and Ay, Bx and By, etc.

Lemme think of an example...

Say motor commands (terminated with '\n'):

SMn = stop motor number n (where n is one character, 0-9)

AMnx = advance motor N by x millimeters (where x is any number)

FMnx = move motorN forwards at x mm/sec

and so-forth.

One could, obviously, parse each character as it comes through, then do a whole bunch of nested if-then statements.

Another idea is to combine the first and second characters into a single 16-bit variable, then use a switch() statement. Maybe not ideal for *this* example, but I've found it useful at times.

So, that's one consideration, here's another:

Say this motor-system also has LED-commands:

BLnx = blink LED n x times per second

Obviously the n and the x, here, don't apply to a motor...

So, then, the whole nested-if statement thing makes sense, again, right?

But we're worried about *size* here, not speed... (baud-rate's way slower than your processor, right?)

So, then, maybe it makes sense to parse 'n' and 'x' and store them in argument-variables, and only *after that* handle the actual Command characters.

"But wait! 'SMn' doesn't have an x!"... Right... but here's the idea:

Say everything's stored in a string-buffer... and whatever arrived after the '\n' may very well be data from a previous command... But, your numeric-parser for x terminates as soon as anything non-numeric comes through (\n (or just get rid of the \n and it'll be terminated with \0...))... the variable for argument x will be filled with 0, but even that doesn't matter, because it's not being used, in this case...

Then why parse it if it's not even part of the command, and isn't even there in the first place?

So, here it is without...

uint16_t command = string[0] | (string[1])<<8;

uint8_t deviceNum = string[2] - '0';
char *value = &(string[3]);

#define commandify(a,b) \
        ((uint16_t)a | ((uint16_t)b)<<8)

switch(command)
{
    case commandify('S','M'):
        motor_stop(deviceNum);
        break;
    case commandify('A','M'):
        mm = parseNumber(value);
        motor_advance(deviceNum, mm);
        break;
    ...
    case commandify('B','L'):
        rate = parseNumber(value);
        led_blink(deviceNum, rate);
        break;
    ...
}

So, now, for the example described, you've either got to call 'parseNumber()' three times for the three commands that use it, or explicitly handle the 'S' case separately from the switch, (makes sense, unless there are *several* such cases, in which case your test becomes quite large, maybe even a second switch-statement).

Or, just parse the number from the start, and don't use it if you don't need to.

And, let's make it even more interesting, what if there's another command:

PSs = Print string s

Could *still* call parseNumber, *and* fill deviceNum (both with garbage) and have a really simple switch-statement (maybe even a lookup-table, at this point):

uint16_t command = string[0] | (string[1])<<8;

uint8_t deviceNum = string[2] - '0';

#define commandify(a,b) \
        ((uint16_t)a | ((uint16_t)b)<<8)

//### No way you're gonna get away with floats
// in a 1K project, without an FPU ;)
float value = parseNum(&(string[2]));

switch(command)
{
    case commandify('S','M'):
        motor_stop(deviceNum);
        break;
    case commandify('A','M'):
        motor_advance(deviceNum, value);
        break;
    ...
    case commandify('B','L'):
        led_blink(deviceNum, value);
        break;
    ...
    case commandify('P','S'):
       printf("%s", &(string[2]));
       break;
    ...
}

Discussions