Add colons to const syntax
diff --git a/cmd/wuffs/gen.go b/cmd/wuffs/gen.go
index 6aa685c..5bb1619 100644
--- a/cmd/wuffs/gen.go
+++ b/cmd/wuffs/gen.go
@@ -274,7 +274,7 @@
 				if !n.Public() {
 					continue
 				}
-				fmt.Fprintf(out, "pub const %s %s = %v\n",
+				fmt.Fprintf(out, "pub const %s : %s = %v\n",
 					n.QID().Str(&h.tm), n.XType().Str(&h.tm), n.Value().Str(&h.tm))
 
 			case a.KFunc:
diff --git a/doc/changelog.md b/doc/changelog.md
index 657defa..0ab8793 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -15,6 +15,7 @@
 - Added `WUFFS_BASE__PIXEL_BLEND__SRC_OVER`.
 - Added `WUFFS_BASE__PIXEL_FORMAT__BGR_565`.
 - Added alloc functions.
+- Added colons to const syntax.
 - Added double-curly blocks.
 - Added interfaces.
 - Added preprocessor.
diff --git a/lang/parse/parse.go b/lang/parse/parse.go
index 4bf02fb..e9eb4f8 100644
--- a/lang/parse/parse.go
+++ b/lang/parse/parse.go
@@ -140,6 +140,12 @@
 			}
 			// TODO: check AllowDoubleUnderscoreNames?
 
+			if x := p.peek1(); x != t.IDColon {
+				got := p.tm.ByID(x)
+				return nil, fmt.Errorf(`parse: expected ":", got %q at %s:%d`, got, p.filename, p.line())
+			}
+			p.src = p.src[1:]
+
 			typ, err := p.parseTypeExpr()
 			if err != nil {
 				return nil, err
diff --git a/lang/render/render.go b/lang/render/render.go
index 64c30cf..4827000 100644
--- a/lang/render/render.go
+++ b/lang/render/render.go
@@ -122,25 +122,34 @@
 		// Apply or update varNameLength.
 		if len(lineTokens) < 3 {
 			varNameLength = 0
-		} else if id := lineTokens[0].ID; id == t.IDPri || id == t.IDPub {
-			inStruct = lineTokens[1].ID == t.IDStruct
-			varNameLength = 0
-		} else if id == t.IDVar || inStruct {
-			if varNameLength == 0 {
-				varNameLength = measureVarNameLength(tm, lineTokens, src)
-			}
-			if id == t.IDVar {
-				buf = append(buf, "var "...)
-				lineTokens = lineTokens[1:]
-			}
-			name := tm.ByID(lineTokens[0].ID)
-			lineTokens = lineTokens[1:]
-			buf = append(buf, name...)
-			for i := uint32(len(name)); i <= varNameLength; i++ {
-				buf = append(buf, ' ')
-			}
 		} else {
-			varNameLength = 0
+			id0 := lineTokens[0].ID
+			id1 := lineTokens[1].ID
+			if (id0 == t.IDPri) || (id0 == t.IDPub) {
+				inStruct = id1 == t.IDStruct
+				if id1 != t.IDConst {
+					varNameLength = 0
+				}
+			}
+			if (id1 == t.IDConst) || (id0 == t.IDVar) || inStruct {
+				if varNameLength == 0 {
+					varNameLength = measureVarNameLength(tm, lineTokens, src)
+				}
+				name := ""
+				if colon := findColon(lineTokens); colon >= 0 {
+					for _, lt := range lineTokens[:colon] {
+						name = tm.ByID(lt.ID)
+						buf = append(buf, name...)
+						buf = append(buf, ' ')
+					}
+					for i := uint32(len(name)); i < varNameLength; i++ {
+						buf = append(buf, ' ')
+					}
+					lineTokens = lineTokens[colon:]
+				}
+			} else {
+				varNameLength = 0
+			}
 		}
 
 		// Render the lineTokens.
@@ -299,26 +308,30 @@
 		x.IsSQStrLiteral(tm) || (x == t.IDQuestion)
 }
 
+func findColon(lineTokens []t.Token) int {
+	for i, lt := range lineTokens {
+		if lt.ID == t.IDColon {
+			return i
+		}
+	}
+	return -1
+}
+
 func measureVarNameLength(tm *t.Map, lineTokens []t.Token, remaining []t.Token) uint32 {
-	if len(lineTokens) < 2 {
+	x := findColon(lineTokens)
+	if x <= 0 {
 		return 0
 	}
 
-	x := 0 // "x T" struct field.
-	if lineTokens[0].ID == t.IDVar {
-		x = 1 // "var x T" var statement.
-	}
-
 	line := lineTokens[0].Line
-	length := len(tm.ByID(lineTokens[x].ID))
+	length := len(tm.ByID(lineTokens[x-1].ID))
 	for (len(remaining) > x) &&
-		((x == 0) || (remaining[0].ID == t.IDVar)) &&
 		(remaining[0].Line == line+1) &&
 		(remaining[x].Line == line+1) &&
-		(remaining[x].ID.IsIdent(tm)) {
+		(remaining[x].ID == t.IDColon) {
 
 		line = remaining[0].Line
-		length = max(length, len(tm.ByID(remaining[x].ID)))
+		length = max(length, len(tm.ByID(remaining[x-1].ID)))
 
 		remaining = remaining[x+1:]
 		for len(remaining) > 0 && remaining[0].Line == line {
diff --git a/std/bmp/decode_bmp.wuffs b/std/bmp/decode_bmp.wuffs
index 6026655..dd88a59 100644
--- a/std/bmp/decode_bmp.wuffs
+++ b/std/bmp/decode_bmp.wuffs
@@ -17,7 +17,7 @@
 
 pri status "#internal error: inconsistent swizzle count"
 
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 0
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
 
 pub struct decoder? implements base.image_decoder(
 	width  : base.u32[..= 0x7FFF_FFFF],
diff --git a/std/crc32/common_crc32.wuffs b/std/crc32/common_crc32.wuffs
index ee2b5ca..b695932 100644
--- a/std/crc32/common_crc32.wuffs
+++ b/std/crc32/common_crc32.wuffs
@@ -62,7 +62,7 @@
 
 // The table below was created by script/print-crc32-magic-numbers.go.
 
-pri const IEEE_TABLE array[16] array[256] base.u32 = [[
+pri const IEEE_TABLE : array[16] array[256] base.u32 = [[
 	0x0000_0000, 0x7707_3096, 0xEE0E_612C, 0x9909_51BA, 0x076D_C419, 0x706A_F48F, 0xE963_A535, 0x9E64_95A3,
 	0x0EDB_8832, 0x79DC_B8A4, 0xE0D5_E91E, 0x97D2_D988, 0x09B6_4C2B, 0x7EB1_7CBD, 0xE7B8_2D07, 0x90BF_1D91,
 	0x1DB7_1064, 0x6AB0_20F2, 0xF3B9_7148, 0x84BE_41DE, 0x1ADA_D47D, 0x6DDD_E4EB, 0xF4D4_B551, 0x83D3_85C7,
diff --git a/std/deflate/common_consts.wuffs b/std/deflate/common_consts.wuffs
index 5213414..9b29b5c 100644
--- a/std/deflate/common_consts.wuffs
+++ b/std/deflate/common_consts.wuffs
@@ -13,12 +13,12 @@
 // limitations under the License.
 
 // CODE_ORDER is defined in the RFC section 3.2.7.
-pri const CODE_ORDER array[19] base.u8[..= 18] = [
+pri const CODE_ORDER : array[19] base.u8[..= 18] = [
 	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
 ]
 
 // REVERSE8 reverses the bits in a byte.
-pri const REVERSE8 array[256] base.u8 = [
+pri const REVERSE8 : array[256] base.u8 = [
 	0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,  // 0x00 - 0x07
 	0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,  // 0x08 - 0x0F
 	0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,  // 0x10 - 0x17
diff --git a/std/deflate/decode_deflate.wuffs b/std/deflate/decode_deflate.wuffs
index e260b98..20c5e5c 100644
--- a/std/deflate/decode_deflate.wuffs
+++ b/std/deflate/decode_deflate.wuffs
@@ -33,7 +33,7 @@
 
 // TODO: replace the placeholder 1 value with either 0 or (32768 + 512),
 // depending on whether we'll move decoder.history into the workbuf.
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 1
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 1
 
 // The next two tables were created by script/print-deflate-magic-numbers.go.
 //
@@ -43,14 +43,14 @@
 //
 // Some trailing elements are 0x08000000. Bit 27 indicates an invalid value.
 
-pri const LCODE_MAGIC_NUMBERS array[32] base.u32 = [
+pri const LCODE_MAGIC_NUMBERS : array[32] base.u32 = [
 	0x4000_0000, 0x4000_0100, 0x4000_0200, 0x4000_0300, 0x4000_0400, 0x4000_0500, 0x4000_0600, 0x4000_0700,
 	0x4000_0810, 0x4000_0A10, 0x4000_0C10, 0x4000_0E10, 0x4000_1020, 0x4000_1420, 0x4000_1820, 0x4000_1C20,
 	0x4000_2030, 0x4000_2830, 0x4000_3030, 0x4000_3830, 0x4000_4040, 0x4000_5040, 0x4000_6040, 0x4000_7040,
 	0x4000_8050, 0x4000_A050, 0x4000_C050, 0x4000_E050, 0x4000_FF00, 0x0800_0000, 0x0800_0000, 0x0800_0000,
 ]
 
-pri const DCODE_MAGIC_NUMBERS array[32] base.u32 = [
+pri const DCODE_MAGIC_NUMBERS : array[32] base.u32 = [
 	0x4000_0000, 0x4000_0100, 0x4000_0200, 0x4000_0300, 0x4000_0410, 0x4000_0610, 0x4000_0820, 0x4000_0C20,
 	0x4000_1030, 0x4000_1830, 0x4000_2040, 0x4000_3040, 0x4000_4050, 0x4000_6050, 0x4000_8060, 0x4000_C060,
 	0x4001_0070, 0x4001_8070, 0x4002_0080, 0x4003_0080, 0x4004_0090, 0x4006_0090, 0x4008_00A0, 0x400C_00A0,
@@ -62,8 +62,8 @@
 // script/print-deflate-huff-table-size.go which calculates that, for a 9-bit
 // primary table, the worst-case size is 852 for the Lit/Len table and 592 for
 // the Distance table.
-pri const HUFFS_TABLE_SIZE base.u32 = 1024
-pri const HUFFS_TABLE_MASK base.u32 = 1023
+pri const HUFFS_TABLE_SIZE : base.u32 = 1024
+pri const HUFFS_TABLE_MASK : base.u32 = 1023
 
 pub struct decoder? implements base.io_transformer(
 	// These fields yield src's bits in Least Significant Bits order.
diff --git a/std/gif/common_consts.wuffs b/std/gif/common_consts.wuffs
index 725840f..7e9a816 100644
--- a/std/gif/common_consts.wuffs
+++ b/std/gif/common_consts.wuffs
@@ -24,7 +24,7 @@
 
 pri status "#internal error: inconsistent ri/wi"
 
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 0
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
 
 // See the spec appendix E "Interlaced Images" on page 29. The first element
 // represents either that the frame was non-interlaced, or that all interlace
@@ -39,26 +39,26 @@
 // accessed after all interlace stages are complete. Being the maximum base.u32
 // value means that, after all interlace stages are complete, dst_y will be set
 // to that maximum value (and therefore outside the frame rect).
-pri const INTERLACE_START array[5] base.u32 = [0xFFFF_FFFF, 1, 2, 4, 0]
-pri const INTERLACE_DELTA array[5] base.u8 = [1, 2, 4, 8, 8]
-pri const INTERLACE_COUNT array[5] base.u8 = [0, 1, 2, 4, 8]
+pri const INTERLACE_START : array[5] base.u32 = [0xFFFF_FFFF, 1, 2, 4, 0]
+pri const INTERLACE_DELTA : array[5] base.u8 = [1, 2, 4, 8, 8]
+pri const INTERLACE_COUNT : array[5] base.u8 = [0, 1, 2, 4, 8]
 
 // ANIMEXTS1DOT0 is "ANIMEXTS1.0" as bytes.
-pri const ANIMEXTS1DOT0 array[11] base.u8 = [
+pri const ANIMEXTS1DOT0 : array[11] base.u8 = [
 	0x41, 0x4E, 0x49, 0x4D, 0x45, 0x58, 0x54, 0x53, 0x31, 0x2E, 0x30,
 ]
 
 // NETSCAPE2DOT0 is "NETSCAPE2.0" as bytes.
-pri const NETSCAPE2DOT0 array[11] base.u8 = [
+pri const NETSCAPE2DOT0 : array[11] base.u8 = [
 	0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30,
 ]
 
 // ICCRGBG1012 Is "ICCRGBG1012" as bytes.
-pri const ICCRGBG1012 array[11] base.u8 = [
+pri const ICCRGBG1012 : array[11] base.u8 = [
 	0x49, 0x43, 0x43, 0x52, 0x47, 0x42, 0x47, 0x31, 0x30, 0x31, 0x32,
 ]
 
 // XMPDATAXMP IS "XMP DataXMP" as bytes.
-pri const XMPDATAXMP array[11] base.u8 = [
+pri const XMPDATAXMP : array[11] base.u8 = [
 	0x58, 0x4D, 0x50, 0x20, 0x44, 0x61, 0x74, 0x61, 0x58, 0x4D, 0x50,
 ]
diff --git a/std/gif/decode_quirks.wuffs b/std/gif/decode_quirks.wuffs
index f31b40d..edb4fb2 100644
--- a/std/gif/decode_quirks.wuffs
+++ b/std/gif/decode_quirks.wuffs
@@ -35,7 +35,7 @@
 // see the N+1'th frame's header (or the end-of-animation terminator), so that
 // e.g. the API for visiting the N'th frame can also return whether it's the
 // final frame. Enabling this quirk allows for matching that behavior.
-pub const QUIRK_DELAY_NUM_DECODED_FRAMES base.u32 = 0x3E16_1800 | 0x00
+pub const QUIRK_DELAY_NUM_DECODED_FRAMES : base.u32 = 0x3E16_1800 | 0x00
 
 // When this quirk is enabled, the background color of the first frame is set
 // to black whenever that first frame has a local (frame-specific) palette.
@@ -47,7 +47,7 @@
 //
 // There isn't really much of a rationale for this, other than it matches the
 // behavior of another GIF implementation.
-pub const QUIRK_FIRST_FRAME_LOCAL_PALETTE_MEANS_BLACK_BACKGROUND base.u32 = 0x3E16_1800 | 0x01
+pub const QUIRK_FIRST_FRAME_LOCAL_PALETTE_MEANS_BLACK_BACKGROUND : base.u32 = 0x3E16_1800 | 0x01
 
 // When this quirk is enabled, the background color is taken from the GIF
 // instead of always being transparent black. If the background color index in
@@ -63,12 +63,12 @@
 // processing WUFFS_BASE__ANIMATION_DISPOSAL__RESTORE_BACKGROUND. In both
 // cases, the caller of Wuffs, not Wuffs itself, is responsible for filling the
 // pixel buffer with that color.
-pub const QUIRK_HONOR_BACKGROUND_COLOR base.u32 = 0x3E16_1800 | 0x02
+pub const QUIRK_HONOR_BACKGROUND_COLOR : base.u32 = 0x3E16_1800 | 0x02
 
 // When this quirk is enabled, silently ignore e.g. a frame that reports a
 // width and height of 6 pixels each, followed by 50 pixel values. In that
 // case, we process the first 36 pixel values and discard the excess 14.
-pub const QUIRK_IGNORE_TOO_MUCH_PIXEL_DATA base.u32 = 0x3E16_1800 | 0x03
+pub const QUIRK_IGNORE_TOO_MUCH_PIXEL_DATA : base.u32 = 0x3E16_1800 | 0x03
 
 // When this quirk is enabled, if the initial frame bounds extends beyond the
 // image bounds, then the image bounds stay unchanged. By default (with this
@@ -77,12 +77,12 @@
 //
 // For more discussion, see
 // https://github.com/google/wuffs/blob/master/test/data/artificial/gif-frame-out-of-bounds.gif.make-artificial.txt
-pub const QUIRK_IMAGE_BOUNDS_ARE_STRICT base.u32 = 0x3E16_1800 | 0x04
+pub const QUIRK_IMAGE_BOUNDS_ARE_STRICT : base.u32 = 0x3E16_1800 | 0x04
 
 // When this quirk is enabled, a frame with zero width or height is rejected
 // during decode_frame (but accepted during decode_frame_config).
-pub const QUIRK_REJECT_EMPTY_FRAME base.u32 = 0x3E16_1800 | 0x05
+pub const QUIRK_REJECT_EMPTY_FRAME : base.u32 = 0x3E16_1800 | 0x05
 
 // When this quirk is enabled, a frame with no explicit palette is rejected,
 // instead of implicitly having a palette with every entry being opaque black.
-pub const QUIRK_REJECT_EMPTY_PALETTE base.u32 = 0x3E16_1800 | 0x06
+pub const QUIRK_REJECT_EMPTY_PALETTE : base.u32 = 0x3E16_1800 | 0x06
diff --git a/std/gzip/decode_gzip.wuffs b/std/gzip/decode_gzip.wuffs
index 0c850d3..df4f90d 100644
--- a/std/gzip/decode_gzip.wuffs
+++ b/std/gzip/decode_gzip.wuffs
@@ -21,7 +21,7 @@
 pub status "#bad header"
 
 // TODO: reference deflate.DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE.
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 1
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 1
 
 pub struct decoder? implements base.io_transformer(
 	ignore_checksum : base.bool,
diff --git a/std/json/common_consts.wuffs b/std/json/common_consts.wuffs
index 7246351..ddcee1f 100644
--- a/std/json/common_consts.wuffs
+++ b/std/json/common_consts.wuffs
@@ -21,18 +21,18 @@
 
 pri status "#internal error: inconsistent I/O"
 
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 0
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
 
 // DECODER_DEPTH_MAX_INCL is the maximum supported recursion depth: how deeply
 // nested [] arrays and {} objects can be.
 //
 // The JSON spec itself does not define a limit, but allows implementations to
 // set their own limits.
-pub const DECODER_DEPTH_MAX_INCL base.u64 = 1024
+pub const DECODER_DEPTH_MAX_INCL : base.u64 = 1024
 
 // DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL is the minimum length of the dst
 // wuffs_base__token_buffer passed to the decoder.
-pub const DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL base.u64 = 1
+pub const DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL : base.u64 = 1
 
 // DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL is the minimum length of the src
 // wuffs_base__io_buffer passed to the decoder.
@@ -42,7 +42,7 @@
 // the backing array's length, not the length of the JSON-formatted input per
 // se. It is perfectly valid to decode "[1,2]" (of length 5) as JSON, as long
 // as that content is placed in an io_buffer whose data.len is at least 100.
-pub const DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL base.u64 = 100
+pub const DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL : base.u64 = 100
 
 // DECODER_NUMBER_LENGTH_MAX_INCL is the longest supported byte length for a
 // JSON number. Unlike JSON strings, this package's tokenizer never splits a
@@ -52,7 +52,7 @@
 //
 // The JSON spec itself does not define a limit, but allows implementations to
 // set their own limits.
-pri const DECODER_NUMBER_LENGTH_MAX_INCL base.u64 = 99
+pri const DECODER_NUMBER_LENGTH_MAX_INCL : base.u64 = 99
 
 // --------
 
@@ -77,7 +77,7 @@
 //
 // If the element is zero then "\i" is invalid, or it is a special case, the
 // start of "\x12", "\u1234" or "\U12345678".
-pri const LUT_BACKSLASHES array[256] base.u8 = [
+pri const LUT_BACKSLASHES : array[256] base.u8 = [
 	// 0     1     2     3     4     5     6     7
 	// 8     9     A     B     C     D     E     F
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // 0x00 ..= 0x07.
@@ -122,7 +122,7 @@
 // LUT_QUIRKY_BACKSLASHES is discussed in the LUT_BACKSLASHES comment. The
 // first element (index 0) is not used, but 8 is a round power of 2, so
 // enforcing index-in-bounds is a simple "&7" operation.
-pri const LUT_QUIRKY_BACKSLASHES array[8] base.u8 = [
+pri const LUT_QUIRKY_BACKSLASHES : array[8] base.u8 = [
 	0x00, 0x07, 0x1B, 0x0A, 0x3F, 0x27, 0x0B, 0x00,
 ]
 
@@ -145,7 +145,7 @@
 //    UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
 //                  %xF4 %x80-8F 2( UTF8-tail )
 //    UTF8-tail   = %x80-BF
-pri const LUT_CHARS array[256] base.u8 = [
+pri const LUT_CHARS : array[256] base.u8 = [
 	// 0     1     2     3     4     5     6     7
 	// 8     9     A     B     C     D     E     F
 	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,  // 0x00 ..= 0x07. C0 control codes.
@@ -187,19 +187,19 @@
 	// 8     9     A     B     C     D     E     F
 ]
 
-pri const CLASS_WHITESPACE base.u8 = 0x00
-pri const CLASS_STRING base.u8 = 0x01
-pri const CLASS_COMMA base.u8 = 0x02
-pri const CLASS_COLON base.u8 = 0x03
-pri const CLASS_NUMBER base.u8 = 0x04
-pri const CLASS_OPEN_CURLY_BRACE base.u8 = 0x05
-pri const CLASS_CLOSE_CURLY_BRACE base.u8 = 0x06
-pri const CLASS_OPEN_SQUARE_BRACKET base.u8 = 0x07
-pri const CLASS_CLOSE_SQUARE_BRACKET base.u8 = 0x08
-pri const CLASS_FALSE base.u8 = 0x09
-pri const CLASS_TRUE base.u8 = 0x0A
-pri const CLASS_NULL_NAN_INF base.u8 = 0x0B
-pri const CLASS_COMMENT base.u8 = 0x0C
+pri const CLASS_WHITESPACE           : base.u8 = 0x00
+pri const CLASS_STRING               : base.u8 = 0x01
+pri const CLASS_COMMA                : base.u8 = 0x02
+pri const CLASS_COLON                : base.u8 = 0x03
+pri const CLASS_NUMBER               : base.u8 = 0x04
+pri const CLASS_OPEN_CURLY_BRACE     : base.u8 = 0x05
+pri const CLASS_CLOSE_CURLY_BRACE    : base.u8 = 0x06
+pri const CLASS_OPEN_SQUARE_BRACKET  : base.u8 = 0x07
+pri const CLASS_CLOSE_SQUARE_BRACKET : base.u8 = 0x08
+pri const CLASS_FALSE                : base.u8 = 0x09
+pri const CLASS_TRUE                 : base.u8 = 0x0A
+pri const CLASS_NULL_NAN_INF         : base.u8 = 0x0B
+pri const CLASS_COMMENT              : base.u8 = 0x0C
 
 // LUT_CLASSES is:
 //  - 0x00 (bitmask 0x0001) is CLASS_WHITESPACE.
@@ -245,7 +245,7 @@
 //
 // Comments are always expected. Whether the relevant quirks are enabled are
 // checked elsewhere.
-pri const LUT_CLASSES array[256] base.u8[..= 0x0F] = [
+pri const LUT_CLASSES : array[256] base.u8[..= 0x0F] = [
 	// 0     1     2     3     4     5     6     7
 	// 8     9     A     B     C     D     E     F
 	0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,  // 0x00 ..= 0x07.
@@ -287,7 +287,7 @@
 	// 8     9     A     B     C     D     E     F
 ]
 
-pri const LUT_DECIMAL_DIGITS array[256] base.u8 = [
+pri const LUT_DECIMAL_DIGITS : array[256] base.u8 = [
 	// 0     1     2     3     4     5     6     7
 	// 8     9     A     B     C     D     E     F
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // 0x00 ..= 0x07.
@@ -329,7 +329,7 @@
 	// 8     9     A     B     C     D     E     F
 ]
 
-pri const LUT_HEXADECIMAL_DIGITS array[256] base.u8 = [
+pri const LUT_HEXADECIMAL_DIGITS : array[256] base.u8 = [
 	// 0     1     2     3     4     5     6     7
 	// 8     9     A     B     C     D     E     F
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // 0x00 ..= 0x07.
diff --git a/std/json/decode_quirks.wuffs b/std/json/decode_quirks.wuffs
index b0ad9a1..3d6b01d 100644
--- a/std/json/decode_quirks.wuffs
+++ b/std/json/decode_quirks.wuffs
@@ -33,22 +33,22 @@
 //
 // Any indentation following a new line is not stripped, but remains part of
 // the decoded string.
-pub const QUIRK_ALLOW_ASCII_CONTROL_CODES base.u32 = 0x4909_9400 | 0x00
+pub const QUIRK_ALLOW_ASCII_CONTROL_CODES : base.u32 = 0x4909_9400 | 0x00
 
 // When this quirk is enabled, e.g. "abc\az" is accepted as a JSON string,
 // equivalent to "abc\u0007z", containing an ASCII Bell control character.
-pub const QUIRK_ALLOW_BACKSLASH_A base.u32 = 0x4909_9400 | 0x01
+pub const QUIRK_ALLOW_BACKSLASH_A : base.u32 = 0x4909_9400 | 0x01
 
 // When this quirk is enabled, e.g. "abc\U0001F4A9z" is accepted as a JSON
 // string, equivalent to "abc\uD83D\uDCA9z", containing the U+0001F4A9 PILE OF
 // POO Unicode code point. There are exactly 8 encoded bytes after each "\U".
 //
 // This quirk can combine with QUIRK_REPLACE_INVALID_UNICODE.
-pub const QUIRK_ALLOW_BACKSLASH_CAPITAL_U base.u32 = 0x4909_9400 | 0x02
+pub const QUIRK_ALLOW_BACKSLASH_CAPITAL_U : base.u32 = 0x4909_9400 | 0x02
 
 // When this quirk is enabled, e.g. "abc\ez" is accepted as a JSON string,
 // equivalent to "abc\u001Bz", containing an ASCII Escape control character.
-pub const QUIRK_ALLOW_BACKSLASH_E base.u32 = 0x4909_9400 | 0x03
+pub const QUIRK_ALLOW_BACKSLASH_E : base.u32 = 0x4909_9400 | 0x03
 
 // When this quirk is enabled, e.g. ("abc\
 // z") is accepted as a JSON string, equivalent to "abc\nz".
@@ -59,20 +59,20 @@
 //
 // Any indentation following a new line is not stripped, but remains part of
 // the decoded string.
-pub const QUIRK_ALLOW_BACKSLASH_NEW_LINE base.u32 = 0x4909_9400 | 0x04
+pub const QUIRK_ALLOW_BACKSLASH_NEW_LINE : base.u32 = 0x4909_9400 | 0x04
 
 // When this quirk is enabled, e.g. "abc\?z" is accepted as a JSON string,
 // equivalent to "abc?z".
-pub const QUIRK_ALLOW_BACKSLASH_QUESTION_MARK base.u32 = 0x4909_9400 | 0x05
+pub const QUIRK_ALLOW_BACKSLASH_QUESTION_MARK : base.u32 = 0x4909_9400 | 0x05
 
 // When this quirk is enabled, e.g. "abc\'z" is accepted as a JSON string,
 // equivalent to "abc'z".
-pub const QUIRK_ALLOW_BACKSLASH_SINGLE_QUOTE base.u32 = 0x4909_9400 | 0x06
+pub const QUIRK_ALLOW_BACKSLASH_SINGLE_QUOTE : base.u32 = 0x4909_9400 | 0x06
 
 // When this quirk is enabled, e.g. "abc\vz" is accepted as a JSON string,
 // equivalent to "abc\u000Bz", containing an ASCII Vertical Tab control
 // character.
-pub const QUIRK_ALLOW_BACKSLASH_V base.u32 = 0x4909_9400 | 0x07
+pub const QUIRK_ALLOW_BACKSLASH_V : base.u32 = 0x4909_9400 | 0x07
 
 // When this quirk is enabled, e.g. "abc\xeFz" is accepted as a JSON string,
 // There are exactly 2 encoded bytes after each "\x".
@@ -91,11 +91,11 @@
 // 0xAF and 0x7A. The UTF-8 encoding of U+00EF LATIN SMALL LETTER I WITH
 // DIAERESIS is the two byte sequence (0xC3, 0xAF). Decoded strings are still
 // valid UTF-8 and tokens still split on UTF-8 boundaries.
-pub const QUIRK_ALLOW_BACKSLASH_X base.u32 = 0x4909_9400 | 0x08
+pub const QUIRK_ALLOW_BACKSLASH_X : base.u32 = 0x4909_9400 | 0x08
 
 // When this quirk is enabled, e.g. "abc\0z" is accepted as a JSON string,
 // equivalent to "abc\u0000z", containing an ASCII NUL control character.
-pub const QUIRK_ALLOW_BACKSLASH_ZERO base.u32 = 0x4909_9400 | 0x09
+pub const QUIRK_ALLOW_BACKSLASH_ZERO : base.u32 = 0x4909_9400 | 0x09
 
 // When this quirk is enabled, "/* C/C++ style block comments */" are accepted
 // anywhere whitespace would be, although see the QUIRK_ALLOW_TRAILING_NEW_LINE
@@ -103,7 +103,7 @@
 //
 // They produce WUFFS_BASE__TOKEN__VBD__FILLER__COMMENT_BLOCK tokens. The token
 // chain's source bytes includes the starting "/*" and the ending "*/".
-pub const QUIRK_ALLOW_COMMENT_BLOCK base.u32 = 0x4909_9400 | 0x0A
+pub const QUIRK_ALLOW_COMMENT_BLOCK : base.u32 = 0x4909_9400 | 0x0A
 
 // When this quirk is enabled, "// C/C++ style line comments\n" are accepted
 // anywhere whitespace would be, although see the QUIRK_ALLOW_TRAILING_NEW_LINE
@@ -118,7 +118,7 @@
 // Even if the line comments are on consecutive lines, each line comment is a
 // separate token chain. There may be whitespace tokens between one line
 // comment's ending "\n" and the next one's starting "//".
-pub const QUIRK_ALLOW_COMMENT_LINE base.u32 = 0x4909_9400 | 0x0B
+pub const QUIRK_ALLOW_COMMENT_LINE : base.u32 = 0x4909_9400 | 0x0B
 
 // When this quirk is enabled, there may be a comma after the final array
 // element or object key-value pair and before the closing "]" or "}". A comma
@@ -127,12 +127,12 @@
 //
 // For example, `[1,]`, `[1,2,3,]` and `{"k":"v",}` all become acceptable, but
 // `[,]`, `{,}` and `{"k",:"v"}` are still rejected.
-pub const QUIRK_ALLOW_EXTRA_COMMA base.u32 = 0x4909_9400 | 0x0C
+pub const QUIRK_ALLOW_EXTRA_COMMA : base.u32 = 0x4909_9400 | 0x0C
 
 // When this quirk is enabled, "inf", "Infinity", "NAN" and their
 // case-insensitive variants, optionally preceded immediately by "-" or "+",
 // are accepted anywhere a JSON number would be.
-pub const QUIRK_ALLOW_INF_NAN_NUMBERS base.u32 = 0x4909_9400 | 0x0D
+pub const QUIRK_ALLOW_INF_NAN_NUMBERS : base.u32 = 0x4909_9400 | 0x0D
 
 // When this quirk is enabled, the input byte stream may optionally start with
 // "\x1E" (the ASCII Record Separator control character). That byte is skipped
@@ -143,7 +143,7 @@
 //
 // When combined with QUIRK_ALLOW_TRAILING_NEW_LINE, this format is also known
 // as RFC 7464, Json Text Sequences and MIME type "application/json-seq".
-pub const QUIRK_ALLOW_LEADING_ASCII_RECORD_SEPARATOR base.u32 = 0x4909_9400 | 0x0E
+pub const QUIRK_ALLOW_LEADING_ASCII_RECORD_SEPARATOR : base.u32 = 0x4909_9400 | 0x0E
 
 // When this quirk is enabled, the input byte stream may optionally start with
 // "\xEF\xBB\xBF", the UTF-8 encoding of the Unicode BOM (Byte Order Mark).
@@ -151,7 +151,7 @@
 //
 // When combined with QUIRK_ALLOW_LEADING_ASCII_RECORD_SEPARATOR, either mark
 // may come first in the byte stream.
-pub const QUIRK_ALLOW_LEADING_UNICODE_BYTE_ORDER_MARK base.u32 = 0x4909_9400 | 0x0F
+pub const QUIRK_ALLOW_LEADING_UNICODE_BYTE_ORDER_MARK : base.u32 = 0x4909_9400 | 0x0F
 
 // When this quirk is enabled, following a successful decoding of a top-level
 // JSON value, any trailing whitespace (ASCII characters 0x09, 0x0A, 0x0D or
@@ -187,7 +187,7 @@
 // end-of-file or '\n' is encountered. Treating this as an error avoids any
 // ambiguity in accounting for new lines within a block comment or ending a
 // line comment.
-pub const QUIRK_ALLOW_TRAILING_NEW_LINE base.u32 = 0x4909_9400 | 0x10
+pub const QUIRK_ALLOW_TRAILING_NEW_LINE : base.u32 = 0x4909_9400 | 0x10
 
 // When this quirk is enabled, invalid UTF-8 inside a JSON string is accepted.
 // Each byte of invalid UTF-8 is equivalent to "\uFFFD", the Unicode
@@ -203,4 +203,4 @@
 // When combined with QUIRK_ALLOW_BACKSLASH_CAPITAL_U, a "\U12345678" 10-byte
 // unit that is an invalid Unicode code point (i.e. in the range U+D800 ..=
 // U+DFFF or above U+10FFFF) is similarly replaced with U+FFFD.
-pub const QUIRK_REPLACE_INVALID_UNICODE base.u32 = 0x4909_9400 | 0x11
+pub const QUIRK_REPLACE_INVALID_UNICODE : base.u32 = 0x4909_9400 | 0x11
diff --git a/std/lzw/decode_lzw.wuffs b/std/lzw/decode_lzw.wuffs
index 3461a42..fd73cf1 100644
--- a/std/lzw/decode_lzw.wuffs
+++ b/std/lzw/decode_lzw.wuffs
@@ -24,7 +24,7 @@
 //  - 85be5b9 Delete the obsolete lzw.decoder.suffixes array
 // and the roll back has combined numbers:
 //  - 3056a84 Roll back 3 recent lzw.decoder.suffixes commits
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 0
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
 
 pub struct decoder? implements base.io_transformer(
 	// set_literal_width_arg is 1 plus the saved argument passed to
diff --git a/std/wbmp/decode_wbmp.wuffs b/std/wbmp/decode_wbmp.wuffs
index fad72ee..6159dcf 100644
--- a/std/wbmp/decode_wbmp.wuffs
+++ b/std/wbmp/decode_wbmp.wuffs
@@ -14,7 +14,7 @@
 
 pub status "#bad header"
 
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 0
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
 
 pub struct decoder? implements base.image_decoder(
 	width  : base.u32,
diff --git a/std/zlib/decode_zlib.wuffs b/std/zlib/decode_zlib.wuffs
index a573fbe..8fef738 100644
--- a/std/zlib/decode_zlib.wuffs
+++ b/std/zlib/decode_zlib.wuffs
@@ -24,7 +24,7 @@
 pub status "#incorrect dictionary"
 
 // TODO: reference deflate.DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE.
-pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE base.u64 = 1
+pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 1
 
 pub struct decoder? implements base.io_transformer(
 	bad_call_sequence : base.bool,