1

My job and my personal interests often have me writing parsing code, that is, code that converts from an input string written in a given formal language to a tree of model objects.

I come from a C language background, and the strategy to do so in C is pretty straightforward:

struct my_object *parse(const char *input);

This is a simple view of things, but you get the point: this single API can do whatever it wants internally, as long as it converts the given string to a model object.

Generally, the function creates a structure of state variables internally that are only meaningful during the parsing process. This structure is shared with internal, private functions:

static void parse_a(struct parsing_state *parsing_state);
static void parse_b(struct parsing_state *parsing_state);
static void parse_c(struct parsing_state *parsing_state);

/* ... */

struct my_object *parse(const char *input)
{
    struct parsing_state parsing_state;

    /* ... */

    parse_a(&parsing_state);
    /* ... */
    parse_b(&parsing_state);
    /* ... */
    parse_c(&parsing_state);

    /* ... */

    return my_object;
}

I believe that procedural programming is a perfect choice for this task: convert this to that. I do not need a "parser" object instance here. When I need to implement such a task in an OOP language (mainly, C++, Java, and Python), I always end up with this scheme (Python used for the following examples):

class Parser:
    def _reset(self, init_state, input_string):
        self._state = init_state
        self._input_string = input_string

    # ...

    def parse(input_string):
        self._reset(23, input_string)
        # ...
        self._parse_a()
        # ...
        self._parse_b()
        # ...
        self._parse_c()
        # ...

        return my_object

And then, to parse an actual input string:

parser = Parser()
my_object = parser.parse(input_string)

In other words, a parser object is created by the user only to hold the current parsing state, and then discarded. I believe that creating this instance on the user side is useless. Of course it can be wrapped:

def parse(input_string):
    parser = Parser()
    return parser.parse(input_string)

But still, I find that the parser object, even if hidden to the user, is useless, because it's only alive for the parsing process. More importantly, this _reset() method above needs to exist somehow, because there is no guarantee that the parser object won't be reused with another input string, therefore old state variables need to be reinitialized. The ideal would be to initialize the state in the constructor, and guarantee that the parse() method is only called once.

The procedural way is always available in all those languages: simple module-level functions in Python, C-style functions or static methods in C++, and static methods in Java. All those functions can pass a plain old data structure keeping the current parsing state to private functions/static methods, which is discarded before returning the final model object.

My question is: is there any idiomatic way to achieve this task without relying on non-member functions/static methods in OOP languages?

  • 3
    What if the idiomatic way is to use static methods? – Ixrec Apr 18 '16 at 22:44
  • 1
    Is there a reason you don't like objects that only hang out long enough to do their duty? – Steven Burnap Apr 18 '16 at 22:47
  • 4
    Seems like just a rant against OO. One thing you could do differently with OO would be to take the input in the constructor and then have the object represent the parsed state. There are probably a lot of useful things you could do by holding the final parsed state and writing member functions against it. – Matthew James Briggs Apr 18 '16 at 23:37
  • 1
    You can still use a single object with an instance method, without requiring that the object be reset each use (and is also thread safe, fwiw). Such a method simply holds no state in the object, using the stack (auto variables) instead. This way we can avoid some of the well-known problems with statics, namely around testability, tight coupling/substitutability, injection/inversion. – Erik Eidt Apr 18 '16 at 23:59
  • 2
    Oh, that's an easy one. Static methods exist precisely so that you don't have to do prototypical OOP in the usual OOP language suspects. Nothing prevents you from using static methods all day long to write procedural code, except perhaps irritating the OOP pedants. Most of the modern OOP languages are multi-paradigm anyway; C# is actually "object-functional." – Robert Harvey Apr 19 '16 at 3:57
2

You are going down the wrong road by using a _reset() method for clearing the parser state. Rather you should ensure that the Parser object is is only used for parsing a single input once. You can do this either by providing the input string in the constructor or in a factory method, and perform the parsing immediately the same place. That way there is no way to "reuse" the Parser object.

With C structs located on the stack, you have a guarantee at the type level that the data is only used "downwards" in the call stack, and cannot possibly be leaked to the client. With Pythons "pass by object" semantics you cannot have such a constraint at the type level, so instead you use encapsulation to provide the same assurance. If you have some temporary object which you don't want to leak to the client, then you simply don't expose or return the object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.