commit 56760f0517c646f41dd179e1f365d3268df3ec50 Author: Clyne Sullivan Date: Thu Feb 9 10:30:55 2023 -0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa983e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.* +*.o +alee diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5357f69 --- /dev/null +++ b/LICENSE @@ -0,0 +1,166 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b34026a --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CXXFLAGS += -g3 -ggdb -O0 + +CXXFILES := corewords.cpp types.cpp +OBJFILES := $(subst .cpp,.o,$(CXXFILES)) +EXEFILE := alee + +all: alee + +alee: $(OBJFILES) + +clean: + rm -f alee $(OBJFILES) + diff --git a/alee.cpp b/alee.cpp new file mode 100644 index 0000000..8ca209c --- /dev/null +++ b/alee.cpp @@ -0,0 +1,58 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#include "memdict.hpp" +#include "parser.hpp" + +#include + +// : variable create 0 , ; +// : create here constant ; +// : constant + +int main() +{ + MemDict dict; + State state (dict); + Parser parser; + + for (;;) { + std::string line; + std::cout << state.size() << ' ' << state.compiling << "> "; + std::getline(std::cin, line); + + ParseStatus r; + std::string_view lv (line); + do { + r = parser.parse(state, lv); + } while (r == ParseStatus::Continue); + + if (r != ParseStatus::Finished) + std::cout << "r " << to_string(r) << std::endl; + } + + return 0; +} + +int user_sys(State& state) +{ + const auto value = state.pop(); + std::cout << value << std::endl; + return 0; +} + diff --git a/corewords.cpp b/corewords.cpp new file mode 100644 index 0000000..6595e41 --- /dev/null +++ b/corewords.cpp @@ -0,0 +1,293 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#include "corewords.hpp" + +Func CoreWords::get(int index) +{ + switch (index) { + case 0: return op_drop; + case 1: return op_dup; + case 2: return op_swap; + case 3: return op_pick; + case 4: return op_sys; + case 5: return op_add; + case 6: return op_sub; + case 7: return op_mul; + case 8: return op_div; + case 9: return op_mod; + case 10: return op_peek; + case 11: return op_poke; + case 12: return op_rot; + case 13: return op_pushr; + case 14: return op_popr; + case 15: return op_eq; + case 16: return op_lt; + case 17: return op_allot; + case 18: return op_and; + case 19: return op_or; + case 20: return op_xor; + case 21: return op_shl; + case 22: return op_shr; + case 23: return op_comment; + case 24: return op_colon; + case 25: return op_semic; + case 26: return op_here; + case 27: return op_exit; + case 28: return op_imm; + case 29: return op_const; + case 30: return op_literal; + case 31: return op_jump; + default: return nullptr; + } +} + +int CoreWords::op_drop(State& state) +{ + state.pop(); + return 0; +} + +int CoreWords::op_dup(State& state) +{ + state.push(state.top()); + return 0; +} + +int CoreWords::op_swap(State& state) +{ + std::swap(state.top(), state.pick(1)); + return 0; +} + +int CoreWords::op_pick(State& state) +{ + state.push(state.pick(state.pop())); + return 0; +} + +int CoreWords::op_sys(State& state) +{ + return user_sys(state); +} + +int CoreWords::op_add(State& state) +{ + const auto a = state.pop(); + state.top() += a; + return 0; +} + +int CoreWords::op_sub(State& state) +{ + const auto a = state.pop(); + state.top() -= a; + return 0; +} + +int CoreWords::op_mul(State& state) { + const auto a = state.pop(); + state.top() *= a; + return 0; +} + +int CoreWords::op_div(State& state) { + const auto a = state.pop(); + state.top() /= a; + return 0; +} + +int CoreWords::op_mod(State& state) { + const auto a = state.pop(); + state.top() %= a; + return 0; +} + +int CoreWords::op_peek(State& state) { + state.push(state.dict.read(state.pop())); + return 0; +} + +int CoreWords::op_poke(State& state) { + const auto addr = state.pop(); + state.dict.write(addr, state.pop()); + return 0; +} + +int CoreWords::op_rot(State& state) { + std::swap(state.pick(2), state.pick(1)); + std::swap(state.pick(1), state.pick(0)); + return 0; +} + +int CoreWords::op_pushr(State& state) { + state.pushr(state.pop()); + return 0; +} + +int CoreWords::op_popr(State& state) { + state.push(state.popr()); + return 0; +} + +int CoreWords::op_eq(State& state) { + const auto a = state.pop(); + state.top() = state.top() == a; + return 0; +} + +int CoreWords::op_lt(State& state) { + const auto a = state.pop(); + state.top() = state.top() < a; + return 0; +} + +int CoreWords::op_allot(State& state) { + state.dict.allot(state.pop()); + return 0; +} + +int CoreWords::op_and(State& state) { + const auto a = state.pop(); + state.top() &= a; + return 0; +} + +int CoreWords::op_or(State& state) { + const auto a = state.pop(); + state.top() |= a; + return 0; +} + +int CoreWords::op_xor(State& state) { + const auto a = state.pop(); + state.top() ^= a; + return 0; +} + +int CoreWords::op_shl(State& state) { + const auto a = state.pop(); + state.top() <<= a; + return 0; +} + +int CoreWords::op_shr(State& state) { + const auto a = state.pop(); + state.top() >>= a; + return 0; +} + +int CoreWords::op_comment(State& state) { + state.pass = Pass::Comment; + return 0; +} + +int CoreWords::op_colon(State& state) { + state.pass = Pass::Colon; + state.pushr(state.dict.here); // this is for EXIT + return 0; +} + +int CoreWords::op_semic(State& state) { + if (!state.compiling) { + state.ip = state.popr(); + } else { + auto begin = state.popr(); + + state.dict.write(begin, + (state.dict.read(begin) & 0x1F) | + ((begin - state.dict.latest) << 6)); + + state.dict.latest = begin; + state.compiling = false; + } + + return 0; +} + +int CoreWords::op_here(State& state) { + state.push(state.dict.here); + return 0; +} + +int CoreWords::op_exit(State& state) +{ + state.ip = state.popr(); + return 0; +} + +int CoreWords::op_imm(State& state) +{ + state.dict.write(state.dict.latest, + state.dict.read(state.dict.latest) | Immediate); + return 0; +} + +int CoreWords::op_const(State& state) +{ + state.pass = Pass::Constant; + state.pushr(state.dict.here); + return 0; +} + +int CoreWords::op_literal(State& state) +{ + state.push(state.beyondip()); + ++state.ip; + return 0; +} + +int CoreWords::op_jump(State& state) +{ + state.pushr(state.ip + 1); + state.ip = state.beyondip() - 1; + return 0; +} + +int CoreWords::findi(std::string_view str) +{ + std::size_t i; + int wordsi = 0; + + std::string_view words (wordsarr, sizeof(wordsarr)); + + for (i = 0; i < words.size();) { + const auto end = words.find('\0', i); + + if (words.compare(i, end - i, str) == 0) + break; + + ++wordsi; + i = end + 1; + } + + return wordsi < VisibleWordCount ? wordsi : -1; +} + +Func CoreWords::find(std::string_view str) +{ + const auto i = findi(str); + return i >= 0 ? get(i) : nullptr; +} + +void CoreWords::run(int i, State& state) +{ + if (i >= 0 && i < WordCount) + get(i)(state); +} + diff --git a/corewords.hpp b/corewords.hpp new file mode 100644 index 0000000..08f172b --- /dev/null +++ b/corewords.hpp @@ -0,0 +1,88 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#ifndef ALEEFORTH_COREWORDS_HPP +#define ALEEFORTH_COREWORDS_HPP + +#include "types.hpp" +#include "state.hpp" + +int user_sys(State&); + +class CoreWords +{ +public: + constexpr static std::size_t VisibleWordCount = 30; // size + constexpr static std::size_t HiddenWordLiteral = 30; // index + constexpr static std::size_t HiddenWordJump = 31; // index + constexpr static std::size_t WordCount = 32; // size + + constexpr static Cell Immediate = (1 << 5); + + static int findi(std::string_view str); + static Func find(std::string_view str); + static void run(int i, State& state); + +private: + constexpr static char wordsarr[] = + "drop\0dup\0swap\0pick\0sys\0" + "+\0-\0*\0/\0%\0" + "@\0!\0rot\0>r\0r>\0" + "=\0<\0allot\0&\0|\0" + "^\0<<\0>>\0(\0:\0" + ";\0here\0exit\0imm\0const\0"; + // lit, jmp, jmp0, ', lits + + static Func get(int index); + + static int op_drop(State& state); + static int op_dup(State& state); + static int op_swap(State& state); + static int op_pick(State& state); + static int op_sys(State& state); + static int op_add(State& state); + static int op_sub(State& state); + static int op_mul(State& state); + static int op_div(State& state); + static int op_mod(State& state); + static int op_peek(State& state); + static int op_poke(State& state); + static int op_rot(State& state); + static int op_pushr(State& state); + static int op_popr(State& state); + static int op_eq(State& state); + static int op_lt(State& state); + static int op_allot(State& state); + static int op_and(State& state); + static int op_or(State& state); + static int op_xor(State& state); + static int op_shl(State& state); + static int op_shr(State& state); + static int op_comment(State& state); + static int op_colon(State& state); + static int op_semic(State& state); + static int op_here(State& state); + static int op_exit(State& state); + static int op_imm(State& state); + static int op_const(State& state); + static int op_literal(State& state); + static int op_jump(State& state); +}; + +#endif // ALEEFORTH_COREWORDS_HPP + diff --git a/dictionary.hpp b/dictionary.hpp new file mode 100644 index 0000000..c527603 --- /dev/null +++ b/dictionary.hpp @@ -0,0 +1,92 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#ifndef ALEEFORTH_DICTIONARY_HPP +#define ALEEFORTH_DICTIONARY_HPP + +#include "types.hpp" + +#include +#include + +struct Dictionary +{ + Addr here = 1; + Addr latest = 0; + + virtual Cell read(Addr) const = 0; + virtual int write(Addr, Cell) = 0; + + Addr allot(Cell amount) { + Addr old = here; + here += amount; + return old; + } + + void add(Cell value) { + write(here++, value); + } + + void addDefinition(std::string_view str) { + add(str.size()); + for (char c : str) + add(c); + + if (here & 1) + allot(1); + } + + bool issame(Addr addr, std::string_view str, std::size_t n) { + if (str.size() != n) + return false; + + for (char c : str) { + if (read(addr++) != c) + return false; + } + + return true; + } + + Addr find(std::string_view str) { + if (latest == 0) + return 0; + + auto lt = latest; + do { + const auto l = read(lt); + const auto len = l & 0x1F; + + if (issame(lt + 1, str, len)) { + return lt; + } else { + lt -= l >> 6; + } + } while (lt); + + return 0; + } + + Addr getexec(Addr addr) { + const auto len = read(addr) & 0x1F; + return ((addr + 1 + len) + 1) & ~1; + } +}; + +#endif // ALEEFORTH_DICTIONARY_HPP + diff --git a/executor.hpp b/executor.hpp new file mode 100644 index 0000000..485e82a --- /dev/null +++ b/executor.hpp @@ -0,0 +1,44 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#ifndef ALEEFORTH_EXECUTOR_HPP +#define ALEEFORTH_EXECUTOR_HPP + +#include "corewords.hpp" + +//#include + +class Executor +{ +public: + static int fullexec(State& state, Addr addr) { + state.pushr(0); + state.ip = addr - 1; + + do { + ++state.ip; + //std::cout << "-- " << state.rsize() << "e " << state.ip << std::endl; + CoreWords::run(state.dict.read(state.ip), state); + } while (state.ip); + + return 0; + } +}; + +#endif // ALEEFORTH_EXECUTOR_HPP + diff --git a/memdict.hpp b/memdict.hpp new file mode 100644 index 0000000..8606da2 --- /dev/null +++ b/memdict.hpp @@ -0,0 +1,42 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#ifndef ALEEFORTH_MEMDICT_HPP +#define ALEEFORTH_MEMDICT_HPP + +#include "dictionary.hpp" + +constexpr unsigned long int MemDictSize = 4096; + +class MemDict : public Dictionary +{ + Cell dict[MemDictSize]; + +public: + virtual Cell read(Addr addr) const final { + return dict[addr]; + } + + virtual int write(Addr addr, Cell value) final { + dict[addr] = value; + return 0; + } +}; + +#endif // ALEEFORTH_MEMDICT_HPP + diff --git a/parser.hpp b/parser.hpp new file mode 100644 index 0000000..a850cb8 --- /dev/null +++ b/parser.hpp @@ -0,0 +1,107 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#ifndef ALEEFORTH_PARSER_HPP +#define ALEEFORTH_PARSER_HPP + +#include "executor.hpp" + +class Parser +{ +public: + ParseStatus parse(State& state, std::string_view& str) { + const auto end = str.find_first_of(" \t\n\r"); + const auto sub = str.substr(0, end); + + if (state.pass != Pass::None) { + switch (state.pass) { + case Pass::Comment: + if (str.front() == ')') + state.pass = Pass::None; + + str = str.substr(1); + break; + case Pass::Colon: + state.pass = Pass::None; + state.compiling = true; + state.dict.addDefinition(sub); + break; + case Pass::Constant: + state.pass = Pass::None; + state.compiling = true; + state.dict.addDefinition(sub); + state.dict.add(CoreWords::HiddenWordLiteral); + state.dict.add(state.pop()); + state.dict.add(CoreWords::findi("exit")); + break; + default: + break; + } + } else { + if (auto i = CoreWords::findi(sub); i >= 0) { + if (state.compiling) + state.dict.add(i); + if (!state.compiling || sub.front() == ';') + CoreWords::run(i, state); + } else if (auto j = state.dict.find(sub); j > 0) { + auto e = state.dict.getexec(j); + if (state.compiling) { + if (state.dict.read(j) & CoreWords::Immediate) { + state.compiling = false; + Executor::fullexec(state, e); + state.compiling = true; + } else { + state.dict.add(CoreWords::HiddenWordJump); + state.dict.add(e); + } + } else { + Executor::fullexec(state, e); + } + } else { + char *p; + const auto l = static_cast(std::strtol(sub.data(), &p, 10)); + + if (p != sub.data()) { + if (state.compiling) { + state.dict.add(CoreWords::HiddenWordLiteral); + state.dict.add(l); + } else { + state.push(l); + } + } else { + return ParseStatus::Error; + } + } + + if (end == std::string_view::npos) + return ParseStatus::Finished; + } + + const auto next = str.find_first_not_of(" \t\n\r", end); + + if (next == std::string_view::npos) { + return ParseStatus::Finished; + } else { + str = str.substr(next); + return ParseStatus::Continue; + } + } +}; + +#endif // ALEEFORTH_PARSER_HPP + diff --git a/state.hpp b/state.hpp new file mode 100644 index 0000000..03d0084 --- /dev/null +++ b/state.hpp @@ -0,0 +1,96 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#ifndef ALEEFORTH_STATE_HPP +#define ALEEFORTH_STATE_HPP + +#include "dictionary.hpp" +#include "types.hpp" + +#include +#include + +constexpr unsigned DataStackSize = 12; +constexpr unsigned ReturnStackSize = 12; + +class State +{ + Cell dstack[DataStackSize] = {}; + Cell rstack[ReturnStackSize] = {}; + Cell *dsp = dstack - 1; + Cell *rsp = rstack - 1; + +public: + bool compiling = false; + Addr ip = 0; + Pass pass = Pass::None; + Dictionary& dict; + + constexpr State(Dictionary& d): dict(d) {} + + Cell beyondip() const { + return dict.read(ip + 1); + } + + void pushr(Cell value) { + if (rsize() == ReturnStackSize) + throw; + *++rsp = value; + } + + Cell popr() { + if (rsize() == 0) + throw; + return *rsp--; + } + + void push(Cell value) { + if (size() == DataStackSize) + throw; + *++dsp = value; + } + + Cell pop() { + if (size() == 0) + throw; + return *dsp--; + } + + Cell& top() { + if (size() == 0) + throw; + return *dsp; + } + + Cell& pick(std::size_t i) { + if (i >= size()) + throw; + return *(dsp - i); + } + + std::size_t size() const noexcept { + return std::distance(dstack, static_cast(dsp)) + 1; + } + + std::size_t rsize() const noexcept { + return std::distance(rstack, static_cast(rsp)) + 1; + } +}; + +#endif // ALEEFORTH_STATE_HPP + diff --git a/types.cpp b/types.cpp new file mode 100644 index 0000000..c1fc822 --- /dev/null +++ b/types.cpp @@ -0,0 +1,30 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#include "types.hpp" + +std::string_view to_string(ParseStatus ps) +{ + switch (ps) { + case ParseStatus::Finished: return "Finished"; + case ParseStatus::Continue: return "Continue"; + case ParseStatus::Error: return "Error"; + default: return "???"; + } +} + diff --git a/types.hpp b/types.hpp new file mode 100644 index 0000000..124f5ba --- /dev/null +++ b/types.hpp @@ -0,0 +1,49 @@ +/** + * Alee Forth: A portable and concise Forth implementation in modern C++. + * Copyright (C) 2023 Clyne Sullivan + * + * 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 . + */ + +#ifndef ALEEFORTH_TYPES_HPP +#define ALEEFORTH_TYPES_HPP + +#include +#include + +struct State; + +using Addr = uint16_t; +using Cell = int16_t; +using Func = int (*)(State&); + +enum class Pass +{ + None, + Comment, + Colon, + Constant +}; + +enum class ParseStatus +{ + Finished, + Continue, + Error +}; + +std::string_view to_string(ParseStatus ps); + +#endif // ALEEFORTH_TYPES_HPP +