27.4 Adding a constant. 4tH 4tH Forth compiler
Reklam
Reklam
CHAPTER 27. MODIFYING 4TH 515
If an error occurred before any compiling took place AbortCompile() also discards the Code Segment and the String Segment, thus returning a bare H-code header. As you already know, the error-code is stored in the member "ErrNo" of the H-code header.
If the error occurred after words have been compiled, this partial compilant is not discarded.
Instead the member "Reliable" is set to FALSE, indicating that the compilant cannot be run or saved. It can be decompiled, thus enabling the user to track the error.
In the next sections we will take a closer look at the three main tables in comp_4th(), which contain all of 4tHs built-in words.
27.4
Adding a constant
Adding a constant is very easy. You only have to update a single table in comp_4th().
The table with constants, which is embedded in GetConstant() has four members:
1. Length byte
2. Name
3. Type
4. Value
The length-byte is used to quickly scan the table. All words are skipped until it reaches the constants with the same length. Then it starts to compare the names.
What happens then depends on the argument "mode". When it equals W_EXEC, the constant is compiled (a literal with the value as argument). Otherwise, only the index in the table is returned, pointing to the constant we just found. This enables mere searches in the table. When a name isn’t found at all, it returns MISSING.
Say, we want to add a constant called "TWENTY". At least, we know its name: "TWENTY".
The name "TWENTY" is six characters long. And of course, we want to compile the number "20" each time it is referenced in the source. To make comp_4th() compile a number, we need the token LITERAL. The four members are:
1. Length byte = 6
2. Name = "TWENTY"
3. Type = LITERAL
4. Value = 20
Since every constant is a signed 32 bits number, we add the modifier ’L’ to the "20". So the complete line we have to add to the table reads:
{ 6, "TWENTY", LITERAL, 20L },
Now we have to insert this line into the table. Note that constants with the same length have to be grouped together:
{ 5, "INPUT", LITERAL, F4_READ },
{ 6, "OUTPUT", LITERAL, F4_WRITE },
{ 6, "APPEND", LITERAL, F4_APPND },
{ 7, "(ERROR)", LITERAL, CELL_MIN },
CHAPTER 27. MODIFYING 4TH 516
We decide to put our constant behind "APPEND". Of course, we could have put it behind
"INPUT" or "OUTPUT" as well:
{ 5, "INPUT", LITERAL, F4_READ },
{ 6, "OUTPUT", LITERAL, F4_WRITE },
{ 6, "APPEND", LITERAL, F4_APPND },
{ 6, "TWENTY", LITERAL, 20L },
{ 7, "(ERROR)", LITERAL, CELL_MIN },
Now recompile 4tH and run this simple program: twenty . cr
It should compile without errors and print "20". That’s all there is to it! Not to difficult to begin with, huh?
27.5
Adding a word
Now for something a little more difficult. Let’s say we want to implement ’NIP’. Of course,
’NIP’ is already available, but if you compile this program:
1 2 nip
You will see that it actually compiles to:
[0]
[1]
[2]
[3] literal literal swap drop
(1)
(2)
(0)
(0)
So, ’NIP’ is actually expanded to ’SWAP’ and ’DROP’. That is because ’NIP’ can be defined as:
: nip swap drop ; ( n1 n2 -- n2)
It removes the number under the top of stack. We call this an inline macro, which we will discuss later on. If you really want to try out ’NIP’ in the following example, you have to remove a line from ImmedList[]:
{ 3, 0, 1, "NIP", "", DoNip },
Your compiler might complain about unused functions, but it will work.
Inline macros are not the only way to add ’NIP’ to 4tH. We can also implement ’NIP’ as a word. Most words can be found in the function GetWord(). There is a single table, which is laid out like the table of constants we encountered in the previous section, so:
1. Length byte = 3
2. Name = "NIP"
But instead of the value of the constant, we have to add something else. That something is called the token. The tokens are defined in cmds_4th.h. Let’s have a look:
CHAPTER 27. MODIFYING 4TH 517
#define PAUSE 100
#define VECTOR 101
#define ENVIRON 102
#define PLITERAL 103
#define FSEEK 104
#define FTELL 105
/* ranges */
#define LastWord4th FTELL
#define LastMsg4th M4CABORT
Well, in fact you can place a new token anywhere, but then you have to renumber all other tokens. The easiest way is to place it after the last token you defined, which in this case is
"FTELL". The token "FTELL" has the number "105". Well, the next number is "106" and that is the number "NIP" is going to get. So we add:
#define PAUSE
#define VECTOR
100
101
#define ENVIRON 102
#define PLITERAL 103
#define FSEEK 104
#define FTELL
#define NIP
105
106
/* ranges */
#define LastWord4th FTELL
#define LastMsg4th M4CABORT
What part don’t you understand? But we’re not ready yet. If you look at the #define below the entry we just made, you will see that it says that the last word in 4tH is "FTELL". That is incorrect now. We’ve just added "NIP". Fixing it is very easy. Just change the line to:
#define LastWord4tH NIP
We’re done now with cmds_4th.h. Now we have change comp_4th.c, so that the compiler can recognize and compile "NIP". Now we can complete the entry for GetWord():
1. Length byte = 3
2. Name = "NIP"
3. Token = NIP
So the complete entry reads:
{ 3, "NIP", NIP },
And like we did with our constant "TWENTY", we have to give it a place inside the table between all other 3-letter words. We decided that pasting "NIP" between "USE" and
"SEEK" would be a good idea (but there are plenty of other places too):
{ 3, "HEX", HEX },
{ 3, "USE", USE },
{ 3, "NIP", NIP },
{ 4, "SEEK", FSEEK },
{ 4, "TELL", FTELL },
{ 3, "HEX", HEX },
Are we done now? No. The compiler will recognize and compile "NIP", but what does it do? That behavior will have to be defined in exec_4th.c, but we’ll discuss that in the next section.
İndir
Reklam