cZPeg generates recursive descent parsers at compile-time in Zig using a
functional API inspired by LPeg to declare and compose patterns. There is
also an re
module for generating parsers from more compact PEG-style pattern
strings. The compiled parser can then be used to match strings at runtime or
comptime!
Make sure you have a recent Zig, we're currently testing with 0.8.0-dev drops from early 2021.
Until Zig's package distribution infrastructure has evolved, it's probably
easiest to simply clone the contents of the czpeg/src/
tree into your
project somewhere, then from your build.zig
, add:
// build.zig
// if this is your LibExeObjStep
const exe = b.addExecutable(...);
// add new package like this
exe.addPackagePath("czpeg", "lib/czpeg.zig");
Now you can import and use the package in your project:
const std = @import("std");
const re = @import("czpeg").re;
test "greeting" {
const pat = re.compile("'Hello' %s+ {%w+}'!'", .{});
std.testing.expectEqualStrings("world", p.matchLean("Hello world!").?);
}
From the cZPeg root, run all available tests using the usual chant:
$ zig build test
It's a good idea to run this regularly as Zig matures to make sure everything stays in sync.
A brief interface comparison for reference. p
and q
are other patterns,
n
is a non-negative integer:
cZPeg | cZPeg.re | LPeg | LPeg.re | PCRE |
---|---|---|---|---|
pat("s") str("s") |
's' "s" |
P(s) S(s) |
's' "s" |
s |
pat(1) any(1) |
. |
P(1) |
. |
. |
pat(n) any(n) |
.^n |
P(n) |
.^n |
.{n} |
set("aeiou") |
[aeiou] |
S('aeiou') |
[aeiou] |
[aeiou] |
span('0', '9') |
[0-9] %d |
R('09') |
[0-9] %d |
[0-9] \d |
pat(-1) |
!. |
P(-1) |
!. |
$ |
rep(0, 1, p) p.rep(0, 1) |
p? |
patt^-1 |
p? |
p? |
rep(0, -1, p) p.rep(0, -1) |
p* |
patt^0 |
p* |
p* |
rep(1, -1, p) p.rep(1, -1) |
p+ |
patt^1 |
p+ |
p+ |
rep(n, -1, p) p.rep(n, -1) |
p^+n |
p^n |
p^+n |
p{n,} |
rep(0, n, p) p.rep(0, n) |
p^-n |
p^-n |
p^-n |
p{,n} |
rep(n, n, p) p.rep(n, n) |
p^n |
p^n |
p{n} |
|
pat(.{p, q}) seq(.{p, q}) |
p q |
p * q |
p q |
pq |
alt(.{p, q}) |
p / q |
p + q |
p / q |
p|q |
if_(p) |
&p |
#p |
&p |
(?=p) |
not(p) |
!p |
-p |
!p |
(?!p) |
(p) |
(p) |
(p) |
(p) |
(:?p) |
pos() |
{} |
Cp() |
{} |
|
cap(p) p.cap() |
{p} |
C(p) |
{p} |
(p) |
grok(type, f, p) p.grok(type, f) |
p => f |
Cmt(p, f) |
p => f |
|
foldRep() |
Cf() |
~> |
||
ref(&g, "name", type) |
name |
V('name') |
name |
|
name = p |
name <- p |
{ name = p } |
name <- p |
|
unsupported | / cap B Carg Cb Cc Cg Cs |
{:p:} {~p~} {|p|} =name -> |
\n |