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?