What is a good approach to build a versatile parser both for REPL interpreters and compilers? What I mean by the interpreters is kind of a read-eval-print loop. To support both of them, the parser should support a whole program parsing and a line-by-line parsing. The LALR(1) algorithm introduced by the Dragon book is good for a whole program parsing, but it should be engineered a little bit for being used to support a line-by-line parsing simultaneously. Since the two styles of the parsing share the same grammar for the programming language, I believe that there would be a modular method for building a single parser for the two purposes, but I can't find it. Can you help me on this matter?
What you want is a GLR parser (or GLL), and you to abuse it particular way.
What makes the GLR parser useful here is its willingness to pursue all possible parses until one resolves out as the valid answer. (Most of the standard parsers offered (LL, LALR, recursive descent) pursue only a single possible parse and often cannot handle complexities introduced by long (e.g, indefinite) lookaheads.
With this in place, you now adjust your original grammar having a goal rule G and set of other nonterminals A-Z:
G -> A;
A -> B;
B -> C;
...
Y -> Z;
to be:
G -> A;
G -> B;
G -> C;
...
G -> Z
A -> B
B -> C
...
that is, you add every nonterminal as an additional goal rule.
Now your parser will any valid nonterminal of the langauge. You can use "nonterminal reduces to G" as a trigger to decide if you want to "compile" the nonterminal if the original toplevel nonterminal A is returned, or "interpret" that nonterminal (if it is not the original top level nonterminal, e.g, B-Z) or just ignore that input and wait for more if you think interpreting things like then clauses without the if part are not interesting.
You can explicitly modify the grammar (easist) or you can bend the GLR parser to launch all nonterminals in its starting state, whcih has the same effect.
My company uses GLR to parse source code patterns (as nonterminals) rather than "compile" or "interpret", but that uses exactly the same trick.
It isn't easy to modify a GLR parser to do it, but it isn't "technically" hard, either. You do have to deeply know how parsers,and esp. GLR work, to work out the details and tie it all together.