awk-polycall
v2.0.0
Published
AWK binding for Polycall: a gawk extension that runs the real core in-process (@load "awk_polycall"), and a POSIX AWK library that drives the polycall CLI (gawk, mawk, busybox awk).
Maintainers
Readme
awk-polycall
AWK binding for Polycall (binding
ABI v1, Polycall >= 1.1.0), published as awk-polycall. It has
two layers, because only gawk can load C:
| layer | AWKs | reaches the core | covers |
| --- | --- | --- | --- |
| gawk extension ext/awk_polycall.c (@load "awk_polycall") | gawk >= 5.0 | in-process: links libpolycall (pkg-config polycall) | the whole binding ABI: polycall_ffi_run_config(path, 1) exactly, describe, call, peer nodes with caller-sized receive buffers, unregister, binary payloads |
| POSIX library src/awk_polycall.awk | gawk, mawk, busybox awk, any POSIX awk | runs the installed polycall CLI through getline pipes | version check, config validation (strict mode approximated, see below), call, peer nodes as polycall peer serve processes |
gawk extension
make ext # build/awk_polycall.so (.dll on Windows)
AWKLIBPATH=build gawk -b -f examples/gawk_ext.awk@load "awk_polycall"
BEGIN {
if (polycall::run_config("awk-polycallrc") != polycall::OK) { # = polycall_ffi_run_config(path, 1)
print polycall::strerror(polycall::STATUS) ": " polycall::last_error() > "/dev/stderr"
exit 1
}
inbox = polycall::peer_open("inbox", "127.0.0.1:0") # handle > 0, or a negative status
outbox = polycall::peer_open("outbox") # no bind = send-only
polycall::peer_send(outbox, polycall::peer_endpoint(inbox), "hello", "m-1")
if (polycall::peer_recv(inbox, 5000, msg) == polycall::OK)
print msg["from"], msg["id"], msg["payload"], msg["length"]
polycall::peer_close(outbox); polycall::peer_close(inbox)
}All functions live in the polycall:: namespace and map 1:1 onto
polycall.h:
| function | returns |
| --- | --- |
| abi_version(), version() | polycall_ffi_abi_version(), polycall_ffi_version() |
| strerror(code), last_error() | the core's status name / the latest failure's detail |
| run_config(path [, run = 1]) | status |
| describe(path) | JSON text ("" on failure) |
| call(endpoint, service, op [, input_json [, timeout_ms [, result]]]) | status; result["output"] = output JSON or the error object, result["length"] |
| peer_open(node_id [, bind [, token]]) | handle (> 0) or status (< 0); bind ""/absent = send-only |
| peer_close(h), peer_register(h, id, ep), peer_unregister(h, id), peer_ping(h, peer [, timeout]), peer_cancel(h) | status |
| peer_endpoint(h), peer_node_id(h), peer_list(h), peer_health(h) | text ("" on failure) |
| peer_send(h, peer, payload [, message_id [, timeout_ms]]) | status; payload is binary-safe |
| peer_recv(h [, timeout_ms [, msg [, payload_cap]]]) | status; msg["from"], msg["id"], msg["payload"], msg["length"]; a message larger than payload_cap (default 1 MiB) gives E_TOO_LARGE, msg["length"] = needed, and stays queued |
Every call sets polycall::STATUS (0 = polycall::OK, negative =
polycall::E_*, all 19 codes are defined); polycall::FOREVER
(4294967295) waits indefinitely. Arguments the C API cannot take (a path
with a NUL byte, a timeout outside 0..2^32-1, a fractional number) are
refused by the binding with E_INVALID_ARGUMENT and a detail in
last_error(). Payloads are byte strings: run gawk with -b (or
LC_ALL=C) so length()/substr() count bytes, and set BINMODE = 3 on
Windows to read binary files without CRLF translation. A gawk program has
one thread, so a blocked peer_recv ends on a message, its timeout, or the
node's close; peer_cancel exists for completeness.
Loading: the extension checks polycall_ffi_abi_version() == 1 before it
registers anything (otherwise it prints why and defines no function), and
on Linux it is linked with -z now, so an old 1.0 libpolycall.so.1
without the ABI v1 symbols fails at load time ("undefined symbol"), never at
a later call. The module is named awk_polycall, not polycall: on
Windows a DLL called polycall.dll would shadow the MSVC core of the same
name.
Building needs gawk's gawkapi.h, a C compiler and the core's pkg-config
file (or POLYCALL_CFLAGS / POLYCALL_LIBS). On Windows the extension
is built with MSYS2 UCRT64 gcc and loaded by Git-for-Windows or MSYS2 gawk
(both API 4.1); give GAWK_CFLAGS=-I<dir holding only gawkapi.h> so the
MSYS headers next to it do not replace the mingw ones, and link either core:
make ext CC=gcc GAWK_CFLAGS=-I/path/to/gawkapi-dir \
POLYCALL_CFLAGS=-I$CORE/include/polycall POLYCALL_LIBS="-L$CORE/lib -lpolycall"POSIX library
awk -v polycall_cli=/path/to/polycall -f src/awk_polycall.awk -f examples/basic.awkFunctions: polycall_check(), polycall_version(), polycall_strerror()
(the status name), polycall_last_error(), polycall_run_config(path
[, strict]), polycall_call(endpoint, service, op, input, timeout)
(output in POLYCALL_OUTPUT), polycall_peer_open/close/endpoint/node_id/
register/list/ping/health/send/send_file/recv/recv_file,
polycall_cleanup(); data comes back in POLYCALL_OUTPUT and
POLYCALL_MSG_*. See the comments in src/awk_polycall.awk.
Limits, because the CLI is the transport: the CLI has no strict validation
mode, so polycall_run_config(path, 1) applies strictness to the CLI's
report (warning count, tls_enabled=true) — use the gawk extension for the
core's exact run=1 semantics. There is no unregister (no CLI command), no
caller-sized receive buffer, and receive timeouts are capped at 30 s. Text
payloads cannot contain NUL in a POSIX awk string; use
polycall_peer_send_file / polycall_peer_recv_file for binary data. On
MSYS / Git Bash / Cygwin, file arguments are handed to a native
polycall.exe as Windows paths (cygpath -m).
Tests
npm test # = sh tests/run-all.sh, plus the npm package test
make valgrind # the gawk extension test under valgrind memchecktests/run-gawk-ext-test.sh builds the extension and runs
tests/gawk_ext_test.awk (the binding-ABI checklist, call against
polycall start and polycall daemon start, payloads both ways with a
polycall peer serve C node, four concurrent gawk sender processes) plus
the loader-error cases (missing / old / ABI-2 library, extension not on
AWKLIBPATH). tests/run-awk-test.sh runs tests/awk_polycall_test.awk
with every AWK it finds (gawk, mawk, busybox awk). Both use the real core
and the real CLI; a missing AWK, CLI or toolchain is exit 77 (SKIP), never
a pass. Tested on Linux x86-64 (Debian 12: gawk 5.2.1, mawk, busybox awk)
and Windows x64 (Git-for-Windows gawk 5.4.1 with both the MSVC
polycall.dll and the UCRT64 libpolycall.dll cores).
Install from npm
npm install awk-polycallThe npm package is a source package: require('awk-polycall')
returns absolute paths (awk, extSource, makefile, config, ...);
build the extension from it with make -C node_modules/awk-polycall ext.
The configuration override awk-polycallrc uses the
core's own grammar (polycall config validate).
Author
Nnamdi Michael Okpala — [email protected]
