diff --git a/.gitignore b/.gitignore index fa983e1..fb13a21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .* *.o alee +libalee.a diff --git a/Makefile b/Makefile index b34026a..36e299d 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,21 @@ -CXXFLAGS += -g3 -ggdb -O0 +CXXFLAGS += -std=c++17 -g3 -ggdb -O0 -CXXFILES := corewords.cpp types.cpp +CXXFILES := corewords.cpp dictionary.cpp executor.cpp parser.cpp state.cpp \ + types.cpp OBJFILES := $(subst .cpp,.o,$(CXXFILES)) +LIBFILE := libalee.a EXEFILE := alee -all: alee +all: $(EXEFILE) -alee: $(OBJFILES) +small: CXXFLAGS += -Os +small: $(EXEFILE) + +$(EXEFILE): $(LIBFILE) + +$(LIBFILE): $(OBJFILES) + $(AR) cr $@ $(OBJFILES) clean: - rm -f alee $(OBJFILES) + rm -f $(EXEFILE) $(LIBFILE) $(OBJFILES) diff --git a/alee.cpp b/alee.cpp index b7858e6..1232aa6 100644 --- a/alee.cpp +++ b/alee.cpp @@ -16,8 +16,8 @@ * along with this program. If not, see . */ +#include "alee.hpp" #include "memdict.hpp" -#include "parser.hpp" #include #include diff --git a/alee.hpp b/alee.hpp new file mode 100644 index 0000000..f091cf8 --- /dev/null +++ b/alee.hpp @@ -0,0 +1,3 @@ +#include "parser.hpp" +#include "state.hpp" + diff --git a/corewords.hpp b/corewords.hpp index ff9d84d..54e02f1 100644 --- a/corewords.hpp +++ b/corewords.hpp @@ -34,9 +34,9 @@ public: 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); + static int findi(std::string_view); + static Func find(std::string_view); + static void run(int, State&); private: constexpr static char wordsarr[] = @@ -48,40 +48,40 @@ private: ";\0here\0exit\0imm\0const\0"; // lit, jmp, jmp0, ', lits - static Func get(int index); + static Func get(int); - 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); + static int op_drop(State&); + static int op_dup(State&); + static int op_swap(State&); + static int op_pick(State&); + static int op_sys(State&); + static int op_add(State&); + static int op_sub(State&); + static int op_mul(State&); + static int op_div(State&); + static int op_mod(State&); + static int op_peek(State&); + static int op_poke(State&); + static int op_rot(State&); + static int op_pushr(State&); + static int op_popr(State&); + static int op_eq(State&); + static int op_lt(State&); + static int op_allot(State&); + static int op_and(State&); + static int op_or(State&); + static int op_xor(State&); + static int op_shl(State&); + static int op_shr(State&); + static int op_comment(State&); + static int op_colon(State&); + static int op_semic(State&); + static int op_here(State&); + static int op_exit(State&); + static int op_imm(State&); + static int op_const(State&); + static int op_literal(State&); + static int op_jump(State&); }; #endif // ALEEFORTH_COREWORDS_HPP diff --git a/dictionary.cpp b/dictionary.cpp new file mode 100644 index 0000000..dced709 --- /dev/null +++ b/dictionary.cpp @@ -0,0 +1,80 @@ +/** + * 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 "dictionary.hpp" + +Addr Dictionary::allot(Cell amount) +{ + Addr old = here; + here += amount; + return old; +} + +void Dictionary::add(Cell value) +{ + write(here++, value); +} + +void Dictionary::addDefinition(std::string_view str) +{ + add(str.size()); + for (char c : str) + add(c); + + if (here & 1) + allot(1); +} + +bool Dictionary::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 Dictionary::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 Dictionary::getexec(Addr addr) +{ + const auto len = read(addr) & 0x1F; + return ((addr + 1 + len) + 1) & ~1; +} + diff --git a/dictionary.hpp b/dictionary.hpp index 880b8a5..add7fc3 100644 --- a/dictionary.hpp +++ b/dictionary.hpp @@ -24,67 +24,23 @@ #include #include -struct Dictionary +class Dictionary { +public: 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; - } + Addr allot(Cell); + void add(Cell); + void addDefinition(std::string_view); + Addr find(std::string_view); + Addr getexec(Addr); - 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; - } +private: + bool issame(Addr, std::string_view, std::size_t); }; #endif // ALEEFORTH_DICTIONARY_HPP diff --git a/executor.cpp b/executor.cpp new file mode 100644 index 0000000..30d43fc --- /dev/null +++ b/executor.cpp @@ -0,0 +1,34 @@ +/** + * 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" +#include "executor.hpp" + +int Executor::fullexec(State& state, Addr addr) +{ + state.pushr(0); + state.ip = addr - 1; + + do { + ++state.ip; + CoreWords::run(state.dict.read(state.ip), state); + } while (state.ip); + + return 0; +} + diff --git a/executor.hpp b/executor.hpp index 62afe5f..66c675b 100644 --- a/executor.hpp +++ b/executor.hpp @@ -19,22 +19,12 @@ #ifndef ALEEFORTH_EXECUTOR_HPP #define ALEEFORTH_EXECUTOR_HPP -#include "corewords.hpp" +#include "types.hpp" class Executor { public: - static int fullexec(State& state, Addr addr) { - state.pushr(0); - state.ip = addr - 1; - - do { - ++state.ip; - CoreWords::run(state.dict.read(state.ip), state); - } while (state.ip); - - return 0; - } + static int fullexec(State&, Addr); }; #endif // ALEEFORTH_EXECUTOR_HPP diff --git a/parser.cpp b/parser.cpp new file mode 100644 index 0000000..d57365b --- /dev/null +++ b/parser.cpp @@ -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 . + */ + +#include "corewords.hpp" +#include "executor.hpp" +#include "parser.hpp" + +#include + +ParseStatus Parser::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 (sub.empty()) + return ParseStatus::Finished; + + 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(";")); + CoreWords::run(CoreWords::findi(";"), state); + 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; + } +} + diff --git a/parser.hpp b/parser.hpp index c136990..b45a5d0 100644 --- a/parser.hpp +++ b/parser.hpp @@ -19,92 +19,14 @@ #ifndef ALEEFORTH_PARSER_HPP #define ALEEFORTH_PARSER_HPP -#include "executor.hpp" +#include "types.hpp" + +#include 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 (sub.empty()) - return ParseStatus::Finished; - - 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(";")); - CoreWords::run(CoreWords::findi(";"), state); - 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; - } - } + ParseStatus parse(State&, std::string_view&); }; #endif // ALEEFORTH_PARSER_HPP diff --git a/state.cpp b/state.cpp new file mode 100644 index 0000000..0b5c8fe --- /dev/null +++ b/state.cpp @@ -0,0 +1,79 @@ +/** + * 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 "state.hpp" + +#include + +Cell State::beyondip() const +{ + return dict.read(ip + 1); +} + +void State::pushr(Cell value) +{ + if (rsize() == ReturnStackSize) + throw; + *++rsp = value; +} + +Cell State::popr() +{ + if (rsize() == 0) + throw; + return *rsp--; +} + +void State::push(Cell value) +{ + if (size() == DataStackSize) + throw; + *++dsp = value; +} + +Cell State::pop() +{ + if (size() == 0) + throw; + return *dsp--; +} + +Cell& State::top() +{ + if (size() == 0) + throw; + return *dsp; +} + +Cell& State::pick(std::size_t i) +{ + if (i >= size()) + throw; + return *(dsp - i); +} + +std::size_t State::size() const noexcept +{ + return std::distance(dstack, static_cast(dsp)) + 1; +} + +std::size_t State::rsize() const noexcept +{ + return std::distance(rstack, static_cast(rsp)) + 1; +} + diff --git a/state.hpp b/state.hpp index e178877..6311949 100644 --- a/state.hpp +++ b/state.hpp @@ -23,7 +23,6 @@ #include "types.hpp" #include -#include constexpr unsigned DataStackSize = 12; constexpr unsigned ReturnStackSize = 12; @@ -43,53 +42,19 @@ public: constexpr State(Dictionary& d): dict(d) {} - Cell beyondip() const { - return dict.read(ip + 1); - } + Cell beyondip() const; - void pushr(Cell value) { - if (rsize() == ReturnStackSize) - throw; - *++rsp = value; - } + void pushr(Cell); + Cell popr(); - Cell popr() { - if (rsize() == 0) - throw; - return *rsp--; - } + void push(Cell); + Cell pop(); - void push(Cell value) { - if (size() == DataStackSize) - throw; - *++dsp = value; - } + Cell& top(); + Cell& pick(std::size_t); - 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; - } + std::size_t size() const noexcept; + std::size_t rsize() const noexcept; }; #endif // ALEEFORTH_STATE_HPP diff --git a/types.hpp b/types.hpp index 124f5ba..95f9ff1 100644 --- a/types.hpp +++ b/types.hpp @@ -43,7 +43,7 @@ enum class ParseStatus Error }; -std::string_view to_string(ParseStatus ps); +std::string_view to_string(ParseStatus); #endif // ALEEFORTH_TYPES_HPP