KSP-KOS/KOS — pull request #378 by Dunbaratu (2014-11-15 17:02:16+00:00)
Title: A large part of the way toward #258

Good news and bad.
=================

Good news, a LOT of the functionality is fixed and working.

In fact it's faster to describe what does NOT work.

Bad news: this is what does NOT work:
-----------------------------------

### Auto-extensions:
Auto-extensions on the LOCAL volume do not work like they do
on the Archive, because the logic of auto-extensions looks
like it was only implemented in the methods of Archive, and
not the base class's methods in Volume.

(What I mean by auto-extensions is: You try to write a file
called "foo", and actually get a file called "foo.ks".)

This is probably fixable by mimicking what was done for Archive.cs
in Volume.cs, but I didn't want to touch that as I suspect
@erendrake will know better if that logic exists somewhere already
in what he wrote but reverted.

### Run command:
The run command only works at one depth from the interactive
shell, and not in programs that call programs. In other words,
the interpreter can invoke prog1, but prog1 can't invoke prog2.
I know exactly why this doesn't work, but fixing it does not look
simple as it probably means moving some compile-time logic into
run-time logic.

To see what I mean, look for the places in the code where I
added the annotation "!!OH CRUD" in a comment. Those are the
comments highlighting the problem - the compiler assumes it
already knows the name of the filename you are using in the RUN
command, and it won't let you leave it as a decision for later
at runtime. The move to using any arbitrary expression *mandates*
that the filename be *calculated* at runtime, rather than fixed
at compile time so we have to find a way to move that logic from the
compiler to the runtime opcodes.

What does work now, and how it works:
--------------------------------------

This is the basic design of how I did it:

- In the TinyPG file, All places expecting a filename now accept any
expression instead.

- In the Compiler.cs file, there were many places where a file command
just tried reading the identifier text directly without sending it through
the rest of the levels of parse tree. These have been replaced with
a call to VisitNode so that the entire expression may be evaluated and
pushed on the stack there.

- If that expression happens to be a single identifier, and the identifier
refers to a variable that doesn't exist, the problem is deferred until
later. (Normally the error "Undefined variable" is not caught by the
compiler. It's not detected until runtime, which suits me just fine here.
At compile time all it does is insert an Opcode to try retrieving the
variable. That's it. At runtime it will notice that the variable can't
be found.).

- Added a flag to popping values off the stack that says "in this
context it's acceptable to have the value be not-found. If it's a
variable that's not found, then just return it's name as if it was
the value." This flag is optional and defaulted to false so it has
no effect on most of the places that code calls CPU.PopValue(). But
the few places we want this behavior (when executing the file
manipulating functions) we turn it on.

Thus when getting a value like "$myvar" when myvar doesn't exist,
will just return the string "myvar" in these special cases, but
not in the normal case.

- Added the logic to coerce a filename with no extension into a
filename with a .ks extension if wanted.

So the thinking that happens when you try to do something like this:
```
LOG "hello" to FOO.
```
Is this in the logfile() function execution:

"Okay, pop the string to print from the stack, and then
pop the filename to print to... with the 'barewordOkay' flag to true."

Then the CPU.PopStack(true) call does this:

"Okay, let's see, the thing atop the stack is $FOO, that's a variable,
go look it up. Darn there is no $FOO. Oh, but in this case I was told
that barewords were okay, so instead of bombing out I'll return 'FOO'."

Then the LOG execution goes on as normal, just as if the script had
said this instead:
```
LOG "hello" to "FOO".
```
This logic covered all the cases... and the following example works as
expected:
```
LOG "hello 1" TO somefile.
LOG "hello 2" TO "somefile".
LOG "hello 3" TO "some" + "file".
SET myFileName to "some" + "file".
LOG "hello 4" TO myFileName.
LOG "hello 5" TO "somefile.txt".
```
This results in two files:
somefile.ks :
```
hello 1
hello 2
hello 3
hello 4
```
somefile.txt :
```
hello 5
```