Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Possible to insert an input line using $PreRead?

77 views
Skip to first unread message

Vince Virgilio

unread,
Aug 29, 2009, 6:33:45 AM8/29/09
to
Hello,

I would like to insert a line into the stream read by the Mathematica
parser, probably using $PreRead. The line following the insertion
should act as the trigger to insert, but must not be interpreted and
"symbolized" (hence $PreRead) until after the insertion. The insertion
should be interpreted/evaluated first.

In particular, I'd like an expression such as scope[namespace, ...] to
insert Begin["namespace`"] before itself, and End[] after itself, to
make any symbols within itself create in context "namespace`". I think
$PreRead gets me closest to this, but not without difficulty. I only
know how to substitute within a single line of input, per the
documentation. I do not know how to add a line of input. A more formal
macro processor (think David Bailey's recent suggestions) would be a
boon here.

That is, I'd like to transform this:

scope[namespace,

x = 7;

];

into this:

Begin["namespace`"];

x = 7;

End[];

with the obvious effect of creating namespace`x instead of Global`x.
I'd set $PreRead to the appropriate value at the top of a package
file, to enable the above behavior in the rest of the package file,
and reset $PreRead at the end of the file.

The above is a toy example. Please let's not get wrapped around the
axle on the merits of this. The actual keyword is not 'scope' and its
usage and effect is more complex than creating a single symbol in a
unique context.

Thank you,

Vince Virgilio

Leonid Shifrin

unread,
Aug 30, 2009, 6:05:13 AM8/30/09
to
Hi Vince,

Let me first say that for most cases, you may not need such a heavy
machinery as $PreRead and use $Pre instead - then you can inspect/process
the already parsed code with various expression-processing functions
available in Mathematica, by using Hold-attributes and wrappers like Hold
and Unevaluated to keep the code unevaluated during code transformations.

In your case, however, this seems not possible since the context of
the parsed symbol is determined at parse-time, so by the time $Pre looks at
it it's too late.

Perhaps, you will get better solutions, but the solution that I finally came
up with is to delay the parsing of the code (body) that must be executed
inside a given context until run-time, that is, replace code

BeginPackage[yourcontext]
body
EndPackage[]

with something like

BeginPackage[yourcontext]
Unevaluated[ToExpression[body-in-the-box-form]]
EndPackage[]

This leads to parsing being performed after BeginPackage statement
has been executed, which solves the problem.

So, here is the code:

---------------------------------------------------------------------------------

ClearAll[stringify];
stringify[code_RowBox] :=
ToString[code /. {x_String /;
StringTake[x, 1] === "\"" && StringTake[x, -1] === "\"" :>
StringJoin["\"\\\"", StringDrop[StringDrop[x, 1], -1],
"\\\"\""], x_String :> StringJoin["\"", x, "\""]}];

ClearAll[shieldBody];
shieldBody[body_String] :=
RowBox[{"Unevaluated", "[", RowBox[{"ToExpression", "[", body, "]"}],
"]"}]

ClearAll[namespaceF];
namespaceF[code_] :=
code /. RowBox[{"scope", "[", RowBox[{context_, ",", body_RowBox}],
"]"}] :>
With[{cont = "\"" <> context <> "`" <> "\""},
RowBox[{RowBox[{"BeginPackage", "[", RowBox[{cont}], "]"}], ";",
shieldBody@stringify@body, ";",
RowBox[{"EndPackage", "[", "]"}]}]];

$PreRead = namespaceF;

---------------------------------------------------------------------------------

It does some number of low-level expression structure manipulations, to
achieve the goal I described above. Here is an example:

In[1] =
scope[TestContext,Print[$Context];z=7;Print[Context[z]]]

TestContext` (Printed)
TestContext` (Printed)

In[2] = z

Out[2] = 7

In[3] = Context[z]

Out[3] = "TestContext`"

Of course, since BeginPackage is used, the symbol gets imported
(the context stays in the $ContextPath before Global`), so subsequent
references to z in Mathematica session will reference TestContext`z.

Note that this construct does not support nested contexts created with
Begin and End - this is doable but I did not implement this. For instance,
in
the following you may expect the <test> symbol to be created in the
`Private` context:

In[4] = scope[TestContext,
Begin["`Private`"];
test = 10;
End[]]

But this is not so - it was created in TestContext` and subsequently
imported,
since by the parse-time that was the current context:

In[5] = Context[test]

Out[5] = "TestContext`"

The above code probably does contain some bugs - I did not do
exhaustive tests. Particularly it may not work if your code contains strings
like "\"abc\"" - that is, strings with escapes. This can also be improved.
I may post an improved implementation with the above problems fixed,
if there is any interest expressed.

Hope this helps.

Regards,
Leonid

0 new messages