Skip to content
/ FORTHEX Public

Forth interpreter with dual-stack VM, AST parsing, and memory simulation. Includes web REPL + Snake game.

License

Notifications You must be signed in to change notification settings

arturz/FORTHEX

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

    ___________  ____  ________  _______  __
   / ____/ __ \/ __ \/_  __/ / / / ____/ |/ /
  / /_  / / / / /_/ / / / / /_/ / __/  |   /
 / __/ / /_/ / _, _/ / / / __  / /___ /   |
/_/    \____/_/ |_| /_/ /_/ /_/_____//_/|_|

CI Coverage Status

A modern, AST-based Forth interpreter built from scratch in Elixir.

๐ŸŽฎ Live demo

Try it directly in your browser: forthex.fly.dev (Snake controls: WASD)

๐Ÿ’ก About

What is Forth?

Forth is a stack-based, procedural programming language using Reverse Polish Notation (RPN).

Why I built this?

This began as a university project and grew into a deeper engineering challenge: building a dual-stack VM with simulated memory, a proper AST parser, and clear separation of concerns, all backed by 100% test coverage.

๐Ÿš€ Quick start

Launch in browser (no installation)

Launch Forthex web REPL.

Note: loops in web REPL are intentionally slowed down. For maximum speed use Docker version.

Using Docker (recommended for local testing)

git clone https://github.com/arturz/forthex.git
cd forthex
docker build -t forthex .
docker run -it forthex

Using Elixir (local development)

Click to expand instructions

Prerequisites: Elixir 1.16+ installed.

# Fetch dependencies
mix deps.get

# Start the REPL
mix forthex

# Run a Forth script
mix forthex forth_scripts/calculator.forth

# Run Snake (requires iex for raw terminal mode)
iex -S mix forthex snake

๐Ÿ“ฆ Examples

๐Ÿ Snake Terminal game with WASD controls, collision detection, scoring.
๐ŸŒ€ Mandelbrot Interactive colorful fractal with navigation controls.
๐Ÿงฎ Calculator Interactive calculator with menu UI.
Mandelbrot

๐Ÿ“š Tutorial

New to Forth? It's easier than it looks! Forth uses a stack for everything. Imagine a stack of plates: you put numbers on top, and operations (like + or .) take them off.

1. Basic math

Type numbers to push them. Type . to print the top number.

1 2 + .

(push 1, push 2, add them, print result -> 3)

2. Stack manipulation

Words operate on the stack.

  • DUP: Duplicate top number (1 -> 1 1)
  • SWAP: Swap top two numbers (1 2 -> 2 1)
  • DROP: Discard top number
5 DUP * .

(5 -> 5 5 -> 25 -> print 25)

3. Defining words

Extend the language by defining new words with : name ... ;.

: SQUARE
  DUP * ;

5 SQUARE .

(define SQUARE, then use it to square 5)

Tip

The semicolon ; is a token and must be preceded by a space (e.g. : FOO ... ;).

4. Loops

Loops use DO ... LOOP. The limit is exclusive, start is inclusive.

: COUNT-DOWN
  11 1 DO
    I .
  LOOP ;

COUNT-DOWN

(define loop word, then run it to print 1 to 10)

๐Ÿ— Architecture

graph LR
    SC[Source code] --> L[Lexer]
    L --> T[Tokens]
    T --> P[Parser]
    P --> A[AST]
    A --> I[Interpreter]
    I --> R[Result]
    
    subgraph State [Interpreter state]
        I -.-> S[Stack]
        I -.-> RS[Return stack]
        I -.-> H[Heap]
        I -.-> D[Dictionary]
    end
Loading

Components

Module Purpose
Forthex.Lexer Tokenizes source into typed tokens (:word_opening, :if, :call_or_literal, etc.)
Forthex.Parser Recursive descent parser building AST nodes (IfExpression, DoLoop, WordDefinition)
Forthex.Interpreter Stack-based VM with pattern matching on AST nodes
Forthex.Interpreter.State Holds data stack, return stack, dictionary, and heap
Forthex.Interpreter.Heap Simulated memory with address allocation and bounds checking (implemented as an immutable map simulating mutable state)
Forthex.Interpreter.Dictionary Maps word names to either native Elixir functions or user-defined ASTs

Dual-stack model

  • Data stack - Primary operand stack for computations
  • Return stack - Used for loop indices (I, J) and control flow

๐Ÿ“‚ Project structure

lib/forthex/
โ”œโ”€โ”€ lexer.ex              # Tokenization
โ”œโ”€โ”€ parser.ex             # AST construction
โ”œโ”€โ”€ interpreter.ex        # VM execution
โ”œโ”€โ”€ runner.ex             # Pipeline orchestration
โ”œโ”€โ”€ ast/                  # AST node definitions
โ”‚   โ”œโ”€โ”€ if_expression.ex
โ”‚   โ”œโ”€โ”€ do_loop.ex
โ”‚   โ”œโ”€โ”€ word_definition.ex
โ”‚   โ””โ”€โ”€ ...
โ””โ”€โ”€ interpreter/
    โ”œโ”€โ”€ state.ex          # VM state container
    โ”œโ”€โ”€ heap.ex           # Memory simulation
    โ”œโ”€โ”€ dictionary.ex     # Word lookup
    โ””โ”€โ”€ dictionary/
        โ”œโ”€โ”€ stack_words.ex
        โ”œโ”€โ”€ math_words.ex
        โ”œโ”€โ”€ io_words.ex
        โ””โ”€โ”€ ...

forth_scripts/            # Example programs
โ”œโ”€โ”€ snake.forth
โ”œโ”€โ”€ mandelbrot.forth
โ””โ”€โ”€ calculator.forth

๐Ÿ›  Supported words

Stack

DUP DROP SWAP OVER ROT -ROT 2DUP 2DROP 2SWAP 2OVER CLEAR . .S

Arithmetic

+ - * / MOD ABS 1-

Comparison & logic

< > = <> <= >= AND OR NOT 0= TRUE FALSE WITHIN

Comments

( inline comment )
\ line comment

Control flow

IF ... THEN
IF ... ELSE ... THEN
DO ... LOOP
?DO ... LOOP        ( safe loop - skips if range empty )
BEGIN ... UNTIL

Loop indices: I (current), J (outer loop)

Memory

VARIABLE name       ( declare variable )
CREATE name N ALLOT ( allocate N cells )
!                   ( store: value addr -- )
@                   ( fetch: addr -- value )
+!                  ( add to: n addr -- )
2!  2@              ( store/fetch pairs )
CELLS               ( convert to cell units )
HERE                ( next free address )
FILL                ( fill memory: addr u char -- )
ERASE               ( zero memory: addr u -- )

I/O

.                   ( print number )
." text"            ( print string )
EMIT                ( print char by ASCII code )
CR                  ( newline )
SPACE               ( print single space )
SPACES              ( print N spaces )
KEY                 ( blocking read key )
KEY?                ( non-blocking key check )
ACCEPT              ( read line as number )
PAGE                ( clear screen )
AT-XY               ( move cursor: x y -- )
MS                  ( sleep milliseconds )
RESET-TERMINAL      ( exit raw (non-canonical) terminal mode )
BANNER              ( show startup banner )

Meta

: name ... ;        ( define new word )
INCLUDE file.forth  ( load external file )
['] word            ( get execution token )
EXECUTE             ( run execution token )
PERFORM             ( @ EXECUTE )
WORDS               ( list all words )
HELP word           ( show word info )
RANDOM              ( from to -- n )
BYE / EXIT          ( quit interpreter )

Built-in programs

SNAKE               ( run snake game )
CALCULATOR          ( run calculator )
MANDELBROT          ( run mandelbrot )

Example: loop with memory

CREATE scores 10 CELLS ALLOT

: INIT-SCORES ( -- )
  10 0 DO
    I 10 * I CELLS scores + !
  LOOP ;

: PRINT-SCORES ( -- )
  10 0 DO
    I CELLS scores + @ .
  LOOP ;

INIT-SCORES PRINT-SCORES  ( prints 0 10 20 30 40 50 60 70 80 90 )

๐Ÿงฉ Implementation notes

Unlike classic Forth, this interpreter separates parsing and execution: parsing builds an AST, and word calls and literals are resolved at runtime.

๐Ÿ“„ License

Distributed under the MIT License. See LICENSE for more information.


Built with โค๏ธ in Elixir. If you like this project, please give it a star! โญ๏ธ

About

Forth interpreter with dual-stack VM, AST parsing, and memory simulation. Includes web REPL + Snake game.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages