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 > >
- References:
- Possible to insert an input line using $PreRead?
- From: Vince Virgilio <blueschi@gmail.com>
- Possible to insert an input line using $PreRead?