This is an old revision of the document!


June 29: Implemented while-done loops and for-done loops. The current idea is:

  • I have a global variable prog_ctr, that is the program counter
  • A stack (not as a stack, but just for storage space) is used to store the commands
  • Another stack is used to store the information for a loop.
  • In the system() function (I'm using this because it is the common point for both scripts and typed commands), a command is stored if the prog_ctr >= size of the command list. This means that it is a new line, that has not been stored before
  • while, for, do (not yet implemented), done, break and continue are defined as commands.
  • The while and for commands will push the required information onto the loop stack, and the evaluated condition onto the if stack. In addition, the for command has to check whether this is the first iteration. If so, it will set the loop variable to the first word in the list, else it will set it to the next one. Similar to the if command, the while and for commands will push a 0 onto the if stack if the top of the stack is 0 (meaning that the commands in the loop should not be executed).
  • The done command will check whether the top of the if stack is true. While it is true, all the commands inside the loop will be executed. This will run through all loops exactly one extra time, without executing it.
  • break and continue will set the values in the if stack, from the while/for condition to the top of the stack to 0. Now, since continue means to continue from the next iteration onwards, this fact needs to be stored as part of loop information, and considered by the done command.
  • The do command will need some thought.

Hence, the following fields are needed for each loop:

  • program counter for start of the loop
  • the position of the loop condition on the if stack (required for break and continue)
  • whether we have encountered a continue
  • the current argument in a for loop

Today's commits:

June 30: Implemented the do command as just a no-op. This was done just for the sake of familiarity with the shell syntax. Another decision was to allow recursive expansion of variables. This will allow the use of 'references', where a variable can hold the name of another. For example:

set cur-iface net0
echo ${${cur-iface}/ip}

will print out the IP address associated with the net0 interface. Today's commits:

July 1: Disaster! I just realised that the script written for the while/for loops doesn't work. This occured after some minor changes that were meant just as sanity checks. After some digging around, and a lot of debugging, I realised that the done command didn't pop the else stack, which grows along with the if stack and is used to detect multiple else statements. So, I fixed this by making sure the if and else stacks were pushed and popped together. Also added an assert statement at these points. Had a discussion with Stefan about the work done so far, specially the generic stack idea. He suggested that instead of keeping a field with each stack to record the size, I could pass the size into the push and pop function (using the typeof macro). Unfortunately had some problems with that, too, which took some time to track down. So, in short, today was mostly spent in debugging. Also did a git rebase, to reorder the commits better in the repository. Today's commits:

July 2: Continued with yesterday's idea, and decided to see if using macros to inline the code would lead to some savings. The obvious code, allocating memory on the heap, did not offer any size changes. However, using arrays led to a decrease of around 0.5KB. But, doing so leads to a limit on features that use the stack: nesting of branches and loops, the number of arguments in a command, and the number of lines in a script. The first two could be solved by setting the limit high enough, but the last may be a problem. So, since the framework for lists is already present in <gpxe/lists.h>, I decided to store the commands using a list:

struct command_entry {
    struct list_head neighbours;
    char line[1];
};

The command_entry is allocated as malloc(sizeof(struct command_entry) + strlen(command)). This required some modification to the program counter. The loop information stored on the loop stack now stores a pointer to a command_entry struct stored in the heap. The prog_ctr variable is also replaced by a cur_command pointer, which points to the currently-executing command. Today's commits:

July 3: While working on quoting, I had missed out incomplete lines, for example:

gPXE>echo "Hello
>World"
gPXE>echo Hello \
>World

So I worked on this today. The idea is that I have a global variable that is set once the line is found to be incomplete. In the system() function, execv() is called with only complete lines, and the space used for the command is freed. Else, the command is kept stored in a static variable, and on the next call to system(), the new command is appended to the old one. The entire string is then parsed as usual.


QR Code
QR Code soc:2009:lynusvaz:journal:week6 (generated for current page)