1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
//
/// @file dictionary.hpp
/// @brief Defines the dictionary interface and common functionality.
//
// Alee Forth: A portable and concise Forth implementation in modern C++.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ALEEFORTH_DICTIONARY_HPP
#define ALEEFORTH_DICTIONARY_HPP
#include "config.hpp"
#include "types.hpp"
#include "ctype.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
/**
* @class Dictionary
* @brief Provides an interface and essential funcitonality for a dictionary.
* @details The core read and write functionality is left virtual so that
* dictionaries can be stored in any medium. So, this class cannot be used
* directly; the programmer must define a dictionary class that inherits this
* one.
*
* Dictionary entry format (for a 16-bit implementation):
* - One information cell:
* - bits 0..4: Length of name
* - bit 5: Set if word is immediate
* - bits 6..15: Distance (backwards) to the next entry
* - If bits 6..15 are all one-bits then "long" distance in following cell.
* - "Length" bytes of name
* - Zero or more bytes for address alignment
* - Zero or more bytes of the definition's contents
*/
class Dictionary
{
public:
/** Stores Numerical base to use for input/output. */
constexpr static Addr Base = 0;
/** Stores the current `here` address. */
constexpr static Addr Here = sizeof(Cell);
/** Stores the address of the most recently defined word. */
constexpr static Addr Latest = sizeof(Cell) * 2;
/** Stores a boolean indication of compiling state. */
constexpr static Addr Compiling = sizeof(Cell) * 3;
/** Stores the address of the last execution token determined by colon. */
constexpr static Addr CompToken = sizeof(Cell) * 4;
/** Stores the address of the current interpreter input source. */
constexpr static Addr Source = sizeof(Cell) * 5;
/** Stores the size in bytes of the interpreter input source. */
constexpr static Addr SourceLen = sizeof(Cell) * 6;
/** Stores the dictionary's input buffer (a counted string). */
constexpr static Addr Input = sizeof(Cell) * 7;
/** Stores the size of the dictionary's input buffer in bytes. */
constexpr static Addr InputCells = 80;
/** Stores the dictionary's "beginning" i.e. where new definitions begin. */
constexpr static Addr Begin = sizeof(Cell) * 8 + InputCells;
/** "Immediate" marker bit for a word's definition. */
constexpr static Cell Immediate = (1 << 5);
constexpr static Cell DistancePos = 6;
/** Maximum "short" distance between two definitions. */
constexpr static Cell MaxDistance = (1 << (sizeof(Cell) * 8 - DistancePos)) - 1;
/** Returns the value of the cell at the given address. */
virtual Cell read(Addr) const noexcept = 0;
/** Writes the given value to the cell at the given address. */
virtual void write(Addr, Cell) noexcept = 0;
/** Returns the byte stored at the given address. */
virtual uint8_t readbyte(Addr) const noexcept = 0;
/** Writes the given byte to the given address. */
virtual void writebyte(Addr, uint8_t) noexcept = 0;
/** Returns the total capacity of the dictionary in bytes. */
virtual unsigned long int capacity() const noexcept = 0;
/**
* Initializes essential dictionary values.
* Must be called before dictionary use.
*/
void initialize();
/**
* Gets the address stored in `here`.
*/
LIBALEE_SECTION
Addr here() const noexcept { return read(Here); }
/**
* Sets the address stored in `here`.
*/
LIBALEE_SECTION
void here(Addr l) noexcept { write(Here, l); }
/**
* Gets the value of `latest`.
*/
LIBALEE_SECTION
Addr latest() const noexcept { return read(Latest); }
/**
* Sets the value of `latest`.
*/
LIBALEE_SECTION
void latest(Addr l) noexcept { write(Latest, l); }
/**
* Aligns the given address to the next Cell boundary if necessary.
* @param addr The address to align.
* @return The resulting aligned address.
*/
static Addr aligned(Addr addr);
/**
* Aligns `here` to the next Cell boundary if necessary.
* @return The new aligned address stored in `here`.
*/
Addr alignhere() noexcept;
/**
* Allocates memory by returning and then increasing the current `here`.
* @param count The number of bytes to increase `here` by.
* @return The address stored in `here` before the increase.
*/
Addr allot(Cell count) noexcept;
/**
* Stores the given value to `here` then calls allot to "save" that cell.
* @param value The value to store.
* @see allot(Cell)
*/
void add(Cell value) noexcept;
/**
* Stores the beginning of a new word definition in the dictionary.
* The word must eventually have its definition concluded via semicolon.
* @param word The dictionary-stored name of the new word.
*/
void addDefinition(Word word) noexcept;
void addNativeWord(const char *s, void (*func)(State&));
/**
* Searches the dictionary for an entry for the given word.
* @param word The dictionary-stored word to search for.
* @return The beginning address of the word or zero if not found.
*/
Addr find(Word word) noexcept;
/**
* Produces the execution token for the given dictionary entry.
* @param addr The beginning address of a defined word.
* @return The execution token for the given word.
* @see find(Word)
*/
Addr getexec(Addr addr) noexcept;
/**
* Reads the next word from the input buffer.
* @return The next word or an empty word if one is not available.
*/
Word input() noexcept;
/**
* Returns true if the dictionary's input buffer has immediately available
* data.
*/
bool hasInput() const noexcept;
/**
* Checks if the dictionary-stored word is equivalent to the given string.
* @param word Dictionary-stored word to compare against.
* @param str String to compare to.
* @param size Size of the string to compare to.
* @return True if the two words are equivalent.
*/
bool equal(Word word, const char *str, unsigned size) const noexcept;
/**
* Checks if two words stored in this dictionary are equivalent.
* @param word1 First word to compare
* @param word2 Second word to compare
* @return True if the words are equivalent.
*/
bool equal(Word word1, Word word2) const noexcept;
/**
* Generic equality comparison using our own case-insensitive comparator.
* Arguments and return value identical to std::equal.
*/
template<typename Iter1, typename Iter2>
LIBALEE_SECTION
constexpr static bool equal(Iter1 b1, Iter1 e1, Iter2 b2) {
return std::equal(b1, e1, b2, eqchars);
}
virtual ~Dictionary() {};
private:
/**
* Case-insensitive character comparison used for dictionary lookup.
* @return True if the characters are equivalent.
*/
LIBALEE_SECTION
constexpr static bool eqchars(char c1, char c2) {
if (isalpha(static_cast<uint8_t>(c1)))
c1 |= 32;
if (isalpha(static_cast<uint8_t>(c2)))
c2 |= 32;
return c1 == c2;
}
};
#endif // ALEEFORTH_DICTIONARY_HPP
|