Files
c/meson.build
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

108 lines
3.0 KiB
Meson

project('zap-c', ['c','cpp'], meson_version: '>=1.0.0', default_options : ['c_std=c99', 'cpp_std=c++17'])
common_c_args = []
common_cpp_args = ['-std=c++17']
libzap_c_args = []
libzap_src = [
'lib' / 'zap-malloc.c',
'lib' / 'zap-stream.c',
'lib' / 'zap.c',
]
libzap = library('zap', libzap_src,
c_args : common_c_args + libzap_c_args,
dependencies: [],
implicit_include_directories: false,
include_directories: include_directories(['compiler', 'lib'])
)
libzap_dep = declare_dependency(
link_with: libzap,
dependencies: [],
include_directories: include_directories(['compiler', 'lib'])
)
zapc_src = [
'compiler' / 'zapc-c.c',
'compiler' / 'schema.zap.c',
'compiler' / 'str.c'
]
zapc_c = executable('zapc-c', zapc_src,
include_directories: include_directories(['lib']),
dependencies: [libzap_dep],
c_args : common_c_args,
)
# zapgen reads a .zap schema - written either way, indentation or braces -
# and writes the C it describes.
zapgen_lib_src = [
'gen' / 'arena.c',
'gen' / 'desugar.c',
'gen' / 'emit.c',
'gen' / 'parse.c',
'gen' / 'rune.c',
'compiler' / 'str.c',
]
zapgen_inc = include_directories(['gen', 'compiler', 'lib'])
zapgen = executable('zapgen', zapgen_lib_src + ['gen' / 'main.c'],
include_directories: zapgen_inc,
c_args : common_c_args,
)
zap_test_src = [
'tests' / 'zap-test.cpp',
'tests' / 'zap-stream-test.cpp',
'tests' / 'example-test.cpp',
'tests' / 'addressbook.zap.c',
'compiler' / 'test.zap.c',
'compiler' / 'schema-test.cpp',
'compiler' / 'schema.zap.c'
]
if get_option('enable_tests') and not meson.is_subproject()
thread_dep = dependency('threads')
gtest_proj = subproject('gtest')
gtest_dep = gtest_proj.get_variable('gtest_dep')
gmock_dep = gtest_proj.get_variable('gmock_dep')
# Sanitizer runtimes are pulled in by the compiler driver via the
# -fsanitize flags that meson's b_sanitize option adds to both compile
# and link steps. No explicit find_library() is needed (and Apple clang
# ships no findable libasan/libubsan), so b_sanitize alone is portable.
test_dependencies = [libzap_dep, gtest_dep, gmock_dep, thread_dep]
exe = executable('zap-test', zap_test_src,
include_directories: include_directories(['lib', 'tests', 'compiler']),
dependencies: test_dependencies,
c_args : common_c_args,
cpp_args : common_cpp_args,
install: false,
install_rpath: '',
implicit_include_directories: false
)
test('zap-test', exe)
# The schema test generates its own bindings and compiles them, so a
# generator that cannot generate, or generates C that does not build,
# fails the suite rather than the next release.
echo_h = custom_target('echo.zap.h',
input: 'gen' / 'echo.zap',
output: 'echo.zap.h',
command: [zapgen, '-o', '@OUTDIR@', '@INPUT@'],
)
zapgen_test = executable('zapgen-test',
zapgen_lib_src + ['gen' / 'test.c', echo_h],
include_directories: zapgen_inc,
c_args : common_c_args,
install: false,
)
test('zapgen-test', zapgen_test)
endif