Files
zeekay a4debdba80 Read a .zap schema the way every other ZAP language reads it
C had no front end for the schema language at all. The compiler directory
holds a code generator plugin, which is handed a request by another tool
and never sees schema text, so a .zap file could not be read here in
either form -- indentation or braces.

zapgen reads it. Whitespace-significant source is rewritten into the brace
form BEFORE the parser, exactly as the Go reference does it: a near
identity that only adds the two tokens the brace grammar requires where
the indented form leaves them out. A file already in braces comes back
byte for byte, so one grammar reads both styles and neither is a dialect
of the other.

The identifier grammar is not restated. Its Unicode ranges are taken from
Go's own tables by gen/table, so a name this parser accepts is a name the
Go parser accepts, and a Go release that moves Unicode moves both.

The generated header gives each struct a view over the message bytes and
one reader per field at a fixed offset. Reading is in place and total: a
field past the end reads zero, a pointer into the head or out of the
message reads null. zap_view.h is the one place that says so.

Checked against the Go front end on every .zap in the estate plus 55
adversarial cases and 15000 random ones: the brace form each writes is
byte-identical, the layout each reads is field-for-field the same, and
they accept and refuse the same files for the same stated reason. A
message built by the Go runtime reads back through these bindings with
every field equal.
2026-09-15 11:32:12 -07:00

131 lines
3.0 KiB
Go

// SPDX-License-Identifier: BSD-3-Clause-Eco
//
// table writes gen/rune.c: the Unicode letter and digit ranges the
// schema parser's identifier grammar is defined over.
//
// The Go reference parser spells that grammar as unicode.IsLetter and
// unicode.IsDigit. C has no such notion, so the ranges are taken FROM Go
// rather than restated: same table, same answer, no second definition of
// what a letter is. Re-run after a Go release that updates Unicode:
//
// go run ./table > ../rune.c
package main
import (
"fmt"
"os"
"unicode"
)
func main() {
w := os.Stdout
fmt.Fprintln(w, "// SPDX-License-Identifier: BSD-3-Clause-Eco")
fmt.Fprintln(w, "//")
fmt.Fprintf(w, "// Generated by gen/table from Go's unicode tables (Unicode %s).\n", unicode.Version)
fmt.Fprintln(w, "// DO NOT EDIT. Run: go run ./table > ../rune.c")
fmt.Fprintln(w)
fmt.Fprintln(w, `#include "rune.h"`)
fmt.Fprintln(w)
table(w, "letter", unicode.Letter)
table(w, "digit", unicode.Digit)
fmt.Fprint(w, body)
}
func table(w *os.File, name string, t *unicode.RangeTable) {
fmt.Fprintf(w, "static const struct span %s16[] = {\n", name)
for _, r := range t.R16 {
fmt.Fprintf(w, "\t{0x%04X, 0x%04X, %d},\n", r.Lo, r.Hi, r.Stride)
}
fmt.Fprintln(w, "};")
fmt.Fprintf(w, "static const struct span %s32[] = {\n", name)
for _, r := range t.R32 {
fmt.Fprintf(w, "\t{0x%06X, 0x%06X, %d},\n", r.Lo, r.Hi, r.Stride)
}
fmt.Fprintln(w, "};")
fmt.Fprintln(w)
}
const body = `static int inspan(const struct span *s, size_t n, uint32_t r)
{
size_t lo = 0, hi = n;
while (lo < hi) {
size_t mid = lo + (hi - lo) / 2;
if (r < s[mid].lo) {
hi = mid;
} else if (r > s[mid].hi) {
lo = mid + 1;
} else {
return (r - s[mid].lo) % s[mid].stride == 0;
}
}
return 0;
}
static int inset(const struct span *s16, size_t n16,
const struct span *s32, size_t n32, uint32_t r)
{
if (r <= 0xFFFF)
return inspan(s16, n16, r);
return inspan(s32, n32, r);
}
int rune_letter(uint32_t r)
{
return inset(letter16, sizeof(letter16) / sizeof(letter16[0]),
letter32, sizeof(letter32) / sizeof(letter32[0]), r);
}
int rune_digit(uint32_t r)
{
return inset(digit16, sizeof(digit16) / sizeof(digit16[0]),
digit32, sizeof(digit32) / sizeof(digit32[0]), r);
}
#define BAD 0xFFFD
uint32_t rune_read(const char *src, size_t len, size_t *size)
{
const unsigned char *s = (const unsigned char *)src;
uint32_t r;
size_t n;
if (len == 0) {
*size = 0;
return 0;
}
*size = 1;
if (s[0] < 0x80)
return s[0];
if (s[0] < 0xC2 || s[0] > 0xF4)
return BAD;
if (s[0] < 0xE0) {
n = 2;
r = s[0] & 0x1F;
} else if (s[0] < 0xF0) {
n = 3;
r = s[0] & 0x0F;
} else {
n = 4;
r = s[0] & 0x07;
}
if (len < n)
return BAD;
for (size_t i = 1; i < n; i++) {
if ((s[i] & 0xC0) != 0x80)
return BAD;
r = (r << 6) | (s[i] & 0x3F);
}
/* Reject overlong forms, surrogates and out-of-range code points, as
* utf8.DecodeRune does. */
if (n == 3 && r < 0x800)
return BAD;
if (n == 4 && (r < 0x10000 || r > 0x10FFFF))
return BAD;
if (r >= 0xD800 && r <= 0xDFFF)
return BAD;
*size = n;
return r;
}
`