mirror of
https://github.com/zap-proto/c.git
synced 2026-10-30 15:05:52 +00:00
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.
131 lines
3.0 KiB
Go
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;
|
|
}
|
|
`
|