# How to programmatically replace an expression in a .m file

I'm trying to write some code to programmatically replace a particular expression in my init.m file with a different expression. (It'll probably be some multi-line construct wrapped in a Module[{someTag}, ...] (where someTag is chosen to be unique and descriptive).)

I was naively thinking Import["init.m", "HeldExpressions"] was going to be just what I needed, and would roundtrip nicely with an Export[], but Import[] doesn't seem to help me preserve white space or comments.

So I think I'm going to have to do this as text, and not as a list of expressions. I'm prepared to write my own (simple) expression splitter, but I'd be thrilled if there's some way for me to avoid going down that path.

This might be crazy talk, but is there any good way to enlist the help of Mathematica's parser to spot where an expression begins and ends?

-
Why not use myComment["this is it"]; instead of (* this is it *)? Then you preserve comments also with "HeldExpressions". And whitespace you could code as mySpace[" "]; And if you want just write a small M-script which then creates a nice-looking init.m file. – Rolf Mertig Jan 9 '13 at 20:33
That'd be fine if we were talking about a script to update just my carefully controlled init.m. But I'd like my init munger to also work with somebody else's pre-existing init.m without them having to rewrite it. – sblom Jan 9 '13 at 23:49
Probably too much work, but you could try to call (via JLink) the source code formatter of Eclipse (Wolfram Workbench) to do the formatting. – Rolf Mertig Jan 10 '13 at 2:50
Why do you want to do this? init.m files are non-portable, and one tends to forget that they are there and all kind of hell can break loose and pseudo-bugs appear (not that there are not already enough deficiencies ...) – Rolf Mertig Jan 10 '13 at 2:52
@RolfMertig, good point about init.m being a possible source of confusion. But what do you mean by non-portable? It seems to me that an init.m file is exactly as portable as its author makes it. – sblom Jan 11 '13 at 15:42

Figured out what I'm going to do:

1. Read a line from the file.
2. Try converting it to an expression (ToExpression[]).
3. If successful, I've found an expression (or a comment or a blank line).
4. If not, grab additional lines and concatenate them with the line that failed to parse until I find a series of lines that succeeds.

The code:

ClearAll[breakExpressions];

breakExpressions[lines_, exprs_: {}, expr_: ""] /; Length[lines] == 0 :=
If[expr == "", Flatten[exprs], Flatten[{exprs, expr}]];

breakExpressions[lines0_, exprs_: {}, expr0_: ""] :=
With[{expr = expr0 <> First[lines0] <> "\n", lines = Rest[lines0]},
If[MatchQ[
Quiet[
Check[ToExpression[expr, InputForm, Hold],
Null,
{ToExpression::sntxi, ToExpression::sntx}],
{ToExpression::sntx, ToExpression::sntxi}],
Null],
breakExpressions[lines, exprs, expr],
breakExpressions[lines, {exprs, expr}, ""]]];

breakExpressions[