Skip to content

Commit

Permalink
support long definitions; add tests as submodule
Browse files Browse the repository at this point in the history
  • Loading branch information
tcsullivan committed Oct 25, 2023
1 parent 7381e87 commit 650a344
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 26 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "tests"]
path = tests
url = https://github.com/gerryjackson/forth2012-test-suite
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ cppcheck:
libalee alee*.cpp *dict.hpp

test: standalone
echo "\nbye\n" | ./alee-standalone forth/core-ext.fth forth/test/tester.fr forth/test/core.fr
echo "bye" | ./alee-standalone forth/core-ext.fth tests/src/tester.fr tests/src/core.fr

$(LIBFILE): $(OBJFILES)
$(AR) crs $@ $(OBJFILES)
Expand Down
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
# Alee Forth

Alee Forth is a concise Forth implementation written in modern C++ that aims for portability, minimal program size, and execution efficiency.
Alee Forth is a concise Forth implementation written in modern C++ that aims for portability, minimal memory footprint, and execution efficiency.

## Cross-platform compatibility

Alee Forth relies on the C++20 standard. It *does not* rely on any operating system. As a result, portability extends down to microcontroller targets with < 1 kB of memory. See the `msp430` folder for an example of such a port.
Alee Forth relies on the C++20 standard. It *does not* rely on any operating system. As a result, portability extends down to microcontroller targets with < 16kB flash and < 1 kB of RAM. See the `msp430` folder for an example of such a port.

System-specific functionality is obtained through a `sys` Forth word. This word calls a user-supplied C++ function that implements whatever functionality is needed.
System-specific functionality is achieved through a `sys` Forth word. This word calls a user-supplied C++ function that implements whatever functionality is needed.

## Forth compatibility

Alee implements the entire "core" and majority of the "core extension" [word-sets](https://forth-standard.org/standard/core). Implementation is tracked in `compat.txt` with missing words listed below. Fundamental words are hard-coded into Alee while the rest of the implementation is found in `forth/core.fth` and `forth/core-ext.fth`. Running Alee without these implementation files will leave you with a very minimal word-set. These files may be compiled into the Alee binary by building the `standalone` target.
Alee Forth uses the [Forth 2012 test suite](https://github.com/gerryjackson/forth2012-test-suite) to ensure standards compliance. The entire "core" [word-set](https://forth-standard.org/standard/core) is implemented as well as most of the "core extension" word-set. The compiled program contains a minimal set of fundamental words with libraries in the `forth` directory supplying these larger word-sets. The "core" word-set can be compiled into the program by building the `standalone` target.

**Missing** core extension words:

**Missing** core extensions:
```
.R HOLDS PAD PARSE PARSE-NAME REFILL RESTORE-INPUT S\" SAVE-INPUT SOURCE-ID U.R U> UNUSED WITHIN [COMPILE]
```

Alee aims for compliance with common Forth standards like Forth 2012 and ANS Forth. Compliance is tested using a [Forth 2012 test suite](https://github.com/gerryjackson/forth2012-test-suite). Supported test files are in the `test` directory with tests for unimplemented words commented out.

## Building

Alee requires `make` and a compiler that supports C++20. Simply running `make` will produce the `libalee.a` library and a REPL binary named `alee`. You will likely want to pass in the core implementation files by calling `./alee forth/core.fth forth/core-ext.fth`.
Alee requires `make` and a compiler that supports C++20. Simply running `make` will produce the `libalee.a` library and a REPL binary named `alee`. The core word-sets can be passed into `alee` via the command line: `./alee forth/core.fth forth/core-ext.fth`.

Other available build targets:

Expand All @@ -30,5 +29,5 @@ Other available build targets:
* `standalone`: Builds the core dictionary (`core.fth`) into the binary.
* `msp430-prep` and `msp430`: Builds a binary for the [MSP430G2553](https://www.ti.com/product/MSP430G2553) microcontroller. See the `msp430` folder for more information.

If building for a new platform, review these files: `Makefile`, `libalee/types.hpp`, and `libalee/state.hpp`.
If building for a new platform, review these files: `Makefile`, `libalee/types.hpp`, and `libalee/state.hpp`. It is possible to modify the implementation to use 32-bit words, but this will require re-writing the core word-sets.

13 changes: 7 additions & 6 deletions forth/core.fth
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,14 @@
swap postpone literal postpone literal ; imm
: ." postpone s" state @ if ['] type , else type then ; imm

: create align here
1 cells 1 chars - allot
bl word count swap drop
1 chars allot
swap over over ! swap allot align
: create align here dup _latest @ - 1 1 cells 8 * 6 - << 1- swap <=
dup if -1 6 << , then 0 , >r
begin key? if key else bl then dup bl <> while
c, 1 over +! repeat drop align
['] _lit , here 3 cells + , ['] exit dup , ,
dup @ 31 & over _latest @ - 6 << or over ! _latest ! ;
dup _latest @ - r> if
over cell+ else 6 << over then +! _latest ! ;

: _does> _latest @ dup @ 31 & + cell+ aligned 2 cells +
['] _jmp over ! cell+
r@ 1 cells - @ swap ! ;
Expand Down
10 changes: 8 additions & 2 deletions libalee/corewords.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,14 @@ void CoreWords::run(Cell ins, State& state)

cell = state.pop();
dcell = (cell - state.dict.latest()) << 6;
state.dict.write(cell,
(state.dict.read(cell) & 0x1F) | static_cast<Cell>(dcell));
if (dcell > (((1 << (sizeof(Cell) * 8 - 6)) - 1) << 6)) {
state.dict.write(cell,
(state.dict.read(cell) & 0x1F) | static_cast<Cell>(((1 << (sizeof(Cell) * 8 - 6)) - 1) << 6));
state.dict.write(static_cast<Addr>(cell) + sizeof(Cell), static_cast<Cell>(dcell >> 6));
} else {
state.dict.write(cell,
(state.dict.read(cell) & 0x1F) | static_cast<Cell>(dcell));
}
state.dict.latest(cell);
break;
case 27: // _jmp0
Expand Down
35 changes: 27 additions & 8 deletions libalee/dictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ void Dictionary::addDefinition(Word word) noexcept
Cell wsize = word.size();
add(wsize);

if (alignhere() - latest() >= ((1 << (sizeof(Cell) * 8 - 6)) - 1))
add(0);

auto addr = allot(wsize);
auto it = word.begin(this);
const auto end = word.end(this);
Expand All @@ -81,23 +84,39 @@ Addr Dictionary::find(Word word) noexcept
for (;;) {
const Addr l = read(lt);
const Addr len = l & 0x1F;
Word lw;

const auto lw = Word::fromLength(lt + sizeof(Cell), len);
if (equal(word, lw))
return lt;
else if (lt == Begin)
break;
else
lt -= l >> 6;
if ((l >> 6) < 1023) {
lw = Word::fromLength(lt + sizeof(Cell), len);
if (equal(word, lw))
return lt;
else if (lt == Begin)
break;
else
lt -= l >> 6;
} else {
lw = Word::fromLength(lt + 2 * sizeof(Cell), len);
if (equal(word, lw))
return lt;
else if (lt == Begin)
break;
else
lt -= static_cast<Addr>(read(lt + sizeof(Cell)));
}
}

return 0;
}

Addr Dictionary::getexec(Addr addr) noexcept
{
const Addr len = read(addr) & 0x1Fu;
const Addr l = read(addr);
const Addr len = l & 0x1Fu;

addr += sizeof(Cell);
if ((l >> 6) == 1023)
addr += sizeof(Cell);

addr += len;
return aligned(addr);
}
Expand Down
1 change: 1 addition & 0 deletions tests
Submodule tests added at 45b7ec

0 comments on commit 650a344

Please sign in to comment.