kitten
kitten copied to clipboard
Locals bound in REPL should be accessible
The REPL currently supports semantics for binding locals to variable names:
>>> 100 -> x
In normal code, this would be accessible within functions or pushable directly onto the stack as desired. In the REPL, it is inaccessible:
>>> 100 -> x
>>> x
REPL:2:1: error: undefined word 'x'
>>>
Bound local variables that exist outside of function scope should be maintained in memory and be able to be pushed back onto the stack when desired. The following input should ultimately be valid:
>>> 100 -> x
>>> x
----
100
>>>
I think that one of the main causes of this bug is that an expression such as 100 -> x
is compiled into:
label 0
push int 100
enter 1
local 0
i.e. 100 is pushed to the Local stack, and then left there. After optimization, this even shortens to ret 0
, or something to that effect, i.e. a null program.
Another main problem is that once something is put in a local, the compiler always generates a leave
instruction to pop it from the locals stack, so the REPL can't get access to it.
I think this is low enough hanging fruit that I can fix it, however I need guidance, how should I best approach this bug . I think that a quick hack would be to have a flag that allows for one to identify single line programs of the form 100 -> x
and convert them to def x (-> int)
and to turn on this flag by default for REPL interpretation but I can't think of anything better
Maybe, I can make Kitten.Interpret.interpret
return a tuple of (envCalls, envClosures, envData, envIp)
, i.e. all stacks and the instruction pointer. From the perspective of the "ML culture" in functional programming this is a bad idea, because it exposes components in the program to "internals" of interpret
. From the perspective of the "Lisp and Smalltalk" culture, this is a good idea because, programs are viewed as "living things", the interpreter works on the basis of introspecting the details of a running program.
Yeah, right now the ->
syntax is parsed in such a way that it introduces a local scope which lasts until the end of the enclosing block. That’s not really correct.
I think the best way to solve this issue is to separate the introduction of names (->
) from the manipulation of the local stack ({…}
). We should push a local frame when we enter a function, and pop it when we return, regardless of the number of locals. ->
should only add a new local. So in interactive mode, we’re simply in a scope that never ends.
These are the basic changes that would need to be made:
- Remove the
TrTerm a
field from theTrLambda
constructor ofKitten.Types.TrTerm
. - Change how
TrLambda
is parsed and used inKitten.Parse
. - Change the type of the
inferenceLocals
field ofKitten.Types.Program
from[Type Scalar]
to[[Type Scalar]]
and make ascope
function likeKitten.Infer.local
that introduces a new local scope. - Change the
TrLambda
case ofKitten.Infer.infer
:- From the perspective of the data stack, it should have the same type as
drop
. -
Kitten.Infer.local
should not be scoped, but only introduce a new local in the current frame.
- From the perspective of the data stack, it should have the same type as
- Change the
TrClosure
case ofinferValue
to usescope
. - Change the
TrLambda
case ofKitten.IR.irTerm
.
I’m not entirely sure about the IR and codegen parts, but we can deal with those when we come to them.
I understand why all of these changes have to be done, except for the ones having to do with inferenceLocals
, as I understand it, this field contains a list of the types of all locals that are in scope during the execution of some program. Why would this have to change after your suggestion?
Ah, right, the type doesn’t need to change. Every function starts with 0 locals and drops all of them at the end; ->
only adds locals. And the leave
instruction is unnecessary.
OK, I'm giving myself the deadline of 29th October
How’s it going? Is there anything I can help with?
I'm afraid it's not going well, due to the demands of school (~6hrs homework) per day, I have only finished the first three bullet points you listed above.
This is semi-fixed in the new compiler. In top-level code, variable scopes can cross other top-level program elements:
"hello" -> x;
define f (…) { … }
x say
Using Kitten.Parse.composeUnderLambda
. This is not yet working in the REPL because Kitten.Enter.defineWord
needs to be updated.