-
Notifications
You must be signed in to change notification settings - Fork 139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Recursive call overrides earlier data #521
Comments
On Wed, 16 May 2018 11:01:28 -0700 Alex Shroyer ***@***.***> wrote:
I'm trying to parse an expression tree like `f(g(), g())` using recursive des
cent, but instead of getting a tree like this:
```
f
/ \
g h
```
I get something more like this:
```
h
/
h
```
This is the full source code:
```/ F(G(),H())
tokens: ((`id;"F")
(`oparen;"(")
(`id;"G")
(`oparen;"(")
(`cparen;")")
(`comma;",")
(`id;"H")
(`oparen;"(")
(`cparen;")")
(`cparen;")"))
peek: {x~tokens[;0][0]}
eat: {
t: *tokens
tokens :: 1 _ tokens
if[~x~t[0]; \echo "no match for ",$x; : 0]
t}
parse_call: {
***@***.***
ae: parse_arg_exprs()
.((`name;name);(`arg_exprs;ae))}
parse_arg_exprs: {
arg_exprs:()
eat[`oparen]
if[~peek[`cparen]
arg_exprs:,parse_call()
while[peek[`comma]
eat[`comma]
arg_exprs,:,parse_call()]]
eat[`cparen]
arg_exprs}
parse_call()
```
And this is the result:
Here's the full result:
```
.((`name;"H";)
(`arg_exprs
,.((`name;"H";)
(`arg_exprs;();))
))
```
To me, it seems that the deeper nested call to `parse_arg_exprs` overwrites t
he "parent". Am I misunderstanding Kona scoping rules or is this a bug?
This is a bug.
|
For what it's worth, this reminded me of a quirk (not a bug) of Python's scoping rules: https://stackoverflow.com/a/233835/2037637 . I wondered if a similar thing is going on in Kona. |
On Wed, 16 May 2018 19:27:42 -0700 Alex Shroyer ***@***.***> wrote:
For what it's worth, this reminded me of a quirk (not a bug) of Python's scop
ing rules [link](https://stackoverflow.com/a/233835/2037637). I wondered if
a similar thing is going on in Kona.
k3 (and kona by extension) variables have only global scope or
local (function level) scope. Your code works as expected in
k3.
|
2 simple cases:
This one works:
|
Kona console:
It seems like the inner function doesn't return anything (not even nil) and breaks the outer function. |
There are several oddities with assignment inside a void function:
@tavmem do you think this is the root cause of the bug I saw with the recursive functions? |
It could be. Hard to say for sure. |
In a k2.8 console
|
The first "oddity"
is a regression. It first shows up in commit 0e63515 made on Aug 10, 2017 titled "suppress display on amend". |
First "oddity" is fixed. The following cases are still problematic:
|
All 3 "oddities" are fixed. Your recursive case is not resolved.
|
Here's the next attempt at a relevant "simplistic" case:
In kona:
|
This issue is not a regression. I went back to the code as of commit 611fdca titled "Fix backslash commands with a leading space" made on June 23, 2011 and got similar results:
|
I suspect the issue may be that the symbol table associated with the function definition is being modified, rather than each function instantiation getting its own copy of the symbol table. For each instantiation (when a function is called), a fresh dictionary needs to be created and pushed on the stack. When the function is exited, it is popped from the stack and destroyed. |
If the subroutine is redefined as a void function, the problem persists
In kona:
|
@bakul: |
However, if you make everything a void function, the problem disappears:
in kona:
|
Just to document all variations:
in kona:
|
That begs the following question:
Yes ... in k2.8:
No ... in kona:
|
A better listing of simple cases that "Don't work" and that "Do work". Don't work:
Do work:
|
It was interesting that this works in Kona: The reason is not what I expected. |
I think the real issue is early binding. The following snippet will behave differently in k3 and kona:
In processing lambda expression on RHS, kona replaces the inner f with self reference. This changes the definition of g to a recursive one so redefining f later has no effect on g and g 4 yields 16. Not so in k3, where g 4 will yield 0. I alluded to this early binding issue in an old bug report (don’t recall which one now). To see the difference, compare the contents of .k for both interpreters after each definition. Do the same for mutually recursive functions. |
I agree that the "early binding" you describe is a problem that needs to be fixed However, I think that the incorrect result in the simplified example that we have been discussing is somewhat different (but could also be described as a "binding" problem):
Here, kona did not replace the inner f with self reference. Consider the following (that gives the correct result):
The assignments to "a" in the lambda of the second example have no effect on the correct value of "a" reported by the outer "f". In the first example, the two executions of "f" (inner & outer) should be completely independent. |
A diagnosis of the problem: The function "f" is stored as a type 7.
Without getting overly verbose, the CACHE_TREE goes through the following transitions:
Then, when processing the inner "f"
When getting back to the outer "f", the CACHE_TREE still contains the values from the inner "f". |
Re: binding. Sorry for the confusion. Looks like you are on the right track in spite of that :-) I modified the program to print before and after values and see interesting results! Kona:
k3:
|
Yes ... thanks ... quite interesting. k2.8
kona
|
Also interesting ... kona:
|
Simply in the interest of documentation, in kona:
The type error occurs in the function vf_ex contained in file ~/kona/src/kx.c
|
But ... (just saying) ... this does seem to reinforce the "need for two paths": |
Maybe ... resolved by a cover function. |
As I noted earlier, there is a solution where you don’t need a local dictionary or a dictionary stack but that’ll require very simple compilation, where ref to a variable becomes an index into local frame. Though this may be hard to implement in kona at present. But even with this scheme you’d need the global dictionary (.k). In k3
You can see that this form of amend changes the global a, not the local a. This shows you need .k but you can compile away local variable and parameter references into stack indices. |
Very interesting. Thanks!
|
Implementing either solution in kona is difficult. I set up a counter to track the number of times that the recursive parser The results for this issue (as defined) is:
The parser is called 164 times, during 31 completed executions. I think my next step is to formulate a very simple example that causes a similar problem. |
Here are a couple of possible simplifications: First, reduce the token definition from 10 lines down to 6 (to cut down the number of recursions):
Second, as an initial step to localizing dictionaries where needed,
The value in "name" must always be "local" to the recursion level, and must change back to "F" as the recursions complete. |
A much simpler script that also fails:
|
An even simpler script, that fails with fewer parse/execute cycles:
|
Simpler ... fewer calls to "parser" and to "execute":
Using this simple script in Kona: |
Making a change to a single line of code gives the correct result:
rcr.k (short for recursion.k) is the name I gave to the original problematic script listed at the beginning of this issue.
However, running k_test (with the change) results in memory leaks for 215 tests:
It seems that;
But ... is there any way to determine that a recursive call is executing? |
Don't you need a |
i am dropping the idea of a "separate branch" as as I don't see an easy way to identify a recursive call. Declaring
All 34 of the "real" fails involve "subfunctions".
Some (or all) of the code that did handle subfunctions previously may need to be removed! |
Of the 2 memory leak fails: One also had a subfunction:
The second should not have been in the test list at all
Both k2.8 and kona report "stack error" when this test is run from the console
|
Pursuing the "separate branch" option again. But, then again, what about a function with a subfunction that is called recursively? |
With these changes:
we are down to 2 fails:
|
The 2 remaining fails demonstrate that the "separate branch" option does not work when there is a combination of recursion and subfunction. Documenting which branch is taken on each iteration (and the target string) gives the following results:
I am turning attention back to subfunction processing.
|
@tavmem and @bakul I just want to say thank you for working on this bug. It's been a good learning experience just to watch the process, and personally I really appreciate that there are people like you investing your time and effort into projects like this that benefit so many people. Anyway, I appreciate your work. 👍 |
Thanks for the feedback!
You are very welcome.
It’s been quite a learning experience for me too!
Best,
Tom
…On Fri, Nov 1, 2019 at 8:25 AM Alex Shroyer ***@***.***> wrote:
@tavmem <https://github.com/tavmem> and @bakul <https://github.com/bakul>
I just want to say thank you for working on this bug. It's been a good
learning experience just to watch the process, and personally I really
appreciate that there are people like you investing your time and effort
into projects like this that benefit so many people.
Anyway, I appreciate your work. 👍
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#521?email_source=notifications&email_token=AALEJJ4SDHL2TW7ICX2EPSDQRQN3BA5CNFSM4FAHDBNKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEC2ZCCA#issuecomment-548770056>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AALEJJYUVKLDI7N24JFUZLTQRQN3BANCNFSM4FAHDBNA>
.
|
@hoosierEE , You're welcome but note that Tom did all the hard work! Tom, this one still fails even with the latest sources (see #521 (comment)):
|
Thanks @bakul .... :-) |
Hey @bakul --
|
I don't know where that last backtick came from. See the original comment #521 (comment) up in the thread. But my guess is most likely you didn't type in the whole expression as one line.
|
Thanks !! |
I'm trying to parse an expression tree like
f(g(), h())
using recursive descent, but instead of getting a tree like this:I get something more like this:
This is the full source code:
And this is the result:
To me, it seems that the deeper nested call to
parse_arg_exprs
overwrites the "parent". Also, it either skips the last node or drops an earlier node, because the resulting tree is missing a leaf. Am I misunderstanding Kona scoping rules or is this a bug?[edit] For completeness, here is what I expected to see as a correct result:
The text was updated successfully, but these errors were encountered: