MathGroup Archive 2009

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Possible to insert an input line using $PreRead?

  • To: mathgroup at smc.vnet.net
  • Subject: [mg102840] Re: [mg102845] Possible to insert an input line using $PreRead?
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Sun, 30 Aug 2009 06:05:18 -0400 (EDT)
  • References: <200908291032.GAA00367@smc.vnet.net>

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


On Sat, Aug 29, 2009 at 3:32 AM, Vince Virgilio <blueschi at gmail.com> wrote:

> 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
>
>


  • Prev by Date: Coupled Diff Eqs or Poisson Eq, is symbolic solution possible?
  • Next by Date: Re: Update on earlier post of Problems encountered with
  • Previous by thread: Possible to insert an input line using $PreRead?
  • Next by thread: Problems encountered with Mathematica