variable declaration

This forum is meant for questions about the Visual FoxPro Language support in X#.

mainhatten
Posts: 200
Joined: Wed Oct 09, 2019 6:51 pm

variable declaration

Post by mainhatten »

António Lopes wrote: I don't think TYPE() treats local variables differently as a special case. It just evaluates its argument at run time as a VFP expression and returns its type. As a compiler problem/use case, probably it falls in the same category of EVALUATE() (as Thomas mentioned) and macro expansion, I would dare to say from the (very far) outside...
Robert has clearly has a point if going strictly by the Help Docs, where Type() and Eval() are listed as functions: If they were typical functions, at the moment they are in their own scope and access the string with the name of the local var, they should be unable to access it.OTOH "Command" is signified normally by not returning a result. From the top of my head I remember 2 vfp "commands" going against that directive: Store ... To and Do Form ... To, both modifying the variable( s)/properties pointed to. Perhaps a separate hermaphrodite between category should be defined for "executing in same scope/stacklevel and changing a result" and group Store To, Do Form To, Type() and Eval thereunder. Sounds more technical than "hanging on to a scope and playing billards with it" ;-)
The "functional" way of calling is necessitated by option of "levelled" calls, which would in normal function result in stacklevel differences > 1, but perhaps way of executing a command [GetType,GetEval] on an expression with resultstorage this can be tackled. Python and Javascript are similarly freewheeling as vfp, but those with static compile targets have less freedom in that area - C# calls it evaluating expressions and in Java myriads of attempts to slice that problem can be found.
As often as type() and evaluate() are used, I'd hate to see a "visible namespace" to be built each time just to run an eval() or type()
User avatar
robert
Posts: 4558
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

variable declaration

Post by robert »

Antonio,

At this moment I see no way to implement this behavior without losing significant performance.
The names of the local variables are thrown away at compile time (unless you compile for debug, because then they are available in the pdb file). The resulting IL code replaces names for locals with stack positions.
The only solution that I see would be:
- If type() contains a literal string, then we could take this literal at compile time and convert it to some kind of lambda expression that we can evaluate which would indeed be able to access local variables
So we could get this to work:

Code: Select all

 LOCAL someLocalVariable := 42
 ? Type("someLocalVariable")
- However if the code looks like this

Code: Select all

 LOCAL someLocalVariable := 42
 LOCAL cMyName := "Some" + "LocalVariable"
 ? Type(cMyName)
Then we would have to resolve this at runtime, and the name "SomeLocalVariable" has been thrown away by the compiler.
FoxPro does not have this problem because it is an interpreter and can access all this information at runtime. But that comes with a (performance) price.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
mainhatten
Posts: 200
Joined: Wed Oct 09, 2019 6:51 pm

variable declaration

Post by mainhatten »

Robert van der Hulst wrote:At this moment I see no way to implement this behavior without losing significant performance. The names of the local variables are thrown away at compile time (unless you compile for debug, because then they are available in the pdb file). The resulting IL code replaces names for locals with stack positions.

- However if the code looks like this

Code: Select all

 LOCAL someLocalVariable := 42
 LOCAL cMyName := "Some" + "LocalVariable"
 ? Type(cMyName)
Then we would have to resolve this at runtime, and the name "SomeLocalVariable" has been thrown away by the compiler.
FoxPro does not have this problem because it is an interpreter and can access all this information at runtime. But that comes with a (performance) price.
You will encounter a smaller, but significant # of calls of concated strings, probably more with eval(), which I guess has the same problem - would appreciate if you could confirm or deny if eval has similar problems from your POV.

Is there a chance to compile resolve into something similar at compile time (here pseudo-vfp)

Code: Select all

private __poVarScope as createobject("varscope")
= addproperty(__poVarScope, "_LocalsStack", createobject("empty") && or add a dict/hashlist, whatever
for lnRun = 1 to alen(LocalVarsKnownAtCompileLikeInPDB)
     = addproperty(__poVarScope._LocalsStack, LocalVarsKnownAtCompileLikeInPDB[lnRun],#CorrespondingStackNumber)
next
=addproperty( __poVarScope, "cParam", ParameterOfTypeOrEval)
varscope.Do()...
before the variable names are ditched or to enable debugging for all functions with such functions or similar ways to keep existing functionality even in compiled code ? Eval() in vfp always was IMO astonishingly guick - if ratio between "normal" code and eval worsens, I can cope, but rewriting sometimes many calls will spook away possible users with large code basis.

my 0.02€
thomas
User avatar
robert
Posts: 4558
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

variable declaration

Post by robert »

Thomas,
Yes, I think something like that could be possible.

We could let the compiler generate list of local names and their current values when it detects a call to Type() or Eval() and pass this list as second parameter to Type() and Eval() so they can create private variables for just the scope of Type() or Eval() with the same names and values as the original local variables, so the variables will be visible to the macro compiler when it evaluates the expression.
I'll talk with Nikos about this. He may have a better idea.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
atlopes
Posts: 83
Joined: Sat Sep 07, 2019 11:43 am
Location: Portugal

variable declaration

Post by atlopes »

Robert [and Thomas, et al.],

In my opinion, without using something that resembles or substitutes the VFP's name table, it is not going to be easy to aim at a reasonable level of language compatibility with FoxPro.

TYPE() and EVALUATE(), as well as macro expansion, are examples where an interpreter execution context is required, and access to objects' names is essential.

But it can get "worse," as Robert puts it.

The full range of array related functions may generate an array passed as a name to a particular function. For instance,

Code: Select all

LOCAL Source AS String

m.Source = "Hi" + CHR(13) + "there!"

? ALINES(Lines, m.Source)

? m.Lines(1)
? m.Lines(2)
results in

Code: Select all

2
Hi
there!
and the creation of a two-element Lines private array. Rewriting it as

Code: Select all

LOCAL Source AS String
LOCAL ARRAY Lines(1)

m.Source = "Hi" + CHR(13) + "there"

? ALINES(m.Lines, m.Source)

? m.Lines(1)
? m.Lines(2)
will not build the private array but will redimension the local one that is implicitly passed by reference to the function.

While I understand performance arguments that may be put forward, without the support of this, migration from VFP code and coders to X# will be harder to accomplish.
User avatar
robert
Posts: 4558
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

variable declaration

Post by robert »

Antonio,
Thanks for your valuable comments.
We are aware of this functionality and will find a way to make this code run in X#.
Some of these "language features" will require us to do special handling in the compiler to allow manipulation of the "evaluation context" from a function in your code from within a function in the Runtime.
In most other languages features like this would never be possible (and would be considered 'bad practice').
But we understand that this is needed to fully support VFP.
So we will add support for this to the compiler and runtime.

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
mainhatten
Posts: 200
Joined: Wed Oct 09, 2019 6:51 pm

variable declaration

Post by mainhatten »

Hi Antonio,
António Lopes wrote: TYPE() and EVALUATE(), as well as macro expansion, are examples where an interpreter execution context is required, and access to objects' names is essential.In my opinion, without using something that resembles or substitutes the VFP's name table, it is not going to be easy to aim at a reasonable level of language compatibility with FoxPro.
Robert seems to think that something along the lines of my proposed solution will work - and I think so as well, even if looking at it from "normal compiler POV" it looks like a wart and probably have runtime ratio of namespaceaware vs. normal code which is much worse than what we are used to in vfp. But if going more for static typed there will be less need for calls to type() for instance and at least sometimes some of those could be exchanged to call to vartype(), which sometimes is not done in vfp as type() won't throw errors, is not that much slower,and even with good coders things like deadlines can result in hasty/sloppy code ;-)

On Eval(): in my code perhaps 20-40% of eval() could be replaced by getpem(), which usually does not have the problem we are discussing unless called in something like getpem(&lcRefDesc)
But it can get "worse," as Robert puts it.
The full range of array related functions may generate an array passed as a name to a particular function
Had thought about those (for us vfp coders) error-forgiving functions and am tentatively certain they won't be as much of a problem as eval(). We are lucky that vfp does not hoist var declarations to top of the function like JS, so we can combat locally. Consider the compiler inserting automatically something equivalent to:

Code: Select all

try
    lcRes = vartype(aInQuestion)
    if lcRes=="U"
        dimens aInQuestion[1]
    else
      *-- sometimes you have to be bad
      lcRes = vartype(aInQuestion[1])
    endif
catch to loExcept when (normal var, or if macroexpanded as normal object.property, OLEObject.Property... missing anything?)
       *-- to be good
        dimens aInQuestion[1]
endtry
= aAnyFuncNeedingOrBuildingArray(aInQuestion, , , ,)
This should work also when resolving the array like vartype(&lcArrayName[1]), aAnyFuncNeedingOrBuildingArray((lcArrayName), , , ,), although I have not done the drudgework of trying all possible permutations... Try...catch should be reasonably fast if no error is thrown and catch branch might be enhanced with a log entry that code will be faster with explicit dimens[1] coding up front
somewhat reordered
In my opinion, without using something that resembles or substitutes the VFP's name table, it is not going to be easy to aim at a reasonable level of language compatibility with FoxPro.
While I understand performance arguments that may be put forward, without the support of this, migration from VFP code and coders to X# will be harder to accomplish.
I agree that without much effort both of us can code something which will put normal compilers into a difficult situation

Code: Select all

= alines(laParms, FiletoStr("withManyLinesOfSingleWords")
= inscrutable(laParms[1], laParms[2], laParms[3], laParms[4], laParms[5], laParms[6],laParms[7])
function inscrutable(tc1, tc2, tc3, tc4, tc5, tc6, tc7)
   &tc1 &tc2
   &tc3 &tc4
   &tc5 &tc6
   return aArrayFunc((tc7), , , ,)
that is not the typical code we produce (I KNOW, as I have read your csv class code and I liked it...)
so let us hammer on those special cases when and if they crop up...

regards
thomas
Post Reply