Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Archetype Documentation

Archetype is a message-passing, object-oriented programming language for
writing text-based adventure games. These are the language's own documents,
first written between 1992 and 1995 and revised for the current interpreter.
For building and running the interpreter itself, see the
[project README](../README.md).

| | |
|---|---|
| [About Archetype](about.md) | What the language is, and where it came from. |
| [How to Play an Archetype Adventure](playing.md) | For players. What to type at the prompt, and what the parser can hear. |
| [How to Quickly Write an Adventure Game](writing.md) | For authors. A tutorial that starts with a four-line adventure and ends with custom verbs. |
| [The Archetype Language Reference Manual](manual.md) | The language proper: every statement, operator, and data type, with BNF. |
| [Known Issues](known-issues.md) | What is currently wrong or missing, and the 1995 bug list scored against the interpreter of today. |

Start with [playing](playing.md) if you have never seen a text adventure, with
[writing](writing.md) if you want to make one, and with the
[manual](manual.md) if you want to know exactly what the language does.

## About these documents

The two audiences never merged, and that is on purpose. *How to Quickly Write
an Adventure Game* assumes a reader who has never met a programming language,
because Archetype was designed so that such a reader could use it — a forgiving
syntax, strongly oriented toward one domain (see [About Archetype](about.md)).
The *Reference Manual* assumes someone who wants the grammar, and gives it in
Backus-Naur form. A beginner's tutorial shipping beside a BNF grammar looks odd
until you know that both were true at once: the language had to be usable by
someone who had never programmed, and it had to be a real language.

What the revision changed is the facts. The original documents describe a pair
of DOS programs, `CREATE.EXE` and `PERFORM.EXE`, running on a machine where a
string could not exceed 256 characters and the screen was always 24 lines tall.
None of that is true any more. Where a limit has been lifted, the text says so
rather than pretending it was never there.

The originals are preserved verbatim in [`original/`](original/).
92 changes: 92 additions & 0 deletions docs/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# About Archetype

Archetype is a simple, stripped-down object-oriented programming language
designed for the writing of text adventure games. The interpreter has a partial
natural language parser, and the language's `write` statement pauses when it
has filled the screen, so that long pieces of text do not get lost off the top.

However, Archetype does not have everything necessary for quickly creating an
adventure game, because the language is more general-purpose than that. There
are about a thousand lines of code in the files `intrptr.arch`, `lexicon.arch`,
and `cardinal.arch`, all of which are included when your adventure includes
`standard.arch`. Thus much of Archetype is itself written in Archetype.

The language is a translator and an interpreter in one program. The interpreter
expects a file that has been syntactically and semantically verified by the
translator and turned into a binary format. This half-a-compiler/interpreter
pair design is found in other languages, such as Icon and certain versions of
LISP. It is not a full interpreter, such as BASIC, nor a full compiler, such as
Pascal.

```shell
archetype --source=mygame.arch --include=games --create # translate
archetype --perform=mygame.acx # interpret
```

## Where it came from

My own computer, a TI-99/4A, had a sprite library: an abstraction under which a
shape animated itself, kept moving once dispatched, and noticed when it
overlapped another. Using it taught me the extraordinary value of having the
right abstractions for a given problem space. What I wanted for text adventures
was something like sprites, but for objects which could be described by text
and addressed with natural language. The clue arrived in a one-hour lecture on
Smalltalk in a Comparative Programming Languages course: I understood the basic
premise right away, the way it turned conventional programming inside-out.
Instead of being verb-oriented, it was noun-oriented. Of course that's how
you'd write a text adventure.

I settled on the design right before graduating in late 1990 and began building
it. Around the same time I found out about a text adventure authoring system
called AdvSys, written in LISP by David Betz, who had also written XLISP 2.0.
It was, no surprises, also object-oriented. Upon this discovery my crest was
felled and a great deal of wind left my sails. But LISP is a powerful language,
not an easy one, and I had designed Archetype to be so simple that even a
non-programmer could use it, with a forgiving syntax that was strongly oriented
toward the text adventure domain. The world could still use it. Somewhere.

I did not know then that Infocom had solved the same problem years earlier with
an internally developed language called ZIL. How could I have known? It was the
closely guarded trade secret of a company making ten million dollars a year at
its peak, long before the open source ethos had proven itself in the global
marketplace.

## A little history

Archetype was developed using Turbo Pascal 5.5 on an IBM-compatible laptop with
an 8088 processor, two disk drives (no hard drive), and a 10 MHz clock. Because
of this, the compiler and interpreter were designed to minimize disk use,
getting things into memory as quickly as possible and working with them there.
As a result it made tremendous use of dynamic memory. On a higher-end machine,
the disk may actually be faster than dynamically allocating memory, so that
architecture did not run as much faster as you might expect on a faster
machine.

Archetype 1.0 was distributed as shareware. Archetype 1.01 was distributed
public-domain, along with its Turbo Pascal source code and the Archetype source
code for the adventures.

Version 3.0 was a complete reimplementation in modern C++, and is the one you
are reading about here. The language it runs is the same language, and the
adventures written for the Turbo Pascal version still play; but the interpreter
is no longer fighting for memory on a machine with two floppy drives, and a
number of the old limits went away with the machine that imposed them. Version
4.0 added lists and list-headed message dispatch. The whole thing is now on
GitHub under the MIT license.

## An invitation

I'd like to know what you think of both the games in this archive and the
language itself. If you would like to try writing an adventure game, I would
really like to know how easy or difficult you found it using Archetype and the
`standard.arch` include files.

But what I would like to know even more is if you can think of an application
for Archetype other than writing adventure games. Somebody once said that the
best tool is one which is used for a purpose other than the one for which it
was designed. The file `animal.arch` in this archive shows one different way to
use Archetype.

Enjoy!

Derek T. Jones
112 changes: 112 additions & 0 deletions docs/known-issues.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Known Issues

This document has two halves. The first is a list of what is currently known to
be wrong or missing. The second is the original 1995 `BUGS.TXT`, scored against
the interpreter as it stands, because a list of eighteen complaints written for
a 10 MHz machine turns out to be a good measure of what thirty years changed and
what it didn't.

## Still open

**Verbs inside noun phrases confuse the parser.** If a game has an object named
"start button" and verbs named "start" and "push", then "push the start button"
is collapsed into `push start` + `button` rather than `push` + `start button`.
The parser removes "a", "an", and "the" before parsing and has no other notion
of article or noun-phrase boundary. Still exactly as reported in 1995; the
modern interpreter merely announces it better ("I don't know how to push start
a start button"). Related: [issue #40](https://github.com/gitosaurus/archetype/issues/40).

It is worth knowing that this is a priced trade rather than an oversight. The
design constraint, set before a line of the parser was written, was that any
natural language parser for the language would have to be both semantically
forgiving and computationally cheap. The grammar-and-lexicon approach that
would resolve "the start button" correctly was neither — daunting in
complexity, expensive in computing power, and brittle. The cheap parser buys
its speed and its tolerance by not knowing where a noun phrase begins, and this
is the bill for it.

**Integers only.** No real numbers, no floating-point arithmetic. Numbers are
32-bit signed and wrap silently on overflow.

**No separate compilation.** There is no equivalent of a `.o` file: no way to
compile `intrptr.arch` once and link it with an adventure later. It is
recompiled every single time you compile your adventure, and an adventure
cannot be built and tested in independent sections.

**The cumulative assignments are not faster.** `a +:= 2` is executed exactly as
`a := a + 2`. Prefer the short form because it is clearer, not because it is
quicker; the 1995 promise that it would someday be faster has not been kept and
no longer matters.

**`&:=` propagates UNDEFINED.** `s &:= expr` leaves `s` UNDEFINED if `expr` is
UNDEFINED, rather than leaving `s` alone. This is often not what the programmer
expects, but it does make sense if you think about it: concatenation with an
undefined value is undefined, and the cumulative form is defined in terms of
the plain one.

**No local variables.** Attributes look like locals syntactically, but they are
global — anyone who knows your name knows your attributes — and they are
static. A method has no scratch space of its own.

**The compiler does not prohibit duplicate declarations.** Declaring more than
one `main` object, or the same attribute or method twice within an object, is
accepted without a word. The last declaration silently wins. It ought to be an
error, as the results may surprise the programmer.

**An unknown operator aborts the compiler.** A token that scans as an operator
but is not one — `<>`, for instance — prints "Unknown operator" and then calls
`terminate()`, so the process dies on SIGABRT with a C++ runtime message on the
user's screen. Every other compile error produces a diagnostic and exits
cleanly.

**`display` of a destroyed object crashes the interpreter.** `ObjectValue::display`
(`src/Value.cc:363`) looks up an object that no longer exists and dereferences
the null result, so `display ref` segfaults when `ref` points at an object that
`destroy` has removed. Only nameless objects — the ones `create` makes — reach
that code path, because a named object returns earlier. `write` of the same
value is unaffected. Tracked as
[issue #62](https://github.com/gitosaurus/archetype/issues/62).

**`\\` produces a spurious diagnostic.** A doubled backslash in a string
literal yields the single backslash it should, but the scanner first complains
"Unknown escape character \\" on its way there.

## The 1995 list, scored

The letter in the original list indicated the kind of problem: **I**, a
solution exists but is not implemented; **D**, several solutions but no chosen
best one; **K**, knotty, needing sweeping design changes; **N**, not really a
problem, but someday it would be nice.

| # | | The original complaint | Where it stands |
|---|---|---|---|
| 1 | I | Verbs cannot appear inside noun phrases | **Still open.** Verified; the parser still knows only "a", "an", "the" |
| 2 | D | A bare verb should say "I don't know what *verb* means all by itself" | **Resolved.** The message exists. The hollow-object case that complicated it — "leave" while sitting on a couch — remains, deliberately |
| 3 | I | Vocabulary is not saved with the game state | **Resolved.** The parser, including its assembled vocabulary, is serialized with everything else |
| 4 | K | The interpreter uses about three times the memory it needs | **Moot.** The design that traded memory for speed on an 8088 is gone, and so is the constraint that made it a bug |
| 5 | N | No lists or arrays | **Resolved in 4.0.** List literals, `@`, `head`, `tail`, and list-valued expressions |
| 6 | N | No arguments when sending messages | **Largely resolved in 4.0.** `['MOVE TO' player] -> thing` dispatches on the head and carries the rest as arguments. The experiment in dataless messages ran for thirty years and the qualified "yes" held up |
| 7 | D | No real numbers | **Still open.** Integers only |
| 8 | K | No `.o` files; no separate compilation | **Still open** |
| 9 | D | A nicer debugger, able to view and change attributes interactively | **Largely resolved.** The REPL evaluates arbitrary expressions against a loaded universe; `display` prints values inline; `--inspect` and `--sitrep` dump world state as RDF/Turtle; the `'DEBUG MESSAGES'`, `'DEBUG EXPRESSIONS'`, and `'DEBUG STATEMENTS'` toggles remain |
| 10 | I | `a +:= 2` is no faster than `a := a + 2` | **Still true, and no longer interesting** |
| 11 | N | `s &:= expr` leaves `s` UNDEFINED if `expr` is | **Still open.** Verified |
| 12 | D | Errors in a declaration are sometimes reported after the whole declaration | **Resolved.** Every diagnostic now carries file, line, column, and a caret under the offending token |
| 13 | N | No local variables | **Still open** |
| 14 | I | I/O is slower than it has to be, thanks to `SAVELOAD.PAS` | **Resolved.** That module and its per-atom I/O are gone |
| 15 | I | Include files must be in the current or the adventure's directory | **Resolved.** `--include` takes a colon-separated search path, and `ARCHETYPE_INCLUDE` supplies more |
| 16 | I | Paging assumes 24 lines (*egads*) | **Resolved.** The pager reads the terminal's actual height |
| 17 | I | Word-wrap does not recognize `\n` and friends | **Resolved.** Verified: a `\n` inside a long string breaks the line and the wrapper picks up correctly afterward |
| 18 | I | Duplicate `main`, attributes, or methods are not prohibited | **Still open.** Verified |

## What the 1995 list never imagined

Half of that document worries about memory and disk on a machine with two
floppy drives, and none of it worries about running anywhere but DOS. The
concerns have almost exactly reversed. Performance is no longer a design
constraint at this scale, while the things that now matter most — that the same
`.acx` plays identically on macOS, Linux, and in a browser under WebAssembly;
that a save file is a mutated copy of the binary and therefore fully resumable;
that compilation is deterministic, so the same source always produces the same
bytes — are not on the list at all, because in 1995 there was only one machine
to be right on.
Loading
Loading