]> code.bitgloo.com Git - bitgloo/alee-forth.git/commitdiff
update documentation
authorClyne Sullivan <clyne@bitgloo.com>
Sat, 11 Mar 2023 15:21:51 +0000 (10:21 -0500)
committerClyne Sullivan <clyne@bitgloo.com>
Sat, 11 Mar 2023 15:21:51 +0000 (10:21 -0500)
README.md
libalee/corewords.cpp
libalee/corewords.hpp
libalee/ctype.hpp
libalee/dictionary.hpp
libalee/parser.cpp
libalee/parser.hpp
libalee/state.hpp
libalee/types.hpp

index 2eade1ca1a1516b9f1e9b1b22af1c6007f1df5c2..d262a87dc4d03c3f4510276459b19ab5fd48e0df 100644 (file)
--- a/README.md
+++ b/README.md
@@ -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.
 
 **Missing** core features:  
-* Pictured numeric output conversion `<# #>`
+* Pictured numeric output conversion (e.g. `<# #>`)
 * Words for unsigned integers: `U. U< UM* UM/MOD`
 * `>NUMBER`
 * `FIND`
@@ -31,12 +31,14 @@ Alee aims for compliance with common Forth standards like Forth 2012 and ANS For
 
 ## 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`.
index b8a415e81e3a6b7cb261feee288c9b797c5b28ca..1a099369baeb55b2c293c4c006e6182318a56e14 100644 (file)
@@ -44,7 +44,7 @@ void tick(State& state)
     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);
index 33f4fc573c256ad405d3a5e7ec6ce0fd11e187ef..5798f598e265a163ece05460ab2f218d8738fd28 100644 (file)
 #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[] =
index d1a1d0b83f8801df6072d3a85bf2e92b057c4d0c..dfc6f50e79f280c38035f85935a3a70781dad127 100644 (file)
 #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);
index 53172d22b0e5bccd7f5690584faf63732a7c5a4b..02fb1cce81628b17f0e8f544f6b5acdbd700abdd 100644 (file)
@@ -39,6 +39,9 @@
 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;
@@ -49,6 +52,22 @@ public:
     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); }
@@ -57,24 +76,50 @@ public:
     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);
@@ -83,6 +128,7 @@ public:
     virtual ~Dictionary() = default;
 
 private:
+    // Case-insensitive comparison.
     static bool eqchars(char c1, char c2);
 };
 
index 39695c44b4a0aa434bb5c554da06a8a42bcddf03..9981b3e6bf581854a0a6c2f0c0a5deb918329acb 100644 (file)
@@ -65,7 +65,7 @@ Error Parser::parseWord(State& state, Word word)
         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);
     }
 
index be6f3f79a019fa4013394d8307565bbf4d8114a2..6af3ef9fbe82794d415c4e198acdd40f391cc516 100644 (file)
 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);
 };
 
index 325f405f6b8b20185a7f0acfb7be3f7478bc993f..4c74f9ae1b45cd29d6996261f3cac7c51b5d5dee 100644 (file)
@@ -34,9 +34,8 @@ class State
 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) {}
@@ -44,10 +43,29 @@ public:
     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;
@@ -65,11 +83,6 @@ public:
         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));
@@ -94,12 +107,17 @@ public:
         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
index 0a7f7ee0c1ecd6f69bea4d52de684b1f225b4a21..29c93e8998068d2e2ab316efebb065729be6b8ea 100644 (file)
 #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,
@@ -44,6 +46,9 @@ enum class Error : int {
     noword
 };
 
+/**
+ * Stores the start and (past-the-)end addresses of a dictionary's word.
+ */
 struct Word
 {
     struct iterator;
@@ -51,9 +56,11 @@ struct Word
     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;