update documentation

llvm
Clyne 2 years ago
parent bc3e952b48
commit e41b124320
Signed by: clyne
GPG Key ID: 3267C8EBF3F9AFC7

@ -17,7 +17,7 @@ Alee implements a large majority of the "core" and "core extension" [word sets](
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. 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: **Missing** core features:
* Pictured numeric output conversion `<# #>` * Pictured numeric output conversion (e.g. `<# #>`)
* Words for unsigned integers: `U. U< UM* UM/MOD` * Words for unsigned integers: `U. U< UM* UM/MOD`
* `>NUMBER` * `>NUMBER`
* `FIND` * `FIND`
@ -31,12 +31,14 @@ Alee aims for compliance with common Forth standards like Forth 2012 and ANS For
## Building ## 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`. There are other build targets:
A `small` target exists that optimizes the build for size.
A `fast` target exists that optimizes for maximum performance on the host system. * `small`: Optimize for minimal binary size.
The `standalone` target will produce a `alee-standalone` binary that has the core dictionary built in. * `fast`: Optimize for maximum performance on the host system.
The `msp430` target builds Alee for the [MSP430G2553](https://www.ti.com/product/MSP430G2553) microcontroller. This target requires `standalone` for the core dictionary. * `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`.

@ -44,7 +44,7 @@ void tick(State& state)
auto word = getword(state); auto word = getword(state);
if (auto j = state.dict.find(word); j > 0) { if (auto j = state.dict.find(word); j > 0) {
state.push(state.dict.getexec(j)); 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); state.push(imm ? 1 : -1);
} else if (auto i = CoreWords::findi(state, word); i >= 0) { } else if (auto i = CoreWords::findi(state, word); i >= 0) {
state.push(i); state.push(i);

@ -22,19 +22,28 @@
#include "types.hpp" #include "types.hpp"
#include "state.hpp" #include "state.hpp"
/**
* To be implemented by the user, this function is called when the `sys` word
* is executed.
*/
void user_sys(State&); void user_sys(State&);
class CoreWords class CoreWords
{ {
public: public:
constexpr static std::size_t WordCount = 33; constexpr static std::size_t WordCount = 33;
constexpr static Cell Immediate = (1 << 5);
constexpr static int Semicolon = 26; 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(const char *);
static int findi(State&, Word); static int findi(State&, Word);
/**
* Executes the given CoreWord execution token using the given state.
*/
static void run(unsigned int, State&); static void run(unsigned int, State&);
constexpr static char wordsarr[] = constexpr static char wordsarr[] =

@ -19,6 +19,10 @@
#ifndef ALEEFORTH_CTYPE_HPP #ifndef ALEEFORTH_CTYPE_HPP
#define ALEEFORTH_CTYPE_HPP #define ALEEFORTH_CTYPE_HPP
/**
* We implement our own character comparison functions to keep them lean.
*/
#include <cstdint> #include <cstdint>
bool isspace(uint8_t); bool isspace(uint8_t);

@ -39,6 +39,9 @@
class Dictionary class Dictionary
{ {
public: public:
/**
* The beginning of the dictionary is used for "internal" variables.
*/
constexpr static Addr Base = 0; constexpr static Addr Base = 0;
constexpr static Addr Here = sizeof(Cell); constexpr static Addr Here = sizeof(Cell);
constexpr static Addr Latest = sizeof(Cell) * 2; constexpr static Addr Latest = sizeof(Cell) * 2;
@ -49,6 +52,22 @@ public:
constexpr static Addr InputCells = 80; // bytes! constexpr static Addr InputCells = 80; // bytes!
constexpr static Addr Begin = sizeof(Cell) * 7 + InputCells; 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(); void initialize();
Addr here() const noexcept { return read(Here); } Addr here() const noexcept { return read(Here); }
@ -57,24 +76,50 @@ public:
Addr latest() const noexcept { return read(Latest); } Addr latest() const noexcept { return read(Latest); }
void latest(Addr l) noexcept { write(Latest, l); } void latest(Addr l) noexcept { write(Latest, l); }
virtual Cell read(Addr) const noexcept = 0; // Aligns the given address.
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;
Addr aligned(Addr) const noexcept; Addr aligned(Addr) const noexcept;
// Aligns `here`.
Addr alignhere() noexcept;
// Advances `here` by the given number of bytes.
Addr allot(Cell) noexcept; Addr allot(Cell) noexcept;
// Stores value to `here`, then adds sizeof(Cell) to `here`.
void add(Cell) noexcept; 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; void addDefinition(Word) noexcept;
/**
* Searches the dictionary for an entry for the given word.
* Returns zero if not found.
*/
Addr find(Word) noexcept; Addr find(Word) noexcept;
/**
* Given the address of a dictionary entry, produces the execution token
* for that entry.
*/
Addr getexec(Addr) noexcept; 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; Word input() noexcept;
/**
* Checks if this dictionary's word is equivalent to the given string/size.
*/
bool equal(Word, const char *, unsigned) const noexcept; 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; bool equal(Word, Word) const noexcept;
// Used for case-insensitive comparison between two iterators.
template<typename Iter1, typename Iter2> template<typename Iter1, typename Iter2>
static bool equal(Iter1 b1, Iter1 e1, Iter2 b2) { static bool equal(Iter1 b1, Iter1 e1, Iter2 b2) {
return std::equal(b1, e1, b2, eqchars); return std::equal(b1, e1, b2, eqchars);
@ -83,6 +128,7 @@ public:
virtual ~Dictionary() = default; virtual ~Dictionary() = default;
private: private:
// Case-insensitive comparison.
static bool eqchars(char c1, char c2); static bool eqchars(char c1, char c2);
}; };

@ -65,7 +65,7 @@ Error Parser::parseWord(State& state, Word word)
else else
imm = ins == CoreWords::Semicolon; imm = ins == CoreWords::Semicolon;
} else { } else {
imm = state.dict.read(ins) & CoreWords::Immediate; imm = state.dict.read(ins) & Dictionary::Immediate;
ins = state.dict.getexec(ins); ins = state.dict.getexec(ins);
} }

@ -26,11 +26,29 @@
class Parser class Parser
{ {
public: 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 *); static Error parse(State&, const char *);
/**
* Parses (and evaluates) through the words stored in the state's input
* buffer.
*/
static Error parseSource(State&); static Error parseSource(State&);
private: private:
/**
* Parses the given word using the given state.
*/
static Error parseWord(State&, Word); static Error parseWord(State&, Word);
/**
* Attempts to parse the given word into a number.
*/
static Error parseNumber(State&, Word); static Error parseNumber(State&, Word);
}; };

@ -34,9 +34,8 @@ class State
public: public:
Addr ip = 0; Addr ip = 0;
Dictionary& dict; Dictionary& dict;
void (*input)(State&); void (*input)(State&); // User-provided function to collect "stdin" input.
std::jmp_buf jmpbuf = {}; // Used when catching execution errors.
std::jmp_buf jmpbuf = {};
constexpr State(Dictionary& d, void (*i)(State&)): constexpr State(Dictionary& d, void (*i)(State&)):
dict(d), input(i) {} dict(d), input(i) {}
@ -44,10 +43,29 @@ public:
bool compiling() const; bool compiling() const;
void compiling(bool); void compiling(bool);
/**
* Saves execution state so that a new execution can begin.
* Used for EVALUATE.
*/
std::pair<Addr, std::jmp_buf> save(); std::pair<Addr, std::jmp_buf> save();
/**
* Reloads the given execution state.
*/
void load(const std::pair<Addr, std::jmp_buf>&); 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); Error execute(Addr);
/**
* Clears the data and return stacks, sets ip to zero, and clears the
* compiling flag.
*/
void reset(); void reset();
std::size_t size() const noexcept; std::size_t size() const noexcept;
@ -65,11 +83,6 @@ public:
return *--dsp; return *--dsp;
} }
inline Cell beyondip() {
ip += sizeof(Cell);
return dict.read(ip);
}
inline void pushr(Cell value) { inline void pushr(Cell value) {
if (rsp == rstack + ReturnStackSize) if (rsp == rstack + ReturnStackSize)
std::longjmp(jmpbuf, static_cast<int>(Error::pushr)); std::longjmp(jmpbuf, static_cast<int>(Error::pushr));
@ -94,12 +107,17 @@ public:
return *(dsp - i - 1); 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: private:
Cell dstack[DataStackSize] = {}; Cell dstack[DataStackSize] = {};
Cell rstack[ReturnStackSize] = {}; Cell rstack[ReturnStackSize] = {};
Cell *dsp = dstack; Cell *dsp = dstack;
Cell *rsp = rstack; Cell *rsp = rstack;
}; };
#endif // ALEEFORTH_STATE_HPP #endif // ALEEFORTH_STATE_HPP

@ -22,15 +22,17 @@
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
struct Dictionary; /**
struct State; * Configure the below three types to match your platform.
*/
using Addr = uint16_t; using Addr = uint16_t;
using Cell = int16_t; using Cell = int16_t;
using DoubleCell = int32_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 { enum class Error : int {
none = 0, none = 0,
@ -44,6 +46,9 @@ enum class Error : int {
noword noword
}; };
/**
* Stores the start and (past-the-)end addresses of a dictionary's word.
*/
struct Word struct Word
{ {
struct iterator; struct iterator;
@ -51,9 +56,11 @@ struct Word
Addr start = 0; Addr start = 0;
Addr wend = 0; Addr wend = 0;
unsigned size() const noexcept;
// Iterators provided for std::equal.
iterator begin(const Dictionary *); iterator begin(const Dictionary *);
iterator end(const Dictionary *); iterator end(const Dictionary *);
unsigned size() const noexcept;
struct iterator { struct iterator {
using iterator_category = std::input_iterator_tag; using iterator_category = std::input_iterator_tag;

Loading…
Cancel
Save