Skip to content

sjnam/gweb

Repository files navigation

GWEB — Literate programming for Go

CI License: MIT

GWEB is a literate programming system for the Go programming language, modeled closely on Donald Knuth and Silvio Levy's CWEB. You write a single .w source file that interleaves TeX documentation with Go code, and two tools turn it into either a program or a typeset document — exactly as CWEB does for C, with C replaced by Go:

CWEB GWEB purpose
ctangle gtangle extract compilable source (.go) for the machine
cweave gweave produce a typeset document (.tex → PDF) for people
  • gtangle strips the documentation, reassembles the named code sections in the order the Go compiler needs, and writes gofmt-formatted .go files.
  • gweave produces a TeX file in which reserved words are bold, identifiers are italic, strings are typewriter, named sections are linked by number, and a cross-referenced index and list of refinements are generated automatically. Declared type names are set bold and const names typewriter; mark other names typewriter too (e.g. another package's @d http.StatusOK), and override any of it with @f/@s.

Prerequisites

GWEB assumes you already know Go and are comfortable with TeX — in particular plain TeX, since gwebmac.tex and this repository's macros are plain TeX, not LaTeX. Beyond that:

  • A TeX distribution (e.g. TeX Live or MacTeX) must be installed, providing the tex, pdftex, and luatex engines the woven output is typeset with.
  • For Korean output (\input kotexgweb; see Usage), the Noto Serif/Sans CJK KR fonts must be installed locally — kotexgweb.tex selects them by name.

Build

make build        # builds bin/gtangle and bin/gweave
make test         # runs the test suite

As in CWEB, only the Go needed to build gtangle is committed; gweave and the weave engine are tangled from their .w sources on the fly. make build does this for you: it builds gtangle from the committed sources, runs it to generate the rest, then builds gweave. (make generate performs just the tangling step.) So build through make, not a bare go build ./..., on a fresh checkout.

Producing a PDF additionally requires a TeX engine (e.g. pdftex, from TeX Live or MacTeX).

Install

gtangle installs the Go way (its sources are committed):

go install github.com/sjnam/gweb/cmd/gtangle@latest

gweave is generated rather than committed, so install both from a checkout with make install (or make install-tools to put just the binaries in your GOBIN). Point TEXINPUTS at a copy of gwebmac.tex so the TeX engine can find it.

For a full install — the commands plus gwebmac.tex (placed in your TeX tree so pdftex foo.tex just works) plus the man page — use the install script:

./install.sh                       # into ~ (TEXMFHOME) and /usr/local (may need sudo)
./install.sh --prefix="$HOME/.local"   # a writable prefix, no sudo
sudo ./install.sh                  # system-wide
./install.sh --uninstall           # remove everything it installed

make install / make uninstall call the script (pass options with ARGS=...); run ./install.sh --help for all options (--bindir, --mandir, --texmf). Then man gtangle / man gweave describe the commands.

Usage

gtangle foo.w     # -> foo.go (plus any @(file@>= outputs)
gweave  foo.w     # -> foo.tex
pdftex  foo.tex   # -> foo.pdf   (gwebmac.tex must be on TEXINPUTS)

The .w extension may be omitted, as in CWEBgtangle foo reads foo.w (and a bare change-file name gets .ch). Each command prints a brief cweb-style progress line, one *N per starred (chapter) section.

For a hyperlinked PDF you can also go through DVI, exactly as CWEB does — request the \special{pdf:…} back end with \let\pdf+, then convert with dvipdfmx:

tex "\let\pdf+ \input foo.tex"   # -> foo.dvi  (with pdf: specials)
dvipdfmx foo.dvi                 # -> foo.pdf  (links + bookmarks)

Both commands accept -o <dir> to choose an output directory, and an optional change file as a second argument — gtangle foo.w foo.ch — which patches the master source without editing it (CWEB's .ch mechanism; see format.md). For example, gtangle examples/wc.w examples/wc.ch builds a CSV variant of the word counter. gwebmac.tex lives at the repository root; point TEXINPUTS there, or copy the file next to your document.

The tangled Go always carries //line directives, so the Go compiler, go vet, and panic traces report errors at .w positions instead of .go ones — the Go counterpart of CWEB's #line, which ctangle likewise emits unconditionally:

gtangle foo.w && go build .       # an error reads  foo.w:42: ...

GWEB tangles its own sources the same way, so editing a .w reshuffles the line numbers in the bootstrap Go it commits — the price of keeping the generated code honest about its literate origin.

For a tour of the bundled examples, the full .w file-format reference, and non-English (e.g. Korean) documentation, see the manual — gwebman.tex (make manual).

More examples

The examples/ directory here is deliberately small — just enough to exercise the tools. For GWEB used in earnest, see the companion repository:

sjnam/gweb-examples — thirty-odd standalone literate Go programs, plus two curated collections that work through a single technique across several problems (cdq-dc/, CDQ divide-and-conquer; cht/, the convex hull trick). The programs range over algorithms and data structures (ntt.w, ziptree.w, suffixautomaton.w, runningmedian.w), concurrency (fast_cancel.w, pmap.w, pipeline.w), and competitive-programming solutions. Several are written in Korean via \input kotexgweb, and some carry MetaPost figures — so the repository doubles as a demonstration of what a finished GWEB document can look like.

How it is organized

Each .w source sits next to the Go it generates. Only the Go that bootstraps gtangle is committed (◇ below); the rest (✦) is tangled by make.

gweb.w             the master web: @i-includes the three below (woven, not tangled)
cmd/
├── gtangle/       gtangle.w -> gtangle: front end + the tangle engine        ◇
└── gweave/        gweave.w -> gweave: front end + the weave engine (lexer,        ✦
                   pretty-printer, cross-references)
common/            common.w -> the shared parser (CWEB's common.w)        ◇
gwebmac.tex        TeX macros for woven output (CWEB's cwebmac.tex)
kotexgweb.tex      Korean (luatexko) localization + fonts + LuaTeX PDF back end
gweb.1             the man page for both commands (CWEB's cweb.1)
format.md          the .w file-format reference
gwebman.tex        the GWEB manual
examples/          worked examples
editors/
└── vscode/        VS Code language support for .w files
install.sh         installer for the commands, gwebmac.tex, and the man page

For editor support and how GWEB's self-hosting (make tangle, make bootstrap, make selfdoc) works, see the manual as well.

Design notes and limitations

  • Lexing for free. Tangle relies on gofmt (go/format) to canonicalize the assembled program, so the emitted Go is always tidily formatted as long as the web assembles into valid Go.
  • Pretty-printing. gweave highlights tokens (bold keywords, italic identifiers, typewriter strings, real math symbols for ≤ ≥ ≠ ←, …) and mirrors the source's own spacing rather than re-deriving layout from a full Go grammar the way CWEB does for C. Because gofmt-formatted Go already encodes the grammar in its spacing, mirroring it reproduces gofmt exactly — including the tricky cases (*T vs a * b, []T vs a[i], precedence spacing like a*b + c) — without any parsing. Long code lines wrap at the inter-token spaces, with continuation lines indented one step deeper. Write the code in your sections in gofmt style for the best-looking output.
  • Definition detection in the index is heuristic (an identifier following func/var/const/type, or just left of :=), not a full type check.
  • Diagnostics. Both tools report, with file:line locations, unterminated control codes, references to undefined sections, ambiguous ... abbreviations, and named sections defined but never used. These are warnings; gtangle still stops if it must actually expand an undefined reference. An origin map kept in step through @i includes and change-file edits makes every location point back to the file (and line) you actually wrote.
  • Page layout follows cweave. gwebmac.tex gives the woven document the same furniture CWEB produces: run-in bold section headings (with a page break before each major starred section), §sec JOBNAME … GROUPTITLE page running heads, a title-less index that sits under your own @* Index. section, a Names of the Sections list, and a contents page (centered title, Section/Page columns, dotted leaders). The index is set in two columns. As in CWEB, the contents page is produced at the end in a single TeX pass; move it to the front when binding if you prefer. The font set mirrors cwebmac.tex (medium-caps \mc, the cmtex10 string font, …), and a few cwebmac prose helpers are available in commentary: \CEE/, \GO/, \UNIX/, \TEX/, and \\{id} for an italic identifier (though the GWEB idiom for inline code is |id|).
  • Hyperlinks and bookmarks. Every section number shown as a reference, in the index, in a cross-reference note, or on the contents page is a blue link to that section; clicking it jumps there (for the underlined index entries, to where the identifier is defined). The starred sections also become a PDF outline (bookmark tree), nested by their @*, @*1, @*2 depths. Two back ends produce these: pdftex/luatex in PDF mode use the engine's own primitives, while the DVI route emits \special{pdf:…} commands for dvipdfmx when you ask for them with \let\pdf+ (the same convention as CWEB). With a plain DVI engine and no \let\pdf+, the links and bookmarks are simply omitted.

License

GWEB is released under the MIT License.

About

Literate programming system for Go

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages