Running Alee without `core.fth` or `core-ext.fth` passed as arguments will leave you with a minimal word set. The `standalone` target will package the `core.fth` dictionary into the program.
**Missing** core features:
-* Pictured numeric output conversion `<# #>`
+* Pictured numeric output conversion (e.g. `<# #>`)
* Words for unsigned integers: `U. U< UM* UM/MOD`
* `>NUMBER`
* `FIND`
## Building
-Alee requires `make` and a C++17-compatible compiler.
+Alee requires `make` and a C++17-compatible compiler. Simply running `make` will produce the `libalee.a` library and a REPL binary named `alee`. Note that this binary has no built-in libraries; these can be passed in by calling `./alee core.fth core-ext.fth`.
-To compile, simply run the `make` command. This will produce a library, `libalee.a`, as well as a REPL binary named `alee`.
-A `small` target exists that optimizes the build for size.
-A `fast` target exists that optimizes for maximum performance on the host system.
-The `standalone` target will produce a `alee-standalone` binary that has the core dictionary built in.
-The `msp430` target builds Alee for the [MSP430G2553](https://www.ti.com/product/MSP430G2553) microcontroller. This target requires `standalone` for the core dictionary.
+There are other build targets:
+
+* `small`: Optimize for minimal binary size.
+* `fast`: Optimize for maximum performance on the host system.
+* `standalone`: Builds the core dictionary (`core.fth`) into the binary.
+* `msp430`: Builds a binary for the [MSP430G2553](https://www.ti.com/product/MSP430G2553) microcontroller. The `standalone` target must be built first for the core dictionary.
+
+If building for a new platform, see `Makefile`, `types.hpp`, and `state.hpp` for available configuration options.
-Configurable constants and types are defined either in the Makefile or in `types.hpp`.
auto word = getword(state);
if (auto j = state.dict.find(word); j > 0) {
state.push(state.dict.getexec(j));
- auto imm = state.dict.read(j) & CoreWords::Immediate;
+ auto imm = state.dict.read(j) & Dictionary::Immediate;
state.push(imm ? 1 : -1);
} else if (auto i = CoreWords::findi(state, word); i >= 0) {
state.push(i);
#include "types.hpp"
#include "state.hpp"
+/**
+ * To be implemented by the user, this function is called when the `sys` word
+ * is executed.
+ */
void user_sys(State&);
class CoreWords
{
public:
constexpr static std::size_t WordCount = 33;
-
- constexpr static Cell Immediate = (1 << 5);
-
constexpr static int Semicolon = 26;
+ /**
+ * Finds execution token that corresponds to the given word.
+ * Returns -1 if not found.
+ */
static int findi(const char *);
static int findi(State&, Word);
+
+ /**
+ * Executes the given CoreWord execution token using the given state.
+ */
static void run(unsigned int, State&);
constexpr static char wordsarr[] =
#ifndef ALEEFORTH_CTYPE_HPP
#define ALEEFORTH_CTYPE_HPP
+/**
+ * We implement our own character comparison functions to keep them lean.
+ */
+
#include <cstdint>
bool isspace(uint8_t);
class Dictionary
{
public:
+ /**
+ * The beginning of the dictionary is used for "internal" variables.
+ */
constexpr static Addr Base = 0;
constexpr static Addr Here = sizeof(Cell);
constexpr static Addr Latest = sizeof(Cell) * 2;
constexpr static Addr InputCells = 80; // bytes!
constexpr static Addr Begin = sizeof(Cell) * 7 + InputCells;
+ constexpr static Cell Immediate = (1 << 5);
+
+ /**
+ * Dictionary data can be stored on any read-write interface.
+ * You must create a dictionary class that inherits Dictionary and
+ * implement these functions. See `memdict.hpp` for a simple block-of-
+ * memory implementation.
+ */
+ virtual Cell read(Addr) const noexcept = 0;
+ virtual void write(Addr, Cell) noexcept = 0;
+ virtual uint8_t readbyte(Addr) const noexcept = 0;
+ virtual void writebyte(Addr, uint8_t) noexcept = 0;
+
+ /**
+ * Does initial dictionary setup, required before use for execution.
+ */
void initialize();
Addr here() const noexcept { return read(Here); }
Addr latest() const noexcept { return read(Latest); }
void latest(Addr l) noexcept { write(Latest, l); }
- virtual Cell read(Addr) const noexcept = 0;
- virtual void write(Addr, Cell) noexcept = 0;
- virtual uint8_t readbyte(Addr) const noexcept = 0;
- virtual void writebyte(Addr, uint8_t) noexcept = 0;
-
- Addr alignhere() noexcept;
+ // Aligns the given address.
Addr aligned(Addr) const noexcept;
+ // Aligns `here`.
+ Addr alignhere() noexcept;
+ // Advances `here` by the given number of bytes.
Addr allot(Cell) noexcept;
+ // Stores value to `here`, then adds sizeof(Cell) to `here`.
void add(Cell) noexcept;
+
+ /**
+ * Uses add() to store a new definition entry starting at `here`.
+ * The entry does not become active until a semicolon is executed.
+ */
void addDefinition(Word) noexcept;
+ /**
+ * Searches the dictionary for an entry for the given word.
+ * Returns zero if not found.
+ */
Addr find(Word) noexcept;
+
+ /**
+ * Given the address of a dictionary entry, produces the execution token
+ * for that entry.
+ */
Addr getexec(Addr) noexcept;
+
+ /**
+ * Reads the next word from the input buffer.
+ * Returns an empty word if the buffer is empty or entirely read.
+ */
Word input() noexcept;
+ /**
+ * Checks if this dictionary's word is equivalent to the given string/size.
+ */
bool equal(Word, const char *, unsigned) const noexcept;
+
+ /**
+ * Checks if two words in this dictionary's word are equivalent.
+ */
bool equal(Word, Word) const noexcept;
+ // Used for case-insensitive comparison between two iterators.
template<typename Iter1, typename Iter2>
static bool equal(Iter1 b1, Iter1 e1, Iter2 b2) {
return std::equal(b1, e1, b2, eqchars);
virtual ~Dictionary() = default;
private:
+ // Case-insensitive comparison.
static bool eqchars(char c1, char c2);
};
else
imm = ins == CoreWords::Semicolon;
} else {
- imm = state.dict.read(ins) & CoreWords::Immediate;
+ imm = state.dict.read(ins) & Dictionary::Immediate;
ins = state.dict.getexec(ins);
}
class Parser
{
public:
+ /**
+ * Parses (and evaluates) the given string using the given state.
+ * The string is stored in the state's input buffer, then parseSource()
+ * works through that using parseWord(). parseWord() will compile or
+ * execute as necessary.
+ */
static Error parse(State&, const char *);
+
+ /**
+ * Parses (and evaluates) through the words stored in the state's input
+ * buffer.
+ */
static Error parseSource(State&);
private:
+ /**
+ * Parses the given word using the given state.
+ */
static Error parseWord(State&, Word);
+
+ /**
+ * Attempts to parse the given word into a number.
+ */
static Error parseNumber(State&, Word);
};
public:
Addr ip = 0;
Dictionary& dict;
- void (*input)(State&);
-
- std::jmp_buf jmpbuf = {};
+ void (*input)(State&); // User-provided function to collect "stdin" input.
+ std::jmp_buf jmpbuf = {}; // Used when catching execution errors.
constexpr State(Dictionary& d, void (*i)(State&)):
dict(d), input(i) {}
bool compiling() const;
void compiling(bool);
+ /**
+ * Saves execution state so that a new execution can begin.
+ * Used for EVALUATE.
+ */
std::pair<Addr, std::jmp_buf> save();
+
+ /**
+ * Reloads the given execution state.
+ */
void load(const std::pair<Addr, std::jmp_buf>&);
+ /**
+ * Begins execution at the given execution token.
+ * If the token is a CoreWord, this function exits after its execution.
+ * Otherwise, execution continues until the word's execution completes.
+ * Encountering an error will cause this function to exit immediately.
+ */
Error execute(Addr);
+
+ /**
+ * Clears the data and return stacks, sets ip to zero, and clears the
+ * compiling flag.
+ */
void reset();
std::size_t size() const noexcept;
return *--dsp;
}
- inline Cell beyondip() {
- ip += sizeof(Cell);
- return dict.read(ip);
- }
-
inline void pushr(Cell value) {
if (rsp == rstack + ReturnStackSize)
std::longjmp(jmpbuf, static_cast<int>(Error::pushr));
return *(dsp - i - 1);
}
+ // Advances the instruction pointer and returns that cell's contents.
+ inline Cell beyondip() {
+ ip += sizeof(Cell);
+ return dict.read(ip);
+ }
+
private:
Cell dstack[DataStackSize] = {};
Cell rstack[ReturnStackSize] = {};
Cell *dsp = dstack;
Cell *rsp = rstack;
-
};
#endif // ALEEFORTH_STATE_HPP
#include <cstdint>
#include <iterator>
-struct Dictionary;
-struct State;
-
+/**
+ * Configure the below three types to match your platform.
+ */
using Addr = uint16_t;
using Cell = int16_t;
using DoubleCell = int32_t;
-using Func = void (*)(State&);
-constexpr unsigned int MaxCellNumberChars = 6; // -32768
+struct Dictionary;
+struct State;
+
+using Func = void (*)(State&);
enum class Error : int {
none = 0,
noword
};
+/**
+ * Stores the start and (past-the-)end addresses of a dictionary's word.
+ */
struct Word
{
struct iterator;
Addr start = 0;
Addr wend = 0;
+ unsigned size() const noexcept;
+
+ // Iterators provided for std::equal.
iterator begin(const Dictionary *);
iterator end(const Dictionary *);
- unsigned size() const noexcept;
struct iterator {
using iterator_category = std::input_iterator_tag;