I/O Operations in Forth
Learn console and file I/O in Forth - EMIT, KEY, TYPE, ACCEPT, formatted numbers, and the ANS file word set with Docker-ready examples
Input and output in Forth follow the same rule as everything else in the language: I/O is performed by words that take their arguments from the data stack and leave their results there. There is no printf format string and no file object — instead you compose small words like EMIT, TYPE, and WRITE-LINE, feeding them addresses, lengths, and character codes through the stack.
This stack-oriented approach makes Forth I/O remarkably explicit. A string is not a first-class object; it is a pair of numbers on the stack — an address and a length ( addr len ). A character is just its numeric code. Once you internalize that data flows through the stack, the whole I/O vocabulary becomes a handful of composable words rather than a sprawling standard library.
In this tutorial you will learn how Forth writes characters and strings to the terminal, formats numbers, reads a line of input with ACCEPT, and uses the ANS Forth file word set (CREATE-FILE, WRITE-LINE, OPEN-FILE, READ-LINE) to write and read files. All examples run under Gforth.
Console Output: EMIT, TYPE, and Numbers
The Hello World page introduced ." for printing a literal string. Here we go deeper: EMIT prints a single character from an ASCII code, TYPE prints a string given as ( addr len ), and . prints a number from the stack. Words like SPACE and SPACES control layout.
Create a file named output.fth:
| |
Key points:
EMITconsumes one number and prints the character with that code.72isH,105isi.S" ..."pushes a string as( addr len );TYPEconsumes that pair and prints those bytes..(“dot”) prints the top stack item as a signed number followed by a trailing space. Because1 2 3 . . .pops from the top down, it prints3 2 1.
Formatted Number Output
Forth formats numbers by controlling the numeric base and field width rather than with format specifiers. .R right-justifies a number in a fixed-width column, and switching BASE with HEX/DECIMAL changes how numbers are displayed.
Create a file named format.fth:
| |
Notice that .R does not append a trailing space (unlike .), which is exactly why it is useful for aligning columns. The HEX and DECIMAL words simply set the global BASE variable, so every subsequent numeric conversion — input and output alike — uses that base until you change it back.
Reading Input with ACCEPT
To read a line of text you first reserve a buffer with CREATE ... ALLOT, then call ACCEPT, which reads up to max characters into the buffer and returns the actual number of characters read. Because Forth strings are ( addr len ) pairs, the buffer address plus that returned length is everything TYPE needs to echo the input back.
Create a file named input.fth:
| |
Walking the stack: ACCEPT consumes the buffer address and the maximum length, leaving just len. Pushing name-buf gives ( len addr ), and SWAP reorders it to ( addr len ) — the exact shape TYPE expects. KEY is the single-character counterpart to ACCEPT: it waits for one keystroke and pushes its ASCII code, which pairs naturally with EMIT for character-at-a-time interaction.
Writing and Reading Files
Gforth implements the ANS Forth file word set. Each file word returns an I/O result code (ior) that is zero on success and non-zero on error, which pairs perfectly with ABORT" — a word that aborts with a message when the flag on the stack is true (non-zero). This single example writes a file, closes it, then reopens it and reads it back line by line.
Create a file named file_io.fth:
| |
The reading loop is a classic Forth idiom: READ-LINE leaves ( len flag ior ); ABORT" consumes ior and bails on any error; WHILE consumes flag and keeps looping as long as a line was actually read. When the file ends, flag is false, the loop exits, and the final DROP discards the leftover length count.
Running with Docker
| |
Each file ends with bye, so Gforth exits cleanly after running it. For the input example, the -i flag keeps standard input open so ACCEPT can read the piped line.
Expected Output
Running output.fth produces:
Hi
Stack-based output
42
5
A B
3 2 1
Running format.fth produces:
42
7
FF
255
Value = 100
Running file_io.fth writes notes.txt and then prints:
--- notes.txt ---
Forth reads and writes files
using the ANS file word set
Running input.fth with Ada supplied on standard input produces:
Enter your name: Hello, Ada! Welcome to Forth.
Key Concepts
- Strings are
( addr len )pairs — there is no string object;S"pushes an address and a length, andTYPEconsumes that pair to print it. EMIT/KEYwork on single characters — they move one ASCII code between the stack and the terminal, whileTYPE/ACCEPThandle whole buffers..adds a trailing space;.Rdoes not — use.Rwith a width when you need aligned columns, and switchBASEwithHEX/DECIMALfor other radixes.- Input needs a buffer — reserve space with
CREATE ... ALLOT, then letACCEPTfill it and return the real length. - File words return an
ior— a zero result means success; pairing each call withABORT"turns a failed open, write, or read into an immediate, readable error. READ-LINEdrives EOF loops — itsflagresult feeding aBEGIN ... WHILE ... REPEATloop is the idiomatic way to read a file to the end.- I/O is just more words — because every I/O primitive composes through the stack, you can wrap your own vocabulary (like the
LABELword) to build higher-level, application-specific output.
Running Today
All examples can be run using Docker:
docker pull forthy42/gforth:latest
Comments
Loading comments...
Leave a Comment