Regular expressions¶
A pattern compiled from a bytes literal is matched natively over a byte buffer. The compiler reads the pattern with CPython's own parser and writes a matcher function for it, and a function that searches with it lowers like any other.
Run it¶
The input is 50,000 generated lines of name = value pairs, words, and hex
numbers. The line starting with # is a timing; it differs between machines
and between the two runs below.
What it prints¶
python patterns.ppy
ppy run patterns.ppy
ppy emit ir patterns.ppy
1480 lines
ppyir 1
module @patterns
dialect core 1
dialect regex 1
func @patterns_count_words(%text: buffer<u8> {ownership = "borrowed", ppy.kind = "view"}) -> i64 attrs {effects = ["alloc", "may_raise", "read_memory"], ppy.abi = "ppy", ppy.qualname = "patterns.count_words", ppy.releases_gil = true, ppy.symbol = "ppy_patterns_count_words"} loc("examples/43_regex/patterns.ppy":15:0) {
^entry:
%0 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":16:4)
%n_addr = core.alloca : ptr<i64, stack>
core.store %0, %n_addr
%1 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":17:4)
%pos_addr = core.alloca : ptr<i64, stack>
core.store %1, %pos_addr
%m_found_addr = core.alloca : ptr<bool, stack>
%m_spans = core.alloca {count = 2} : ptr<i64, stack>
%2 = core.const 0 : i64
%3 = core.const 1 : i64
%4 = core.const 1 : i64
%5 = core.const 1 : i64
core.br ^while.head1 loc("examples/43_regex/patterns.ppy":18:4)
^while.head1:
%6 = core.load %pos_addr : i64 loc("examples/43_regex/patterns.ppy":19:8)
%7 = core.buffer_len %text : index
%8 = core.cast %7 : i64
%9, %10, %11 = core.call %text, %6, %8 {callee = @ppy.regex.0} : bool, i64, i64
core.store %9, %m_found_addr loc("examples/43_regex/patterns.ppy":19:8)
%12 = core.ptr_offset %m_spans, %2 : ptr<i64, stack>
core.store %10, %12
%13 = core.ptr_offset %m_spans, %3 : ptr<i64, stack>
core.store %11, %13
%14 = core.load %m_found_addr : bool loc("examples/43_regex/patterns.ppy":20:8)
%15 = core.const false : bool
%16 = core.cmp.eq %14, %15 : bool
core.cond_br %16, ^then4, ^else5
^then4:
%17 = core.load %n_addr : i64 loc("examples/43_regex/patterns.ppy":21:12)
core.ret %17
^else5:
%18 = core.load %n_addr : i64 loc("examples/43_regex/patterns.ppy":22:8)
%19 = core.add %18, %4 {overflow = "python"} : i64
core.store %19, %n_addr
%20 = core.ptr_offset %m_spans, %5 : ptr<i64, stack> loc("examples/43_regex/patterns.ppy":23:8)
%21 = core.load %20 : i64
core.store %21, %pos_addr
core.br ^while.head1
}
func @patterns_longest_word(%text: buffer<u8> {ownership = "borrowed", ppy.kind = "view"}) -> i64 attrs {effects = ["alloc", "may_raise", "read_memory"], ppy.abi = "ppy", ppy.qualname = "patterns.longest_word", ppy.releases_gil = true, ppy.symbol = "ppy_patterns_longest_word"} loc("examples/43_regex/patterns.ppy":26:0) {
^entry:
%0 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":27:4)
%best_addr = core.alloca : ptr<i64, stack>
core.store %0, %best_addr
%1 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":28:4)
%pos_addr = core.alloca : ptr<i64, stack>
core.store %1, %pos_addr
%m_found_addr = core.alloca : ptr<bool, stack>
%m_spans = core.alloca {count = 2} : ptr<i64, stack>
%2 = core.const 0 : i64
%3 = core.const 1 : i64
%4 = core.const 0 : i64
%5 = core.const 1 : i64
%start_addr = core.alloca : ptr<i64, stack>
%end_addr = core.alloca : ptr<i64, stack>
core.br ^while.head1 loc("examples/43_regex/patterns.ppy":29:4)
^while.head1:
%6 = core.load %pos_addr : i64 loc("examples/43_regex/patterns.ppy":29:4)
%7 = core.buffer_len %text : index
%8 = core.cast %7 : i64
%9 = core.cmp.lt %6, %8 : bool
core.cond_br %9, ^while.body2, ^while.end3
^while.body2:
%10 = core.load %pos_addr : i64 loc("examples/43_regex/patterns.ppy":30:8)
%11 = core.buffer_len %text : index
%12 = core.cast %11 : i64
%13, %14, %15 = core.call %text, %10, %12 {callee = @ppy.regex.0} : bool, i64, i64
core.store %13, %m_found_addr loc("examples/43_regex/patterns.ppy":30:8)
%16 = core.ptr_offset %m_spans, %2 : ptr<i64, stack>
core.store %14, %16
%17 = core.ptr_offset %m_spans, %3 : ptr<i64, stack>
core.store %15, %17
%18 = core.load %m_found_addr : bool loc("examples/43_regex/patterns.ppy":31:8)
%19 = core.const true : bool
%20 = core.xor %18, %19 : bool
core.cond_br %20, ^then4, ^else5
^while.end3:
%21 = core.load %best_addr : i64 loc("examples/43_regex/patterns.ppy":36:4)
core.ret %21
^then4:
core.br ^while.end3 loc("examples/43_regex/patterns.ppy":32:12)
^else5:
%22 = core.ptr_offset %m_spans, %4 : ptr<i64, stack> loc("examples/43_regex/patterns.ppy":33:8)
%23 = core.load %22 : i64
%24 = core.ptr_offset %m_spans, %5 : ptr<i64, stack>
%25 = core.load %24 : i64
core.store %23, %start_addr
core.store %25, %end_addr
%26 = core.load %best_addr : i64 loc("examples/43_regex/patterns.ppy":34:8)
%27 = core.load %end_addr : i64
%28 = core.load %start_addr : i64
%29 = core.sub %27, %28 {overflow = "python"} : i64
%30 = core.cmp.gt %29, %26 : bool
%31 = core.select %30, %29, %26 : i64
core.store %31, %best_addr
%32 = core.load %end_addr : i64 loc("examples/43_regex/patterns.ppy":35:8)
core.store %32, %pos_addr
core.br ^while.head1
}
func @patterns_sum_values(%text: buffer<u8> {ownership = "borrowed", ppy.kind = "view"}) -> i64 attrs {effects = ["alloc", "may_raise", "read_memory"], ppy.abi = "ppy", ppy.qualname = "patterns.sum_values", ppy.releases_gil = true, ppy.symbol = "ppy_patterns_sum_values"} loc("examples/43_regex/patterns.ppy":39:0) {
^entry:
%0 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":40:4)
%total_addr = core.alloca : ptr<i64, stack>
core.store %0, %total_addr
%1 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":41:4)
%pos_addr = core.alloca : ptr<i64, stack>
core.store %1, %pos_addr
%m_found_addr = core.alloca : ptr<bool, stack>
%m_spans = core.alloca {count = 6} : ptr<i64, stack>
%2 = core.const 0 : i64
%3 = core.const 1 : i64
%4 = core.const 2 : i64
%5 = core.const 3 : i64
%6 = core.const 4 : i64
%7 = core.const 5 : i64
%8 = core.const 0 : i64
%value_addr = core.alloca : ptr<i64, stack>
%9 = core.const 4 : i64
%10 = core.const 5 : i64
%11 = core.const 1 : i64
%i_addr = core.alloca : ptr<i64, stack>
%12 = core.const 10 : i64
%13 = core.const 48 : i64
%14 = core.const 1 : i64
core.br ^while.head1 loc("examples/43_regex/patterns.ppy":42:4)
^while.head1:
%15 = core.load %pos_addr : i64 loc("examples/43_regex/patterns.ppy":43:8)
%16 = core.buffer_len %text : index
%17 = core.cast %16 : i64
%18, %19, %20, %21, %22, %23, %24 = core.call %text, %15, %17 {callee = @ppy.regex.1} : bool, i64, i64, i64, i64, i64, i64
core.store %18, %m_found_addr loc("examples/43_regex/patterns.ppy":43:8)
%25 = core.ptr_offset %m_spans, %2 : ptr<i64, stack>
core.store %19, %25
%26 = core.ptr_offset %m_spans, %3 : ptr<i64, stack>
core.store %20, %26
%27 = core.ptr_offset %m_spans, %4 : ptr<i64, stack>
core.store %21, %27
%28 = core.ptr_offset %m_spans, %5 : ptr<i64, stack>
core.store %22, %28
%29 = core.ptr_offset %m_spans, %6 : ptr<i64, stack>
core.store %23, %29
%30 = core.ptr_offset %m_spans, %7 : ptr<i64, stack>
core.store %24, %30
%31 = core.load %m_found_addr : bool loc("examples/43_regex/patterns.ppy":44:8)
%32 = core.const false : bool
%33 = core.cmp.eq %31, %32 : bool
core.cond_br %33, ^then4, ^else5
^then4:
%34 = core.load %total_addr : i64 loc("examples/43_regex/patterns.ppy":45:12)
core.ret %34
^else5:
core.store %8, %value_addr loc("examples/43_regex/patterns.ppy":46:8)
%35 = core.ptr_offset %m_spans, %9 : ptr<i64, stack> loc("examples/43_regex/patterns.ppy":47:8)
%36 = core.load %35 : i64
%37 = core.ptr_offset %m_spans, %10 : ptr<i64, stack>
%38 = core.load %37 : i64
%39 = core.const 1 : i64
%40 = core.sub %38, %39 {overflow = "wrap"} : i64
%41 = core.const 0 : i64
%42 = core.buffer_len %text : index
%43 = core.cast %42 : i64
%44 = core.cmp.ge %36, %41 : bool
%45 = core.cmp.lt %40, %43 : bool
%46 = core.and %44, %45 : bool
core.guard %46 {kind = "bounds", message = "hoisted bounds check"}
core.store %36, %i_addr loc("examples/43_regex/patterns.ppy":47:8)
core.br ^for.head9
^for.head9:
%47 = core.load %i_addr : i64 loc("examples/43_regex/patterns.ppy":47:8)
%48 = core.cmp.lt %47, %38 : bool
core.cond_br %48, ^for.body10, ^for.end12
^for.body10:
%49 = core.load %value_addr : i64 loc("examples/43_regex/patterns.ppy":48:12)
%50 = core.mul %49, %12 {overflow = "python"} : i64
%51 = core.load %i_addr : i64
%52 = core.buffer_load %text, %51 : u8
%53 = core.cast %52 : i64
%54 = core.sub %53, %13 {overflow = "python"} : i64
%55 = core.add %50, %54 {overflow = "python"} : i64
core.store %55, %value_addr
%56 = core.load %i_addr : i64
%57 = core.add %56, %11 {overflow = "python"} : i64
core.store %57, %i_addr
core.br ^for.head9
^for.end12:
%58 = core.load %total_addr : i64 loc("examples/43_regex/patterns.ppy":49:8)
%59 = core.load %value_addr : i64
%60 = core.add %58, %59 {overflow = "python"} : i64
core.store %60, %total_addr
%61 = core.ptr_offset %m_spans, %14 : ptr<i64, stack> loc("examples/43_regex/patterns.ppy":50:8)
%62 = core.load %61 : i64
core.store %62, %pos_addr
core.br ^while.head1
}
func @patterns_count_hex(%text: buffer<u8> {ownership = "borrowed", ppy.kind = "view"}) -> i64 attrs {effects = ["alloc", "may_raise", "read_memory"], ppy.abi = "ppy", ppy.qualname = "patterns.count_hex", ppy.releases_gil = true, ppy.symbol = "ppy_patterns_count_hex"} loc("examples/43_regex/patterns.ppy":53:0) {
^entry:
%0 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":54:4)
%n_addr = core.alloca : ptr<i64, stack>
core.store %0, %n_addr
%1 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":55:4)
%pos_addr = core.alloca : ptr<i64, stack>
core.store %1, %pos_addr
%m_found_addr = core.alloca : ptr<bool, stack>
%m_spans = core.alloca {count = 2} : ptr<i64, stack>
%2 = core.const 0 : i64
%3 = core.const 1 : i64
%4 = core.const 1 : i64
%5 = core.const 1 : i64
core.br ^while.head1 loc("examples/43_regex/patterns.ppy":56:4)
^while.head1:
%6 = core.load %pos_addr : i64 loc("examples/43_regex/patterns.ppy":57:8)
%7 = core.buffer_len %text : index
%8 = core.cast %7 : i64
%9, %10, %11 = core.call %text, %6, %8 {callee = @ppy.regex.2} : bool, i64, i64
core.store %9, %m_found_addr loc("examples/43_regex/patterns.ppy":57:8)
%12 = core.ptr_offset %m_spans, %2 : ptr<i64, stack>
core.store %10, %12
%13 = core.ptr_offset %m_spans, %3 : ptr<i64, stack>
core.store %11, %13
%14 = core.load %m_found_addr : bool loc("examples/43_regex/patterns.ppy":58:8)
%15 = core.const false : bool
%16 = core.cmp.eq %14, %15 : bool
core.cond_br %16, ^then4, ^else5
^then4:
%17 = core.load %n_addr : i64 loc("examples/43_regex/patterns.ppy":59:12)
core.ret %17
^else5:
%18 = core.load %n_addr : i64 loc("examples/43_regex/patterns.ppy":60:8)
%19 = core.add %18, %4 {overflow = "python"} : i64
core.store %19, %n_addr
%20 = core.ptr_offset %m_spans, %5 : ptr<i64, stack> loc("examples/43_regex/patterns.ppy":61:8)
%21 = core.load %20 : i64
core.store %21, %pos_addr
core.br ^while.head1
}
func @patterns_is_identifier(%text: buffer<u8> {ownership = "borrowed", ppy.kind = "view"}) -> bool attrs {effects = ["alloc", "may_raise", "read_memory"], ppy.abi = "ppy", ppy.qualname = "patterns.is_identifier", ppy.releases_gil = true, ppy.symbol = "ppy_patterns_is_identifier"} loc("examples/43_regex/patterns.ppy":64:0) {
^entry:
%0 = core.const 0 : i64 loc("examples/43_regex/patterns.ppy":65:4)
%1 = core.buffer_len %text : index
%2 = core.cast %1 : i64
%3, %4, %5 = core.call %text, %0, %2 {callee = @ppy.regex.3} : bool, i64, i64
core.ret %3 loc("examples/43_regex/patterns.ppy":65:4)
}
private func @ppy.regex.0(%buf: buffer<u8>, %pos: i64, %endpos: i64) -> (bool, i64, i64) attrs {ppy.regex = "[A-Za-z]+", ppy.synthesized = "regex.search"} {
^entry:
%1 = core.buffer_len %buf : index
%2 = core.cast %1 : i64
%3 = core.const 0 : i64
%4 = core.cmp.lt %endpos, %3 : bool
%5 = core.select %4, %3, %endpos : i64
%6 = core.cmp.gt %5, %2 : bool
%7 = core.select %6, %2, %5 : i64
%8 = core.cmp.lt %pos, %3 : bool
%9 = core.select %8, %3, %pos : i64
%10 = core.cmp.gt %9, %2 : bool
%11 = core.select %10, %2, %9 : i64
%p = core.alloca : ptr<i64, stack>
%sp = core.alloca : ptr<i64, stack>
%start = core.alloca : ptr<i64, stack>
%stack = core.alloca {count = 12288} : ptr<i64, stack>
%caps = core.alloca {count = 2} : ptr<i64, stack>
%12 = core.const 0 : i64
%13 = core.cmp.gt %11, %7 : bool
core.cond_br %13, ^notfound4, ^attempt1(%11)
^attempt1(%s: i64):
core.store %s, %p
core.store %s, %start
core.store %12, %sp
%14 = core.const -1 : i64
%15 = core.const 0 : i64
%16 = core.ptr_offset %caps, %15 : ptr<i64, stack>
core.store %14, %16
%17 = core.const 1 : i64
%18 = core.ptr_offset %caps, %17 : ptr<i64, stack>
core.store %14, %18
%19 = core.const 0 : i64
%20 = core.ptr_offset %caps, %19 : ptr<i64, stack>
core.store %s, %20
%21 = core.load %p : i64
%22 = core.const 1 : i64
%23 = core.add %21, %22 {overflow = "proven"} : i64
%24 = core.cmp.le %23, %7 : bool
core.cond_br %24, ^fits7, ^fail2
^fail2:
%25 = core.load %sp : i64
%26 = core.const 0 : i64
%27 = core.cmp.eq %25, %26 : bool
core.cond_br %27, ^exhausted3, ^pop17
^exhausted3:
%28 = core.load %start : i64
%29 = core.const 1 : i64
%30 = core.add %28, %29 {overflow = "proven"} : i64
%31 = core.cmp.gt %30, %7 : bool
core.cond_br %31, ^notfound4, ^attempt1(%30)
^notfound4:
%32 = core.const false : bool
%33 = core.const -1 : i64
%34 = core.const -1 : i64
core.ret %32, %33, %34
^fits7:
core.br ^scan8(%21)
^scan8(%q: i64):
%35 = core.cmp.lt %q, %7 : bool
core.cond_br %35, ^bound9, ^scanned11(%q)
^bound9:
%36 = core.buffer_load %buf, %q : u8
%37 = core.cast %36 : i64
%38 = core.const 65 : i64
%39 = core.cmp.ge %37, %38 : bool
%40 = core.const 90 : i64
%41 = core.cmp.le %37, %40 : bool
%42 = core.and %39, %41 : bool
%43 = core.const 97 : i64
%44 = core.cmp.ge %37, %43 : bool
%45 = core.const 122 : i64
%46 = core.cmp.le %37, %45 : bool
%47 = core.and %44, %46 : bool
%48 = core.or %42, %47 : bool
%49 = core.const 1 : i64
%50 = core.add %q, %49 {overflow = "proven"} : i64
core.cond_br %48, ^scan8(%50), ^scanned11(%q)
^scanned11(%0: i64):
%51 = core.const 1 : i64
%52 = core.add %21, %51 {overflow = "proven"} : i64
%53 = core.cmp.lt %0, %52 : bool
core.cond_br %53, ^fail2, ^enough13
^exit12:
%54 = core.load %p : i64
%55 = core.const 1 : i64
%56 = core.ptr_offset %caps, %55 : ptr<i64, stack>
core.store %54, %56
%57 = core.const true : bool
%58 = core.const 0 : i64
%59 = core.ptr_offset %caps, %58 : ptr<i64, stack>
%60 = core.load %59 : i64
%61 = core.const 1 : i64
%62 = core.ptr_offset %caps, %61 : ptr<i64, stack>
%63 = core.load %62 : i64
core.ret %57, %60, %63
^enough13:
core.store %0, %p
%64 = core.cmp.gt %0, %52 : bool
core.cond_br %64, ^keep15, ^exit12
^giveback14(%held: i64):
%65 = core.load %p : i64
%66 = core.const 1 : i64
%67 = core.sub %65, %66 {overflow = "proven"} : i64
core.store %67, %p
%68 = core.cmp.gt %67, %held : bool
core.cond_br %68, ^keep16, ^exit12
^keep15:
%69 = core.load %sp : i64
%70 = core.const 4096 : i64
%71 = core.cmp.lt %69, %70 : bool
core.guard %71 {kind = "range", label = "regex.stack.ok"}
%72 = core.const 3 : i64
%73 = core.mul %69, %72 {overflow = "proven"} : i64
%74 = core.ptr_offset %stack, %73 : ptr<i64, stack>
%75 = core.const 0 : i64
%76 = core.load %p : i64
%77 = core.const 0 : i64
%78 = core.ptr_offset %74, %77 : ptr<i64, stack>
core.store %75, %78
%79 = core.const 1 : i64
%80 = core.ptr_offset %74, %79 : ptr<i64, stack>
core.store %76, %80
%81 = core.const 2 : i64
%82 = core.ptr_offset %74, %81 : ptr<i64, stack>
core.store %52, %82
%83 = core.const 1 : i64
%84 = core.add %69, %83 {overflow = "proven"} : i64
core.store %84, %sp
core.br ^exit12
^keep16:
%85 = core.load %sp : i64
%86 = core.const 4096 : i64
%87 = core.cmp.lt %85, %86 : bool
core.guard %87 {kind = "range", label = "regex.stack.ok"}
%88 = core.const 3 : i64
%89 = core.mul %85, %88 {overflow = "proven"} : i64
%90 = core.ptr_offset %stack, %89 : ptr<i64, stack>
%91 = core.const 0 : i64
%92 = core.load %p : i64
%93 = core.const 0 : i64
%94 = core.ptr_offset %90, %93 : ptr<i64, stack>
core.store %91, %94
%95 = core.const 1 : i64
%96 = core.ptr_offset %90, %95 : ptr<i64, stack>
core.store %92, %96
%97 = core.const 2 : i64
%98 = core.ptr_offset %90, %97 : ptr<i64, stack>
core.store %held, %98
%99 = core.const 1 : i64
%100 = core.add %85, %99 {overflow = "proven"} : i64
core.store %100, %sp
core.br ^exit12
^pop17:
%101 = core.const 1 : i64
%102 = core.sub %25, %101 {overflow = "proven"} : i64
core.store %102, %sp
%103 = core.const 3 : i64
%104 = core.mul %102, %103 {overflow = "proven"} : i64
%105 = core.ptr_offset %stack, %104 : ptr<i64, stack>
%106 = core.const 0 : i64
%107 = core.ptr_offset %105, %106 : ptr<i64, stack>
%108 = core.load %107 : i64
%109 = core.const 1 : i64
%110 = core.ptr_offset %105, %109 : ptr<i64, stack>
%111 = core.load %110 : i64
core.store %111, %p
%112 = core.const 2 : i64
%113 = core.ptr_offset %105, %112 : ptr<i64, stack>
%114 = core.load %113 : i64
%115 = core.const 0 : i64
%116 = core.cmp.eq %108, %115 : bool
core.cond_br %116, ^giveback14(%114), ^dispatch18
^dispatch18:
core.unreachable
}
private func @ppy.regex.1(%buf: buffer<u8>, %pos: i64, %endpos: i64) -> (bool, i64, i64, i64, i64, i64, i64) attrs {ppy.regex = "(?P<key>\\w+)\\s*=\\s*(\\d+)", ppy.synthesized = "regex.search"} {
^entry:
%14 = core.buffer_len %buf : index
%15 = core.cast %14 : i64
%16 = core.const 0 : i64
%17 = core.cmp.lt %endpos, %16 : bool
%18 = core.select %17, %16, %endpos : i64
%19 = core.cmp.gt %18, %15 : bool
%20 = core.select %19, %15, %18 : i64
%21 = core.cmp.lt %pos, %16 : bool
%22 = core.select %21, %16, %pos : i64
%23 = core.cmp.gt %22, %15 : bool
%24 = core.select %23, %15, %22 : i64
%p = core.alloca : ptr<i64, stack>
%sp = core.alloca : ptr<i64, stack>
%start = core.alloca : ptr<i64, stack>
%stack = core.alloca {count = 12288} : ptr<i64, stack>
%caps = core.alloca {count = 6} : ptr<i64, stack>
%25 = core.const 0 : i64
%26 = core.cmp.gt %24, %20 : bool
core.cond_br %26, ^notfound4, ^attempt1(%24)
^attempt1(%s: i64):
core.store %s, %p
core.store %s, %start
core.store %25, %sp
%27 = core.const -1 : i64
%28 = core.const 0 : i64
%29 = core.ptr_offset %caps, %28 : ptr<i64, stack>
core.store %27, %29
%30 = core.const 1 : i64
%31 = core.ptr_offset %caps, %30 : ptr<i64, stack>
core.store %27, %31
%32 = core.const 2 : i64
%33 = core.ptr_offset %caps, %32 : ptr<i64, stack>
core.store %27, %33
%34 = core.const 3 : i64
%35 = core.ptr_offset %caps, %34 : ptr<i64, stack>
core.store %27, %35
%36 = core.const 4 : i64
%37 = core.ptr_offset %caps, %36 : ptr<i64, stack>
core.store %27, %37
%38 = core.const 5 : i64
%39 = core.ptr_offset %caps, %38 : ptr<i64, stack>
core.store %27, %39
%40 = core.const 0 : i64
%41 = core.ptr_offset %caps, %40 : ptr<i64, stack>
core.store %s, %41
%42 = core.const 2 : i64
%43 = core.ptr_offset %caps, %42 : ptr<i64, stack>
%44 = core.load %43 : i64
%45 = core.load %sp : i64
%46 = core.const 4096 : i64
%47 = core.cmp.lt %45, %46 : bool
core.guard %47 {kind = "range", label = "regex.stack.ok"}
%48 = core.const 3 : i64
%49 = core.mul %45, %48 {overflow = "proven"} : i64
%50 = core.ptr_offset %stack, %49 : ptr<i64, stack>
%51 = core.const 0 : i64
%52 = core.load %p : i64
%53 = core.const 0 : i64
%54 = core.ptr_offset %50, %53 : ptr<i64, stack>
core.store %51, %54
%55 = core.const 1 : i64
%56 = core.ptr_offset %50, %55 : ptr<i64, stack>
core.store %52, %56
%57 = core.const 2 : i64
%58 = core.ptr_offset %50, %57 : ptr<i64, stack>
core.store %44, %58
%59 = core.const 1 : i64
%60 = core.add %45, %59 {overflow = "proven"} : i64
core.store %60, %sp
%61 = core.load %p : i64
%62 = core.const 2 : i64
%63 = core.ptr_offset %caps, %62 : ptr<i64, stack>
core.store %61, %63
%64 = core.load %p : i64
%65 = core.const 1 : i64
%66 = core.add %64, %65 {overflow = "proven"} : i64
%67 = core.cmp.le %66, %20 : bool
core.cond_br %67, ^fits8, ^fail2
^fail2:
%68 = core.load %sp : i64
%69 = core.const 0 : i64
%70 = core.cmp.eq %68, %69 : bool
core.cond_br %70, ^exhausted3, ^pop53
^exhausted3:
%71 = core.load %start : i64
%72 = core.const 1 : i64
%73 = core.add %71, %72 {overflow = "proven"} : i64
%74 = core.cmp.gt %73, %20 : bool
core.cond_br %74, ^notfound4, ^attempt1(%73)
^notfound4:
%75 = core.const false : bool
%76 = core.const -1 : i64
%77 = core.const -1 : i64
%78 = core.const -1 : i64
%79 = core.const -1 : i64
%80 = core.const -1 : i64
%81 = core.const -1 : i64
core.ret %75, %76, %77, %78, %79, %80, %81
^restore7(%held: i64):
%82 = core.const 2 : i64
%83 = core.ptr_offset %caps, %82 : ptr<i64, stack>
core.store %held, %83
core.br ^fail2
^fits8:
core.br ^scan9(%64)
^scan9(%q: i64):
%84 = core.cmp.lt %q, %20 : bool
core.cond_br %84, ^bound10, ^scanned12(%q)
^bound10:
%85 = core.buffer_load %buf, %q : u8
%86 = core.cast %85 : i64
%87 = core.const 0 : i64
%88 = core.const 192 : i64
%89 = core.cmp.lt %86, %88 : bool
%90 = core.const 0 : i64
%91 = core.select %89, %90, %87 : i64
%92 = core.const 128 : i64
%93 = core.cmp.lt %86, %92 : bool
%94 = core.const 576460745995190270 : i64
%95 = core.select %93, %94, %91 : i64
%96 = core.const 64 : i64
%97 = core.cmp.lt %86, %96 : bool
%98 = core.const 287948901175001088 : i64
%99 = core.select %97, %98, %95 : i64
%100 = core.const 63 : i64
%101 = core.and %86, %100 : i64
%102 = core.shr %99, %101 : i64
%103 = core.const 1 : i64
%104 = core.and %102, %103 : i64
%105 = core.const 0 : i64
%106 = core.cmp.ne %104, %105 : bool
%107 = core.const 1 : i64
%108 = core.add %q, %107 {overflow = "proven"} : i64
core.cond_br %106, ^scan9(%108), ^scanned12(%q)
^scanned12(%0: i64):
%109 = core.const 1 : i64
%110 = core.add %64, %109 {overflow = "proven"} : i64
%111 = core.cmp.lt %0, %110 : bool
core.cond_br %111, ^fail2, ^enough14
^exit13:
%112 = core.const 3 : i64
%113 = core.ptr_offset %caps, %112 : ptr<i64, stack>
%114 = core.load %113 : i64
%115 = core.load %sp : i64
%116 = core.const 4096 : i64
%117 = core.cmp.lt %115, %116 : bool
core.guard %117 {kind = "range", label = "regex.stack.ok"}
%118 = core.const 3 : i64
%119 = core.mul %115, %118 {overflow = "proven"} : i64
%120 = core.ptr_offset %stack, %119 : ptr<i64, stack>
%121 = core.const 2 : i64
%122 = core.load %p : i64
%123 = core.const 0 : i64
%124 = core.ptr_offset %120, %123 : ptr<i64, stack>
core.store %121, %124
%125 = core.const 1 : i64
%126 = core.ptr_offset %120, %125 : ptr<i64, stack>
core.store %122, %126
%127 = core.const 2 : i64
%128 = core.ptr_offset %120, %127 : ptr<i64, stack>
core.store %114, %128
%129 = core.const 1 : i64
%130 = core.add %115, %129 {overflow = "proven"} : i64
core.store %130, %sp
%131 = core.load %p : i64
%132 = core.const 3 : i64
%133 = core.ptr_offset %caps, %132 : ptr<i64, stack>
core.store %131, %133
%134 = core.load %p : i64
%135 = core.cmp.le %134, %20 : bool
core.cond_br %135, ^fits19, ^fail2
^enough14:
core.store %0, %p
%136 = core.cmp.gt %0, %110 : bool
core.cond_br %136, ^keep16, ^exit13
^giveback15(%1: i64):
%137 = core.load %p : i64
%138 = core.const 1 : i64
%139 = core.sub %137, %138 {overflow = "proven"} : i64
core.store %139, %p
%140 = core.cmp.gt %139, %1 : bool
core.cond_br %140, ^keep17, ^exit13
^keep16:
%141 = core.load %sp : i64
%142 = core.const 4096 : i64
%143 = core.cmp.lt %141, %142 : bool
core.guard %143 {kind = "range", label = "regex.stack.ok"}
%144 = core.const 3 : i64
%145 = core.mul %141, %144 {overflow = "proven"} : i64
%146 = core.ptr_offset %stack, %145 : ptr<i64, stack>
%147 = core.const 1 : i64
%148 = core.load %p : i64
%149 = core.const 0 : i64
%150 = core.ptr_offset %146, %149 : ptr<i64, stack>
core.store %147, %150
%151 = core.const 1 : i64
%152 = core.ptr_offset %146, %151 : ptr<i64, stack>
core.store %148, %152
%153 = core.const 2 : i64
%154 = core.ptr_offset %146, %153 : ptr<i64, stack>
core.store %110, %154
%155 = core.const 1 : i64
%156 = core.add %141, %155 {overflow = "proven"} : i64
core.store %156, %sp
core.br ^exit13
^keep17:
%157 = core.load %sp : i64
%158 = core.const 4096 : i64
%159 = core.cmp.lt %157, %158 : bool
core.guard %159 {kind = "range", label = "regex.stack.ok"}
%160 = core.const 3 : i64
%161 = core.mul %157, %160 {overflow = "proven"} : i64
%162 = core.ptr_offset %stack, %161 : ptr<i64, stack>
%163 = core.const 1 : i64
%164 = core.load %p : i64
%165 = core.const 0 : i64
%166 = core.ptr_offset %162, %165 : ptr<i64, stack>
core.store %163, %166
%167 = core.const 1 : i64
%168 = core.ptr_offset %162, %167 : ptr<i64, stack>
core.store %164, %168
%169 = core.const 2 : i64
%170 = core.ptr_offset %162, %169 : ptr<i64, stack>
core.store %1, %170
%171 = core.const 1 : i64
%172 = core.add %157, %171 {overflow = "proven"} : i64
core.store %172, %sp
core.br ^exit13
^restore18(%2: i64):
%173 = core.const 3 : i64
%174 = core.ptr_offset %caps, %173 : ptr<i64, stack>
core.store %2, %174
core.br ^fail2
^fits19:
core.br ^scan20(%134)
^scan20(%3: i64):
%175 = core.cmp.lt %3, %20 : bool
core.cond_br %175, ^bound21, ^scanned23(%3)
^bound21:
%176 = core.buffer_load %buf, %3 : u8
%177 = core.cast %176 : i64
%178 = core.const 9 : i64
%179 = core.cmp.ge %177, %178 : bool
%180 = core.const 13 : i64
%181 = core.cmp.le %177, %180 : bool
%182 = core.and %179, %181 : bool
%183 = core.const 32 : i64
%184 = core.cmp.eq %177, %183 : bool
%185 = core.or %182, %184 : bool
%186 = core.const 1 : i64
%187 = core.add %3, %186 {overflow = "proven"} : i64
core.cond_br %185, ^scan20(%187), ^scanned23(%3)
^scanned23(%4: i64):
%188 = core.cmp.lt %4, %134 : bool
core.cond_br %188, ^fail2, ^enough25
^exit24:
%189 = core.load %p : i64
%190 = core.cmp.lt %189, %20 : bool
core.cond_br %190, ^read29, ^fail2
^enough25:
core.store %4, %p
%191 = core.cmp.gt %4, %134 : bool
core.cond_br %191, ^keep27, ^exit24
^giveback26(%5: i64):
%192 = core.load %p : i64
%193 = core.const 1 : i64
%194 = core.sub %192, %193 {overflow = "proven"} : i64
core.store %194, %p
%195 = core.cmp.gt %194, %5 : bool
core.cond_br %195, ^keep28, ^exit24
^keep27:
%196 = core.load %sp : i64
%197 = core.const 4096 : i64
%198 = core.cmp.lt %196, %197 : bool
core.guard %198 {kind = "range", label = "regex.stack.ok"}
%199 = core.const 3 : i64
%200 = core.mul %196, %199 {overflow = "proven"} : i64
%201 = core.ptr_offset %stack, %200 : ptr<i64, stack>
%202 = core.const 3 : i64
%203 = core.load %p : i64
%204 = core.const 0 : i64
%205 = core.ptr_offset %201, %204 : ptr<i64, stack>
core.store %202, %205
%206 = core.const 1 : i64
%207 = core.ptr_offset %201, %206 : ptr<i64, stack>
core.store %203, %207
%208 = core.const 2 : i64
%209 = core.ptr_offset %201, %208 : ptr<i64, stack>
core.store %134, %209
%210 = core.const 1 : i64
%211 = core.add %196, %210 {overflow = "proven"} : i64
core.store %211, %sp
core.br ^exit24
^keep28:
%212 = core.load %sp : i64
%213 = core.const 4096 : i64
%214 = core.cmp.lt %212, %213 : bool
core.guard %214 {kind = "range", label = "regex.stack.ok"}
%215 = core.const 3 : i64
%216 = core.mul %212, %215 {overflow = "proven"} : i64
%217 = core.ptr_offset %stack, %216 : ptr<i64, stack>
%218 = core.const 3 : i64
%219 = core.load %p : i64
%220 = core.const 0 : i64
%221 = core.ptr_offset %217, %220 : ptr<i64, stack>
core.store %218, %221
%222 = core.const 1 : i64
%223 = core.ptr_offset %217, %222 : ptr<i64, stack>
core.store %219, %223
%224 = core.const 2 : i64
%225 = core.ptr_offset %217, %224 : ptr<i64, stack>
core.store %5, %225
%226 = core.const 1 : i64
%227 = core.add %212, %226 {overflow = "proven"} : i64
core.store %227, %sp
core.br ^exit24
^read29:
%228 = core.buffer_load %buf, %189 : u8
%229 = core.cast %228 : i64
%230 = core.const 61 : i64
%231 = core.cmp.eq %229, %230 : bool
core.cond_br %231, ^advance30, ^fail2
^advance30:
%232 = core.const 1 : i64
%233 = core.add %189, %232 {overflow = "proven"} : i64
core.store %233, %p
%234 = core.load %p : i64
%235 = core.cmp.le %234, %20 : bool
core.cond_br %235, ^fits31, ^fail2
^fits31:
core.br ^scan32(%234)
^scan32(%6: i64):
%236 = core.cmp.lt %6, %20 : bool
core.cond_br %236, ^bound33, ^scanned35(%6)
^bound33:
%237 = core.buffer_load %buf, %6 : u8
%238 = core.cast %237 : i64
%239 = core.const 9 : i64
%240 = core.cmp.ge %238, %239 : bool
%241 = core.const 13 : i64
%242 = core.cmp.le %238, %241 : bool
%243 = core.and %240, %242 : bool
%244 = core.const 32 : i64
%245 = core.cmp.eq %238, %244 : bool
%246 = core.or %243, %245 : bool
%247 = core.const 1 : i64
%248 = core.add %6, %247 {overflow = "proven"} : i64
core.cond_br %246, ^scan32(%248), ^scanned35(%6)
^scanned35(%7: i64):
%249 = core.cmp.lt %7, %234 : bool
core.cond_br %249, ^fail2, ^enough37
^exit36:
%250 = core.const 4 : i64
%251 = core.ptr_offset %caps, %250 : ptr<i64, stack>
%252 = core.load %251 : i64
%253 = core.load %sp : i64
%254 = core.const 4096 : i64
%255 = core.cmp.lt %253, %254 : bool
core.guard %255 {kind = "range", label = "regex.stack.ok"}
%256 = core.const 3 : i64
%257 = core.mul %253, %256 {overflow = "proven"} : i64
%258 = core.ptr_offset %stack, %257 : ptr<i64, stack>
%259 = core.const 5 : i64
%260 = core.load %p : i64
%261 = core.const 0 : i64
%262 = core.ptr_offset %258, %261 : ptr<i64, stack>
core.store %259, %262
%263 = core.const 1 : i64
%264 = core.ptr_offset %258, %263 : ptr<i64, stack>
core.store %260, %264
%265 = core.const 2 : i64
%266 = core.ptr_offset %258, %265 : ptr<i64, stack>
core.store %252, %266
%267 = core.const 1 : i64
%268 = core.add %253, %267 {overflow = "proven"} : i64
core.store %268, %sp
%269 = core.load %p : i64
%270 = core.const 4 : i64
%271 = core.ptr_offset %caps, %270 : ptr<i64, stack>
core.store %269, %271
%272 = core.load %p : i64
%273 = core.const 1 : i64
%274 = core.add %272, %273 {overflow = "proven"} : i64
%275 = core.cmp.le %274, %20 : bool
core.cond_br %275, ^fits42, ^fail2
^enough37:
core.store %7, %p
%276 = core.cmp.gt %7, %234 : bool
core.cond_br %276, ^keep39, ^exit36
^giveback38(%8: i64):
%277 = core.load %p : i64
%278 = core.const 1 : i64
%279 = core.sub %277, %278 {overflow = "proven"} : i64
core.store %279, %p
%280 = core.cmp.gt %279, %8 : bool
core.cond_br %280, ^keep40, ^exit36
^keep39:
%281 = core.load %sp : i64
%282 = core.const 4096 : i64
%283 = core.cmp.lt %281, %282 : bool
core.guard %283 {kind = "range", label = "regex.stack.ok"}
%284 = core.const 3 : i64
%285 = core.mul %281, %284 {overflow = "proven"} : i64
%286 = core.ptr_offset %stack, %285 : ptr<i64, stack>
%287 = core.const 4 : i64
%288 = core.load %p : i64
%289 = core.const 0 : i64
%290 = core.ptr_offset %286, %289 : ptr<i64, stack>
core.store %287, %290
%291 = core.const 1 : i64
%292 = core.ptr_offset %286, %291 : ptr<i64, stack>
core.store %288, %292
%293 = core.const 2 : i64
%294 = core.ptr_offset %286, %293 : ptr<i64, stack>
core.store %234, %294
%295 = core.const 1 : i64
%296 = core.add %281, %295 {overflow = "proven"} : i64
core.store %296, %sp
core.br ^exit36
^keep40:
%297 = core.load %sp : i64
%298 = core.const 4096 : i64
%299 = core.cmp.lt %297, %298 : bool
core.guard %299 {kind = "range", label = "regex.stack.ok"}
%300 = core.const 3 : i64
%301 = core.mul %297, %300 {overflow = "proven"} : i64
%302 = core.ptr_offset %stack, %301 : ptr<i64, stack>
%303 = core.const 4 : i64
%304 = core.load %p : i64
%305 = core.const 0 : i64
%306 = core.ptr_offset %302, %305 : ptr<i64, stack>
core.store %303, %306
%307 = core.const 1 : i64
%308 = core.ptr_offset %302, %307 : ptr<i64, stack>
core.store %304, %308
%309 = core.const 2 : i64
%310 = core.ptr_offset %302, %309 : ptr<i64, stack>
core.store %8, %310
%311 = core.const 1 : i64
%312 = core.add %297, %311 {overflow = "proven"} : i64
core.store %312, %sp
core.br ^exit36
^restore41(%9: i64):
%313 = core.const 4 : i64
%314 = core.ptr_offset %caps, %313 : ptr<i64, stack>
core.store %9, %314
core.br ^fail2
^fits42:
core.br ^scan43(%272)
^scan43(%10: i64):
%315 = core.cmp.lt %10, %20 : bool
core.cond_br %315, ^bound44, ^scanned46(%10)
^bound44:
%316 = core.buffer_load %buf, %10 : u8
%317 = core.cast %316 : i64
%318 = core.const 48 : i64
%319 = core.cmp.ge %317, %318 : bool
%320 = core.const 57 : i64
%321 = core.cmp.le %317, %320 : bool
%322 = core.and %319, %321 : bool
%323 = core.const 1 : i64
%324 = core.add %10, %323 {overflow = "proven"} : i64
core.cond_br %322, ^scan43(%324), ^scanned46(%10)
^scanned46(%11: i64):
%325 = core.const 1 : i64
%326 = core.add %272, %325 {overflow = "proven"} : i64
%327 = core.cmp.lt %11, %326 : bool
core.cond_br %327, ^fail2, ^enough48
^exit47:
%328 = core.const 5 : i64
%329 = core.ptr_offset %caps, %328 : ptr<i64, stack>
%330 = core.load %329 : i64
%331 = core.load %sp : i64
%332 = core.const 4096 : i64
%333 = core.cmp.lt %331, %332 : bool
core.guard %333 {kind = "range", label = "regex.stack.ok"}
%334 = core.const 3 : i64
%335 = core.mul %331, %334 {overflow = "proven"} : i64
%336 = core.ptr_offset %stack, %335 : ptr<i64, stack>
%337 = core.const 7 : i64
%338 = core.load %p : i64
%339 = core.const 0 : i64
%340 = core.ptr_offset %336, %339 : ptr<i64, stack>
core.store %337, %340
%341 = core.const 1 : i64
%342 = core.ptr_offset %336, %341 : ptr<i64, stack>
core.store %338, %342
%343 = core.const 2 : i64
%344 = core.ptr_offset %336, %343 : ptr<i64, stack>
core.store %330, %344
%345 = core.const 1 : i64
%346 = core.add %331, %345 {overflow = "proven"} : i64
core.store %346, %sp
%347 = core.load %p : i64
%348 = core.const 5 : i64
%349 = core.ptr_offset %caps, %348 : ptr<i64, stack>
core.store %347, %349
%350 = core.load %p : i64
%351 = core.const 1 : i64
%352 = core.ptr_offset %caps, %351 : ptr<i64, stack>
core.store %350, %352
%353 = core.const true : bool
%354 = core.const 0 : i64
%355 = core.ptr_offset %caps, %354 : ptr<i64, stack>
%356 = core.load %355 : i64
%357 = core.const 1 : i64
%358 = core.ptr_offset %caps, %357 : ptr<i64, stack>
%359 = core.load %358 : i64
%360 = core.const 2 : i64
%361 = core.ptr_offset %caps, %360 : ptr<i64, stack>
%362 = core.load %361 : i64
%363 = core.const 3 : i64
%364 = core.ptr_offset %caps, %363 : ptr<i64, stack>
%365 = core.load %364 : i64
%366 = core.const 4 : i64
%367 = core.ptr_offset %caps, %366 : ptr<i64, stack>
%368 = core.load %367 : i64
%369 = core.const 5 : i64
%370 = core.ptr_offset %caps, %369 : ptr<i64, stack>
%371 = core.load %370 : i64
core.ret %353, %356, %359, %362, %365, %368, %371
^enough48:
core.store %11, %p
%372 = core.cmp.gt %11, %326 : bool
core.cond_br %372, ^keep50, ^exit47
^giveback49(%12: i64):
%373 = core.load %p : i64
%374 = core.const 1 : i64
%375 = core.sub %373, %374 {overflow = "proven"} : i64
core.store %375, %p
%376 = core.cmp.gt %375, %12 : bool
core.cond_br %376, ^keep51, ^exit47
^keep50:
%377 = core.load %sp : i64
%378 = core.const 4096 : i64
%379 = core.cmp.lt %377, %378 : bool
core.guard %379 {kind = "range", label = "regex.stack.ok"}
%380 = core.const 3 : i64
%381 = core.mul %377, %380 {overflow = "proven"} : i64
%382 = core.ptr_offset %stack, %381 : ptr<i64, stack>
%383 = core.const 6 : i64
%384 = core.load %p : i64
%385 = core.const 0 : i64
%386 = core.ptr_offset %382, %385 : ptr<i64, stack>
core.store %383, %386
%387 = core.const 1 : i64
%388 = core.ptr_offset %382, %387 : ptr<i64, stack>
core.store %384, %388
%389 = core.const 2 : i64
%390 = core.ptr_offset %382, %389 : ptr<i64, stack>
core.store %326, %390
%391 = core.const 1 : i64
%392 = core.add %377, %391 {overflow = "proven"} : i64
core.store %392, %sp
core.br ^exit47
^keep51:
%393 = core.load %sp : i64
%394 = core.const 4096 : i64
%395 = core.cmp.lt %393, %394 : bool
core.guard %395 {kind = "range", label = "regex.stack.ok"}
%396 = core.const 3 : i64
%397 = core.mul %393, %396 {overflow = "proven"} : i64
%398 = core.ptr_offset %stack, %397 : ptr<i64, stack>
%399 = core.const 6 : i64
%400 = core.load %p : i64
%401 = core.const 0 : i64
%402 = core.ptr_offset %398, %401 : ptr<i64, stack>
core.store %399, %402
%403 = core.const 1 : i64
%404 = core.ptr_offset %398, %403 : ptr<i64, stack>
core.store %400, %404
%405 = core.const 2 : i64
%406 = core.ptr_offset %398, %405 : ptr<i64, stack>
core.store %12, %406
%407 = core.const 1 : i64
%408 = core.add %393, %407 {overflow = "proven"} : i64
core.store %408, %sp
core.br ^exit47
^restore52(%13: i64):
%409 = core.const 5 : i64
%410 = core.ptr_offset %caps, %409 : ptr<i64, stack>
core.store %13, %410
core.br ^fail2
^pop53:
%411 = core.const 1 : i64
%412 = core.sub %68, %411 {overflow = "proven"} : i64
core.store %412, %sp
%413 = core.const 3 : i64
%414 = core.mul %412, %413 {overflow = "proven"} : i64
%415 = core.ptr_offset %stack, %414 : ptr<i64, stack>
%416 = core.const 0 : i64
%417 = core.ptr_offset %415, %416 : ptr<i64, stack>
%418 = core.load %417 : i64
%419 = core.const 1 : i64
%420 = core.ptr_offset %415, %419 : ptr<i64, stack>
%421 = core.load %420 : i64
core.store %421, %p
%422 = core.const 2 : i64
%423 = core.ptr_offset %415, %422 : ptr<i64, stack>
%424 = core.load %423 : i64
%425 = core.const 0 : i64
%426 = core.cmp.eq %418, %425 : bool
core.cond_br %426, ^restore7(%424), ^dispatch54
^dispatch54:
%427 = core.const 1 : i64
%428 = core.cmp.eq %418, %427 : bool
core.cond_br %428, ^giveback15(%424), ^dispatch55
^dispatch55:
%429 = core.const 2 : i64
%430 = core.cmp.eq %418, %429 : bool
core.cond_br %430, ^restore18(%424), ^dispatch56
^dispatch56:
%431 = core.const 3 : i64
%432 = core.cmp.eq %418, %431 : bool
core.cond_br %432, ^giveback26(%424), ^dispatch57
^dispatch57:
%433 = core.const 4 : i64
%434 = core.cmp.eq %418, %433 : bool
core.cond_br %434, ^giveback38(%424), ^dispatch58
^dispatch58:
%435 = core.const 5 : i64
%436 = core.cmp.eq %418, %435 : bool
core.cond_br %436, ^restore41(%424), ^dispatch59
^dispatch59:
%437 = core.const 6 : i64
%438 = core.cmp.eq %418, %437 : bool
core.cond_br %438, ^giveback49(%424), ^dispatch60
^dispatch60:
%439 = core.const 7 : i64
%440 = core.cmp.eq %418, %439 : bool
core.cond_br %440, ^restore52(%424), ^dispatch61
^dispatch61:
core.unreachable
}
private func @ppy.regex.2(%buf: buffer<u8>, %pos: i64, %endpos: i64) -> (bool, i64, i64) attrs {ppy.regex = "0x[0-9a-f]+", ppy.synthesized = "regex.search"} {
^entry:
%1 = core.buffer_len %buf : index
%2 = core.cast %1 : i64
%3 = core.const 0 : i64
%4 = core.cmp.lt %endpos, %3 : bool
%5 = core.select %4, %3, %endpos : i64
%6 = core.cmp.gt %5, %2 : bool
%7 = core.select %6, %2, %5 : i64
%8 = core.cmp.lt %pos, %3 : bool
%9 = core.select %8, %3, %pos : i64
%10 = core.cmp.gt %9, %2 : bool
%11 = core.select %10, %2, %9 : i64
%p = core.alloca : ptr<i64, stack>
%sp = core.alloca : ptr<i64, stack>
%start = core.alloca : ptr<i64, stack>
%stack = core.alloca {count = 12288} : ptr<i64, stack>
%caps = core.alloca {count = 2} : ptr<i64, stack>
%12 = core.const 0 : i64
%13 = core.cmp.gt %11, %7 : bool
core.cond_br %13, ^notfound4, ^attempt1(%11)
^attempt1(%s: i64):
core.store %s, %p
core.store %s, %start
core.store %12, %sp
%14 = core.const -1 : i64
%15 = core.const 0 : i64
%16 = core.ptr_offset %caps, %15 : ptr<i64, stack>
core.store %14, %16
%17 = core.const 1 : i64
%18 = core.ptr_offset %caps, %17 : ptr<i64, stack>
core.store %14, %18
%19 = core.const 0 : i64
%20 = core.ptr_offset %caps, %19 : ptr<i64, stack>
core.store %s, %20
%21 = core.load %p : i64
%22 = core.cmp.lt %21, %7 : bool
core.cond_br %22, ^read7, ^fail2
^fail2:
%23 = core.load %sp : i64
%24 = core.const 0 : i64
%25 = core.cmp.eq %23, %24 : bool
core.cond_br %25, ^exhausted3, ^pop21
^exhausted3:
%26 = core.load %start : i64
%27 = core.const 1 : i64
%28 = core.add %26, %27 {overflow = "proven"} : i64
%29 = core.cmp.gt %28, %7 : bool
core.cond_br %29, ^notfound4, ^attempt1(%28)
^notfound4:
%30 = core.const false : bool
%31 = core.const -1 : i64
%32 = core.const -1 : i64
core.ret %30, %31, %32
^read7:
%33 = core.buffer_load %buf, %21 : u8
%34 = core.cast %33 : i64
%35 = core.const 48 : i64
%36 = core.cmp.eq %34, %35 : bool
core.cond_br %36, ^advance8, ^fail2
^advance8:
%37 = core.const 1 : i64
%38 = core.add %21, %37 {overflow = "proven"} : i64
core.store %38, %p
%39 = core.load %p : i64
%40 = core.cmp.lt %39, %7 : bool
core.cond_br %40, ^read9, ^fail2
^read9:
%41 = core.buffer_load %buf, %39 : u8
%42 = core.cast %41 : i64
%43 = core.const 88 : i64
%44 = core.cmp.eq %42, %43 : bool
%45 = core.const 120 : i64
%46 = core.cmp.eq %42, %45 : bool
%47 = core.or %44, %46 : bool
core.cond_br %47, ^advance10, ^fail2
^advance10:
%48 = core.const 1 : i64
%49 = core.add %39, %48 {overflow = "proven"} : i64
core.store %49, %p
%50 = core.load %p : i64
%51 = core.const 1 : i64
%52 = core.add %50, %51 {overflow = "proven"} : i64
%53 = core.cmp.le %52, %7 : bool
core.cond_br %53, ^fits11, ^fail2
^fits11:
core.br ^scan12(%50)
^scan12(%q: i64):
%54 = core.cmp.lt %q, %7 : bool
core.cond_br %54, ^bound13, ^scanned15(%q)
^bound13:
%55 = core.buffer_load %buf, %q : u8
%56 = core.cast %55 : i64
%57 = core.const 48 : i64
%58 = core.cmp.ge %56, %57 : bool
%59 = core.const 57 : i64
%60 = core.cmp.le %56, %59 : bool
%61 = core.and %58, %60 : bool
%62 = core.const 65 : i64
%63 = core.cmp.ge %56, %62 : bool
%64 = core.const 70 : i64
%65 = core.cmp.le %56, %64 : bool
%66 = core.and %63, %65 : bool
%67 = core.const 97 : i64
%68 = core.cmp.ge %56, %67 : bool
%69 = core.const 102 : i64
%70 = core.cmp.le %56, %69 : bool
%71 = core.and %68, %70 : bool
%72 = core.or %61, %66 : bool
%73 = core.or %72, %71 : bool
%74 = core.const 1 : i64
%75 = core.add %q, %74 {overflow = "proven"} : i64
core.cond_br %73, ^scan12(%75), ^scanned15(%q)
^scanned15(%0: i64):
%76 = core.const 1 : i64
%77 = core.add %50, %76 {overflow = "proven"} : i64
%78 = core.cmp.lt %0, %77 : bool
core.cond_br %78, ^fail2, ^enough17
^exit16:
%79 = core.load %p : i64
%80 = core.const 1 : i64
%81 = core.ptr_offset %caps, %80 : ptr<i64, stack>
core.store %79, %81
%82 = core.const true : bool
%83 = core.const 0 : i64
%84 = core.ptr_offset %caps, %83 : ptr<i64, stack>
%85 = core.load %84 : i64
%86 = core.const 1 : i64
%87 = core.ptr_offset %caps, %86 : ptr<i64, stack>
%88 = core.load %87 : i64
core.ret %82, %85, %88
^enough17:
core.store %0, %p
%89 = core.cmp.gt %0, %77 : bool
core.cond_br %89, ^keep19, ^exit16
^giveback18(%held: i64):
%90 = core.load %p : i64
%91 = core.const 1 : i64
%92 = core.sub %90, %91 {overflow = "proven"} : i64
core.store %92, %p
%93 = core.cmp.gt %92, %held : bool
core.cond_br %93, ^keep20, ^exit16
^keep19:
%94 = core.load %sp : i64
%95 = core.const 4096 : i64
%96 = core.cmp.lt %94, %95 : bool
core.guard %96 {kind = "range", label = "regex.stack.ok"}
%97 = core.const 3 : i64
%98 = core.mul %94, %97 {overflow = "proven"} : i64
%99 = core.ptr_offset %stack, %98 : ptr<i64, stack>
%100 = core.const 0 : i64
%101 = core.load %p : i64
%102 = core.const 0 : i64
%103 = core.ptr_offset %99, %102 : ptr<i64, stack>
core.store %100, %103
%104 = core.const 1 : i64
%105 = core.ptr_offset %99, %104 : ptr<i64, stack>
core.store %101, %105
%106 = core.const 2 : i64
%107 = core.ptr_offset %99, %106 : ptr<i64, stack>
core.store %77, %107
%108 = core.const 1 : i64
%109 = core.add %94, %108 {overflow = "proven"} : i64
core.store %109, %sp
core.br ^exit16
^keep20:
%110 = core.load %sp : i64
%111 = core.const 4096 : i64
%112 = core.cmp.lt %110, %111 : bool
core.guard %112 {kind = "range", label = "regex.stack.ok"}
%113 = core.const 3 : i64
%114 = core.mul %110, %113 {overflow = "proven"} : i64
%115 = core.ptr_offset %stack, %114 : ptr<i64, stack>
%116 = core.const 0 : i64
%117 = core.load %p : i64
%118 = core.const 0 : i64
%119 = core.ptr_offset %115, %118 : ptr<i64, stack>
core.store %116, %119
%120 = core.const 1 : i64
%121 = core.ptr_offset %115, %120 : ptr<i64, stack>
core.store %117, %121
%122 = core.const 2 : i64
%123 = core.ptr_offset %115, %122 : ptr<i64, stack>
core.store %held, %123
%124 = core.const 1 : i64
%125 = core.add %110, %124 {overflow = "proven"} : i64
core.store %125, %sp
core.br ^exit16
^pop21:
%126 = core.const 1 : i64
%127 = core.sub %23, %126 {overflow = "proven"} : i64
core.store %127, %sp
%128 = core.const 3 : i64
%129 = core.mul %127, %128 {overflow = "proven"} : i64
%130 = core.ptr_offset %stack, %129 : ptr<i64, stack>
%131 = core.const 0 : i64
%132 = core.ptr_offset %130, %131 : ptr<i64, stack>
%133 = core.load %132 : i64
%134 = core.const 1 : i64
%135 = core.ptr_offset %130, %134 : ptr<i64, stack>
%136 = core.load %135 : i64
core.store %136, %p
%137 = core.const 2 : i64
%138 = core.ptr_offset %130, %137 : ptr<i64, stack>
%139 = core.load %138 : i64
%140 = core.const 0 : i64
%141 = core.cmp.eq %133, %140 : bool
core.cond_br %141, ^giveback18(%139), ^dispatch22
^dispatch22:
core.unreachable
}
private func @ppy.regex.3(%buf: buffer<u8>, %pos: i64, %endpos: i64) -> (bool, i64, i64) attrs {ppy.regex = "[A-Za-z_]\\w*", ppy.synthesized = "regex.fullmatch"} {
^entry:
%1 = core.buffer_len %buf : index
%2 = core.cast %1 : i64
%3 = core.const 0 : i64
%4 = core.cmp.lt %endpos, %3 : bool
%5 = core.select %4, %3, %endpos : i64
%6 = core.cmp.gt %5, %2 : bool
%7 = core.select %6, %2, %5 : i64
%8 = core.cmp.lt %pos, %3 : bool
%9 = core.select %8, %3, %pos : i64
%10 = core.cmp.gt %9, %2 : bool
%11 = core.select %10, %2, %9 : i64
%p = core.alloca : ptr<i64, stack>
%sp = core.alloca : ptr<i64, stack>
%stack = core.alloca {count = 12288} : ptr<i64, stack>
%caps = core.alloca {count = 2} : ptr<i64, stack>
%12 = core.const 0 : i64
core.store %11, %p
core.store %12, %sp
%13 = core.const -1 : i64
%14 = core.const 0 : i64
%15 = core.ptr_offset %caps, %14 : ptr<i64, stack>
core.store %13, %15
%16 = core.const 1 : i64
%17 = core.ptr_offset %caps, %16 : ptr<i64, stack>
core.store %13, %17
%18 = core.const 0 : i64
%19 = core.ptr_offset %caps, %18 : ptr<i64, stack>
core.store %11, %19
%20 = core.load %p : i64
%21 = core.cmp.lt %20, %7 : bool
core.cond_br %21, ^read7, ^fail2
^fail2:
%22 = core.load %sp : i64
%23 = core.const 0 : i64
%24 = core.cmp.eq %22, %23 : bool
core.cond_br %24, ^exhausted3, ^pop19
^exhausted3:
%25 = core.const false : bool
%26 = core.const -1 : i64
%27 = core.const -1 : i64
core.ret %25, %26, %27
^success5:
%28 = core.load %p : i64
%29 = core.const 1 : i64
%30 = core.ptr_offset %caps, %29 : ptr<i64, stack>
core.store %28, %30
%31 = core.const true : bool
%32 = core.const 0 : i64
%33 = core.ptr_offset %caps, %32 : ptr<i64, stack>
%34 = core.load %33 : i64
%35 = core.const 1 : i64
%36 = core.ptr_offset %caps, %35 : ptr<i64, stack>
%37 = core.load %36 : i64
core.ret %31, %34, %37
^read7:
%38 = core.buffer_load %buf, %20 : u8
%39 = core.cast %38 : i64
%40 = core.const 65 : i64
%41 = core.cmp.ge %39, %40 : bool
%42 = core.const 90 : i64
%43 = core.cmp.le %39, %42 : bool
%44 = core.and %41, %43 : bool
%45 = core.const 95 : i64
%46 = core.cmp.eq %39, %45 : bool
%47 = core.const 97 : i64
%48 = core.cmp.ge %39, %47 : bool
%49 = core.const 122 : i64
%50 = core.cmp.le %39, %49 : bool
%51 = core.and %48, %50 : bool
%52 = core.or %44, %46 : bool
%53 = core.or %52, %51 : bool
core.cond_br %53, ^advance8, ^fail2
^advance8:
%54 = core.const 1 : i64
%55 = core.add %20, %54 {overflow = "proven"} : i64
core.store %55, %p
%56 = core.load %p : i64
%57 = core.cmp.le %56, %7 : bool
core.cond_br %57, ^fits9, ^fail2
^fits9:
core.br ^scan10(%56)
^scan10(%q: i64):
%58 = core.cmp.lt %q, %7 : bool
core.cond_br %58, ^bound11, ^scanned13(%q)
^bound11:
%59 = core.buffer_load %buf, %q : u8
%60 = core.cast %59 : i64
%61 = core.const 0 : i64
%62 = core.const 192 : i64
%63 = core.cmp.lt %60, %62 : bool
%64 = core.const 0 : i64
%65 = core.select %63, %64, %61 : i64
%66 = core.const 128 : i64
%67 = core.cmp.lt %60, %66 : bool
%68 = core.const 576460745995190270 : i64
%69 = core.select %67, %68, %65 : i64
%70 = core.const 64 : i64
%71 = core.cmp.lt %60, %70 : bool
%72 = core.const 287948901175001088 : i64
%73 = core.select %71, %72, %69 : i64
%74 = core.const 63 : i64
%75 = core.and %60, %74 : i64
%76 = core.shr %73, %75 : i64
%77 = core.const 1 : i64
%78 = core.and %76, %77 : i64
%79 = core.const 0 : i64
%80 = core.cmp.ne %78, %79 : bool
%81 = core.const 1 : i64
%82 = core.add %q, %81 {overflow = "proven"} : i64
core.cond_br %80, ^scan10(%82), ^scanned13(%q)
^scanned13(%0: i64):
%83 = core.cmp.lt %0, %56 : bool
core.cond_br %83, ^fail2, ^enough15
^exit14:
%84 = core.load %p : i64
%85 = core.cmp.eq %84, %7 : bool
core.cond_br %85, ^success5, ^fail2
^enough15:
core.store %0, %p
%86 = core.cmp.gt %0, %56 : bool
core.cond_br %86, ^keep17, ^exit14
^giveback16(%held: i64):
%87 = core.load %p : i64
%88 = core.const 1 : i64
%89 = core.sub %87, %88 {overflow = "proven"} : i64
core.store %89, %p
%90 = core.cmp.gt %89, %held : bool
core.cond_br %90, ^keep18, ^exit14
^keep17:
%91 = core.load %sp : i64
%92 = core.const 4096 : i64
%93 = core.cmp.lt %91, %92 : bool
core.guard %93 {kind = "range", label = "regex.stack.ok"}
%94 = core.const 3 : i64
%95 = core.mul %91, %94 {overflow = "proven"} : i64
%96 = core.ptr_offset %stack, %95 : ptr<i64, stack>
%97 = core.const 0 : i64
%98 = core.load %p : i64
%99 = core.const 0 : i64
%100 = core.ptr_offset %96, %99 : ptr<i64, stack>
core.store %97, %100
%101 = core.const 1 : i64
%102 = core.ptr_offset %96, %101 : ptr<i64, stack>
core.store %98, %102
%103 = core.const 2 : i64
%104 = core.ptr_offset %96, %103 : ptr<i64, stack>
core.store %56, %104
%105 = core.const 1 : i64
%106 = core.add %91, %105 {overflow = "proven"} : i64
core.store %106, %sp
core.br ^exit14
^keep18:
%107 = core.load %sp : i64
%108 = core.const 4096 : i64
%109 = core.cmp.lt %107, %108 : bool
core.guard %109 {kind = "range", label = "regex.stack.ok"}
%110 = core.const 3 : i64
%111 = core.mul %107, %110 {overflow = "proven"} : i64
%112 = core.ptr_offset %stack, %111 : ptr<i64, stack>
%113 = core.const 0 : i64
%114 = core.load %p : i64
%115 = core.const 0 : i64
%116 = core.ptr_offset %112, %115 : ptr<i64, stack>
core.store %113, %116
%117 = core.const 1 : i64
%118 = core.ptr_offset %112, %117 : ptr<i64, stack>
core.store %114, %118
%119 = core.const 2 : i64
%120 = core.ptr_offset %112, %119 : ptr<i64, stack>
core.store %held, %120
%121 = core.const 1 : i64
%122 = core.add %107, %121 {overflow = "proven"} : i64
core.store %122, %sp
core.br ^exit14
^pop19:
%123 = core.const 1 : i64
%124 = core.sub %22, %123 {overflow = "proven"} : i64
core.store %124, %sp
%125 = core.const 3 : i64
%126 = core.mul %124, %125 {overflow = "proven"} : i64
%127 = core.ptr_offset %stack, %126 : ptr<i64, stack>
%128 = core.const 0 : i64
%129 = core.ptr_offset %127, %128 : ptr<i64, stack>
%130 = core.load %129 : i64
%131 = core.const 1 : i64
%132 = core.ptr_offset %127, %131 : ptr<i64, stack>
%133 = core.load %132 : i64
core.store %133, %p
%134 = core.const 2 : i64
%135 = core.ptr_offset %127, %134 : ptr<i64, stack>
%136 = core.load %135 : i64
%137 = core.const 0 : i64
%138 = core.cmp.eq %130, %137 : bool
core.cond_br %138, ^giveback16(%136), ^dispatch20
^dispatch20:
core.unreachable
}
ppy emit c patterns.ppy
1270 lines
/* patterns: generated by ppy, C11 */
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#if defined(__GNUC__) || defined(__clang__)
#define PPY_UNREACHABLE() __builtin_unreachable()
#elif defined(_MSC_VER)
#define PPY_UNREACHABLE() __assume(0)
#else
#define PPY_UNREACHABLE() abort()
#endif
static inline int ppy_ovf_add_i64(int64_t a, int64_t b, int64_t *out) {
#if defined(__GNUC__) || defined(__clang__)
return __builtin_add_overflow(a, b, out);
#else
if ((b > 0 && a > INT64_MAX - b) || (b < 0 && a < INT64_MIN - b)) {
return 1;
}
*out = a + b;
return 0;
#endif
}
static inline int ppy_ovf_sub_i64(int64_t a, int64_t b, int64_t *out) {
#if defined(__GNUC__) || defined(__clang__)
return __builtin_sub_overflow(a, b, out);
#else
if ((b < 0 && a > INT64_MAX + b) || (b > 0 && a < INT64_MIN + b)) {
return 1;
}
*out = a - b;
return 0;
#endif
}
static inline int ppy_ovf_mul_i64(int64_t a, int64_t b, int64_t *out) {
#if defined(__GNUC__) || defined(__clang__)
return __builtin_mul_overflow(a, b, out);
#else
if (a > 0) {
if (b > 0) { if (a > INT64_MAX / b) return 1; }
else if (b < INT64_MIN / a) return 1;
} else if (b > 0) {
if (a < INT64_MIN / b) return 1;
} else if (a != 0 && b < INT64_MAX / a) return 1;
*out = a * b;
return 0;
#endif
}
int32_t ppy_patterns_count_words(uint8_t *text, int64_t text_len, int64_t *out);
int32_t ppy_patterns_longest_word(uint8_t *text, int64_t text_len, int64_t *out);
int32_t ppy_patterns_sum_values(uint8_t *text, int64_t text_len, int64_t *out);
int32_t ppy_patterns_count_hex(uint8_t *text, int64_t text_len, int64_t *out);
int32_t ppy_patterns_is_identifier(uint8_t *text, int64_t text_len, int8_t *out);
int32_t ppy_regex_0(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2);
int32_t ppy_regex_1(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2, int64_t *out3, int64_t *out4, int64_t *out5, int64_t *out6);
int32_t ppy_regex_2(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2);
int32_t ppy_regex_3(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2);
int32_t ppy_patterns_count_words(uint8_t *text, int64_t text_len, int64_t *out) {
int64_t m_spans[2];
int64_t n = 0;
int64_t pos = 0;
while (1) {
int8_t res;
int64_t t1;
int64_t t2;
if (ppy_regex_0(text, text_len, pos, text_len, &res, &t1, &t2) != 0) return 1; /* call.ok */
bool m_found = res != 0;
m_spans[0] = t1;
m_spans[1] = t2;
if (m_found == false) break;
if (ppy_ovf_add_i64(n, 1, &n)) return 1; /* arith.ok */
pos = m_spans[1];
}
*out = n;
return 0;
}
int32_t ppy_patterns_longest_word(uint8_t *text, int64_t text_len, int64_t *out) {
int64_t best;
int64_t pos;
bool m_found;
int64_t start;
int64_t end;
int64_t m_spans[2];
int8_t res;
int64_t t1;
int64_t t2;
int64_t t3;
best = 0;
pos = 0;
goto L1_while_head1;
L1_while_head1:;
if (pos < text_len) {
goto L2_while_body2;
} else {
goto L3_while_end3;
}
L2_while_body2:;
if (!(ppy_regex_0(text, text_len, pos, text_len, &res, &t1, &t2) == 0)) goto fallback; /* call.ok */
m_found = res != 0;
m_spans[0] = t1;
m_spans[1] = t2;
if (m_found ^ true) {
goto L4_then4;
} else {
goto L5_else5;
}
L3_while_end3:;
*out = best;
return 0;
L4_then4:;
goto L3_while_end3;
L5_else5:;
start = m_spans[0];
end = m_spans[1];
if (!(!ppy_ovf_sub_i64(end, start, &t3))) goto fallback; /* arith.ok */
best = t3 > best ? t3 : best;
pos = end;
goto L1_while_head1;
fallback:
return 1;
}
int32_t ppy_patterns_sum_values(uint8_t *text, int64_t text_len, int64_t *out) {
int64_t m_spans[6];
int64_t total = 0;
int64_t pos = 0;
while (1) {
int8_t res;
int64_t t1;
int64_t t2;
int64_t t3;
int64_t t4;
int64_t t5;
int64_t t6;
if (ppy_regex_1(text, text_len, pos, text_len, &res, &t1, &t2, &t3, &t4, &t5, &t6) != 0) return 1; /* call.ok */
bool m_found = res != 0;
m_spans[0] = t1;
m_spans[1] = t2;
m_spans[2] = t3;
m_spans[3] = t4;
m_spans[4] = t5;
m_spans[5] = t6;
if (m_found == false) break;
int64_t value = 0;
int64_t t7 = m_spans[4];
int64_t t8 = m_spans[5];
if (t7 < 0 || (int64_t)((uint64_t)t8 - 1u) >= text_len) return 1; /* bounds.ok */
int64_t i = t7;
while (i < t8) {
int64_t t9;
if (ppy_ovf_mul_i64(value, 10, &t9)) return 1; /* arith.ok */
int64_t t10;
if (ppy_ovf_sub_i64((int64_t)text[i], 48, &t10)) return 1; /* arith.ok */
if (ppy_ovf_add_i64(t9, t10, &value)) return 1; /* arith.ok */
if (ppy_ovf_add_i64(i, 1, &i)) return 1; /* arith.ok */
}
if (ppy_ovf_add_i64(total, value, &total)) return 1; /* arith.ok */
pos = m_spans[1];
}
*out = total;
return 0;
}
int32_t ppy_patterns_count_hex(uint8_t *text, int64_t text_len, int64_t *out) {
int64_t m_spans[2];
int64_t n = 0;
int64_t pos = 0;
while (1) {
int8_t res;
int64_t t1;
int64_t t2;
if (ppy_regex_2(text, text_len, pos, text_len, &res, &t1, &t2) != 0) return 1; /* call.ok */
bool m_found = res != 0;
m_spans[0] = t1;
m_spans[1] = t2;
if (m_found == false) break;
if (ppy_ovf_add_i64(n, 1, &n)) return 1; /* arith.ok */
pos = m_spans[1];
}
*out = n;
return 0;
}
int32_t ppy_patterns_is_identifier(uint8_t *text, int64_t text_len, int8_t *out) {
int8_t res;
int64_t res_2;
int64_t res_3;
if (ppy_regex_3(text, text_len, 0, text_len, &res, &res_2, &res_3) != 0) return 1; /* call.ok */
bool t1 = res != 0;
*out = (int8_t)t1;
return 0;
}
int32_t ppy_regex_0(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2) {
int64_t p;
int64_t sp;
int64_t start;
int64_t s;
int64_t q;
int64_t q_2;
int64_t held;
int64_t t1;
int64_t t2;
int64_t t3;
int64_t t4;
int64_t stack[12288];
int64_t caps[2];
int64_t pass;
int64_t t5;
int64_t t6;
int64_t t7;
int64_t pass_2;
int64_t pass_3;
int64_t pass_4;
int64_t t8;
int64_t pass_5;
int64_t pass_6;
int64_t t9;
int64_t t10;
int64_t *t11;
int64_t *t12;
int64_t t13;
int64_t *t14;
int64_t pass_7;
t1 = endpos < 0 ? 0 : endpos;
t2 = t1 > buf_len ? buf_len : t1;
t3 = pos < 0 ? 0 : pos;
t4 = t3 > buf_len ? buf_len : t3;
if (t4 > t2) {
goto L4_notfound4;
} else {
pass = t4;
s = pass;
goto L1_attempt1;
}
L1_attempt1:;
p = s;
start = s;
sp = 0;
caps[0] = -1;
caps[1] = -1;
caps[0] = s;
t5 = p;
if (t5 + 1 <= t2) {
goto L5_fits7;
} else {
goto L2_fail2;
}
L2_fail2:;
t6 = sp;
if (t6 == 0) {
goto L3_exhausted3;
} else {
goto L14_pop17;
}
L3_exhausted3:;
t7 = start + 1;
if (t7 > t2) {
goto L4_notfound4;
} else {
pass_2 = t7;
s = pass_2;
goto L1_attempt1;
}
L4_notfound4:;
*out0 = (int8_t)false;
*out1 = -1;
*out2 = -1;
return 0;
L5_fits7:;
pass_3 = t5;
q = pass_3;
goto L6_scan8;
L6_scan8:;
if (q < t2) {
goto L7_bound9;
} else {
pass_4 = q;
q_2 = pass_4;
goto L8_scanned11;
}
L7_bound9:;
t8 = (int64_t)buf[q];
if (((t8 >= 65) & (t8 <= 90)) | ((t8 >= 97) & (t8 <= 122))) {
pass_5 = q + 1;
q = pass_5;
goto L6_scan8;
} else {
pass_6 = q;
q_2 = pass_6;
goto L8_scanned11;
}
L8_scanned11:;
t9 = t5 + 1;
if (q_2 < t9) {
goto L2_fail2;
} else {
goto L10_enough13;
}
L9_exit12:;
caps[1] = p;
*out0 = (int8_t)true;
*out1 = caps[0];
*out2 = caps[1];
return 0;
L10_enough13:;
p = q_2;
if (q_2 > t9) {
goto L12_keep15;
} else {
goto L9_exit12;
}
L11_giveback14:;
t10 = p - 1;
p = t10;
if (t10 > held) {
goto L13_keep16;
} else {
goto L9_exit12;
}
L12_keep15:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t11 = stack + sp * 3;
t11[0] = 0;
t11[1] = p;
t11[2] = t9;
sp += 1;
goto L9_exit12;
L13_keep16:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t12 = stack + sp * 3;
t12[0] = 0;
t12[1] = p;
t12[2] = held;
sp += 1;
goto L9_exit12;
L14_pop17:;
t13 = t6 - 1;
sp = t13;
t14 = stack + t13 * 3;
p = t14[1];
if (t14[0] == 0) {
pass_7 = t14[2];
held = pass_7;
goto L11_giveback14;
} else {
goto L15_dispatch18;
}
L15_dispatch18:;
PPY_UNREACHABLE();
fallback:
return 1;
}
int32_t ppy_regex_1(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2, int64_t *out3, int64_t *out4, int64_t *out5, int64_t *out6) {
int64_t p;
int64_t sp;
int64_t start;
int64_t s;
int64_t held;
int64_t q;
int64_t q_2;
int64_t held_2;
int64_t held_3;
int64_t q_3;
int64_t q_4;
int64_t held_4;
int64_t q_5;
int64_t q_6;
int64_t held_5;
int64_t held_6;
int64_t q_7;
int64_t q_8;
int64_t held_7;
int64_t held_8;
int64_t t1;
int64_t t2;
int64_t t3;
int64_t t4;
int64_t stack[12288];
int64_t caps[6];
int64_t pass;
int64_t t5;
int64_t *t6;
int64_t t7;
int64_t t8;
int64_t t9;
int64_t pass_2;
int64_t pass_3;
int64_t pass_4;
int64_t t10;
int64_t pass_5;
int64_t pass_6;
int64_t t11;
int64_t t12;
int64_t *t13;
int64_t t14;
int64_t t15;
int64_t *t16;
int64_t *t17;
int64_t pass_7;
int64_t pass_8;
int64_t t18;
int64_t pass_9;
int64_t pass_10;
int64_t t19;
int64_t t20;
int64_t *t21;
int64_t *t22;
int64_t t23;
int64_t pass_11;
int64_t pass_12;
int64_t t24;
int64_t pass_13;
int64_t pass_14;
int64_t t25;
int64_t *t26;
int64_t t27;
int64_t t28;
int64_t *t29;
int64_t *t30;
int64_t pass_15;
int64_t pass_16;
int64_t t31;
int64_t pass_17;
int64_t pass_18;
int64_t t32;
int64_t t33;
int64_t *t34;
int64_t t35;
int64_t *t36;
int64_t *t37;
int64_t t38;
int64_t *t39;
int64_t t40;
int64_t t41;
int64_t pass_19;
int64_t pass_20;
int64_t pass_21;
int64_t pass_22;
int64_t pass_23;
int64_t pass_24;
int64_t pass_25;
int64_t pass_26;
t1 = endpos < 0 ? 0 : endpos;
t2 = t1 > buf_len ? buf_len : t1;
t3 = pos < 0 ? 0 : pos;
t4 = t3 > buf_len ? buf_len : t3;
if (t4 > t2) {
goto L4_notfound4;
} else {
pass = t4;
s = pass;
goto L1_attempt1;
}
L1_attempt1:;
p = s;
start = s;
sp = 0;
caps[0] = -1;
caps[1] = -1;
caps[2] = -1;
caps[3] = -1;
caps[4] = -1;
caps[5] = -1;
caps[0] = s;
t5 = caps[2];
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t6 = stack + sp * 3;
t6[0] = 0;
t6[1] = p;
t6[2] = t5;
sp += 1;
caps[2] = p;
t7 = p;
if (t7 + 1 <= t2) {
goto L6_fits8;
} else {
goto L2_fail2;
}
L2_fail2:;
t8 = sp;
if (t8 == 0) {
goto L3_exhausted3;
} else {
goto L47_pop53;
}
L3_exhausted3:;
t9 = start + 1;
if (t9 > t2) {
goto L4_notfound4;
} else {
pass_2 = t9;
s = pass_2;
goto L1_attempt1;
}
L4_notfound4:;
*out0 = (int8_t)false;
*out1 = -1;
*out2 = -1;
*out3 = -1;
*out4 = -1;
*out5 = -1;
*out6 = -1;
return 0;
L5_restore7:;
caps[2] = held;
goto L2_fail2;
L6_fits8:;
pass_3 = t7;
q = pass_3;
goto L7_scan9;
L7_scan9:;
if (q < t2) {
goto L8_bound10;
} else {
pass_4 = q;
q_2 = pass_4;
goto L9_scanned12;
}
L8_bound10:;
t10 = (int64_t)buf[q];
if ((((t10 < 64 ? INT64_C(287948901175001088) : (t10 < 128 ? INT64_C(576460745995190270) : (t10 < 192 ? 0 : 0))) >> (t10 & 63)) & 1) != 0) {
pass_5 = q + 1;
q = pass_5;
goto L7_scan9;
} else {
pass_6 = q;
q_2 = pass_6;
goto L9_scanned12;
}
L9_scanned12:;
t11 = t7 + 1;
if (q_2 < t11) {
goto L2_fail2;
} else {
goto L11_enough14;
}
L10_exit13:;
t12 = caps[3];
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t13 = stack + sp * 3;
t13[0] = 2;
t13[1] = p;
t13[2] = t12;
sp += 1;
caps[3] = p;
t14 = p;
if (t14 <= t2) {
goto L16_fits19;
} else {
goto L2_fail2;
}
L11_enough14:;
p = q_2;
if (q_2 > t11) {
goto L13_keep16;
} else {
goto L10_exit13;
}
L12_giveback15:;
t15 = p - 1;
p = t15;
if (t15 > held_2) {
goto L14_keep17;
} else {
goto L10_exit13;
}
L13_keep16:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t16 = stack + sp * 3;
t16[0] = 1;
t16[1] = p;
t16[2] = t11;
sp += 1;
goto L10_exit13;
L14_keep17:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t17 = stack + sp * 3;
t17[0] = 1;
t17[1] = p;
t17[2] = held_2;
sp += 1;
goto L10_exit13;
L15_restore18:;
caps[3] = held_3;
goto L2_fail2;
L16_fits19:;
pass_7 = t14;
q_3 = pass_7;
goto L17_scan20;
L17_scan20:;
if (q_3 < t2) {
goto L18_bound21;
} else {
pass_8 = q_3;
q_4 = pass_8;
goto L19_scanned23;
}
L18_bound21:;
t18 = (int64_t)buf[q_3];
if (((t18 >= 9) & (t18 <= 13)) | (t18 == 32)) {
pass_9 = q_3 + 1;
q_3 = pass_9;
goto L17_scan20;
} else {
pass_10 = q_3;
q_4 = pass_10;
goto L19_scanned23;
}
L19_scanned23:;
if (q_4 < t14) {
goto L2_fail2;
} else {
goto L21_enough25;
}
L20_exit24:;
t19 = p;
if (t19 < t2) {
goto L25_read29;
} else {
goto L2_fail2;
}
L21_enough25:;
p = q_4;
if (q_4 > t14) {
goto L23_keep27;
} else {
goto L20_exit24;
}
L22_giveback26:;
t20 = p - 1;
p = t20;
if (t20 > held_4) {
goto L24_keep28;
} else {
goto L20_exit24;
}
L23_keep27:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t21 = stack + sp * 3;
t21[0] = 3;
t21[1] = p;
t21[2] = t14;
sp += 1;
goto L20_exit24;
L24_keep28:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t22 = stack + sp * 3;
t22[0] = 3;
t22[1] = p;
t22[2] = held_4;
sp += 1;
goto L20_exit24;
L25_read29:;
if ((int64_t)buf[t19] == 61) {
goto L26_advance30;
} else {
goto L2_fail2;
}
L26_advance30:;
p = t19 + 1;
t23 = p;
if (t23 <= t2) {
goto L27_fits31;
} else {
goto L2_fail2;
}
L27_fits31:;
pass_11 = t23;
q_5 = pass_11;
goto L28_scan32;
L28_scan32:;
if (q_5 < t2) {
goto L29_bound33;
} else {
pass_12 = q_5;
q_6 = pass_12;
goto L30_scanned35;
}
L29_bound33:;
t24 = (int64_t)buf[q_5];
if (((t24 >= 9) & (t24 <= 13)) | (t24 == 32)) {
pass_13 = q_5 + 1;
q_5 = pass_13;
goto L28_scan32;
} else {
pass_14 = q_5;
q_6 = pass_14;
goto L30_scanned35;
}
L30_scanned35:;
if (q_6 < t23) {
goto L2_fail2;
} else {
goto L32_enough37;
}
L31_exit36:;
t25 = caps[4];
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t26 = stack + sp * 3;
t26[0] = 5;
t26[1] = p;
t26[2] = t25;
sp += 1;
caps[4] = p;
t27 = p;
if (t27 + 1 <= t2) {
goto L37_fits42;
} else {
goto L2_fail2;
}
L32_enough37:;
p = q_6;
if (q_6 > t23) {
goto L34_keep39;
} else {
goto L31_exit36;
}
L33_giveback38:;
t28 = p - 1;
p = t28;
if (t28 > held_5) {
goto L35_keep40;
} else {
goto L31_exit36;
}
L34_keep39:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t29 = stack + sp * 3;
t29[0] = 4;
t29[1] = p;
t29[2] = t23;
sp += 1;
goto L31_exit36;
L35_keep40:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t30 = stack + sp * 3;
t30[0] = 4;
t30[1] = p;
t30[2] = held_5;
sp += 1;
goto L31_exit36;
L36_restore41:;
caps[4] = held_6;
goto L2_fail2;
L37_fits42:;
pass_15 = t27;
q_7 = pass_15;
goto L38_scan43;
L38_scan43:;
if (q_7 < t2) {
goto L39_bound44;
} else {
pass_16 = q_7;
q_8 = pass_16;
goto L40_scanned46;
}
L39_bound44:;
t31 = (int64_t)buf[q_7];
if ((t31 >= 48) & (t31 <= 57)) {
pass_17 = q_7 + 1;
q_7 = pass_17;
goto L38_scan43;
} else {
pass_18 = q_7;
q_8 = pass_18;
goto L40_scanned46;
}
L40_scanned46:;
t32 = t27 + 1;
if (q_8 < t32) {
goto L2_fail2;
} else {
goto L42_enough48;
}
L41_exit47:;
t33 = caps[5];
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t34 = stack + sp * 3;
t34[0] = 7;
t34[1] = p;
t34[2] = t33;
sp += 1;
caps[5] = p;
caps[1] = p;
*out0 = (int8_t)true;
*out1 = caps[0];
*out2 = caps[1];
*out3 = caps[2];
*out4 = caps[3];
*out5 = caps[4];
*out6 = caps[5];
return 0;
L42_enough48:;
p = q_8;
if (q_8 > t32) {
goto L44_keep50;
} else {
goto L41_exit47;
}
L43_giveback49:;
t35 = p - 1;
p = t35;
if (t35 > held_7) {
goto L45_keep51;
} else {
goto L41_exit47;
}
L44_keep50:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t36 = stack + sp * 3;
t36[0] = 6;
t36[1] = p;
t36[2] = t32;
sp += 1;
goto L41_exit47;
L45_keep51:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t37 = stack + sp * 3;
t37[0] = 6;
t37[1] = p;
t37[2] = held_7;
sp += 1;
goto L41_exit47;
L46_restore52:;
caps[5] = held_8;
goto L2_fail2;
L47_pop53:;
t38 = t8 - 1;
sp = t38;
t39 = stack + t38 * 3;
t40 = t39[0];
p = t39[1];
t41 = t39[2];
if (t40 == 0) {
pass_19 = t41;
held = pass_19;
goto L5_restore7;
} else {
goto L48_dispatch54;
}
L48_dispatch54:;
if (t40 == 1) {
pass_20 = t41;
held_2 = pass_20;
goto L12_giveback15;
} else {
goto L49_dispatch55;
}
L49_dispatch55:;
if (t40 == 2) {
pass_21 = t41;
held_3 = pass_21;
goto L15_restore18;
} else {
goto L50_dispatch56;
}
L50_dispatch56:;
if (t40 == 3) {
pass_22 = t41;
held_4 = pass_22;
goto L22_giveback26;
} else {
goto L51_dispatch57;
}
L51_dispatch57:;
if (t40 == 4) {
pass_23 = t41;
held_5 = pass_23;
goto L33_giveback38;
} else {
goto L52_dispatch58;
}
L52_dispatch58:;
if (t40 == 5) {
pass_24 = t41;
held_6 = pass_24;
goto L36_restore41;
} else {
goto L53_dispatch59;
}
L53_dispatch59:;
if (t40 == 6) {
pass_25 = t41;
held_7 = pass_25;
goto L43_giveback49;
} else {
goto L54_dispatch60;
}
L54_dispatch60:;
if (t40 == 7) {
pass_26 = t41;
held_8 = pass_26;
goto L46_restore52;
} else {
goto L55_dispatch61;
}
L55_dispatch61:;
PPY_UNREACHABLE();
fallback:
return 1;
}
int32_t ppy_regex_2(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2) {
int64_t p;
int64_t sp;
int64_t start;
int64_t s;
int64_t q;
int64_t q_2;
int64_t held;
int64_t t1;
int64_t t2;
int64_t t3;
int64_t t4;
int64_t stack[12288];
int64_t caps[2];
int64_t pass;
int64_t t5;
int64_t t6;
int64_t t7;
int64_t pass_2;
int64_t t8;
int64_t t9;
int64_t t10;
int64_t pass_3;
int64_t pass_4;
int64_t t11;
int64_t pass_5;
int64_t pass_6;
int64_t t12;
int64_t t13;
int64_t *t14;
int64_t *t15;
int64_t t16;
int64_t *t17;
int64_t pass_7;
t1 = endpos < 0 ? 0 : endpos;
t2 = t1 > buf_len ? buf_len : t1;
t3 = pos < 0 ? 0 : pos;
t4 = t3 > buf_len ? buf_len : t3;
if (t4 > t2) {
goto L4_notfound4;
} else {
pass = t4;
s = pass;
goto L1_attempt1;
}
L1_attempt1:;
p = s;
start = s;
sp = 0;
caps[0] = -1;
caps[1] = -1;
caps[0] = s;
t5 = p;
if (t5 < t2) {
goto L5_read7;
} else {
goto L2_fail2;
}
L2_fail2:;
t6 = sp;
if (t6 == 0) {
goto L3_exhausted3;
} else {
goto L18_pop21;
}
L3_exhausted3:;
t7 = start + 1;
if (t7 > t2) {
goto L4_notfound4;
} else {
pass_2 = t7;
s = pass_2;
goto L1_attempt1;
}
L4_notfound4:;
*out0 = (int8_t)false;
*out1 = -1;
*out2 = -1;
return 0;
L5_read7:;
if ((int64_t)buf[t5] == 48) {
goto L6_advance8;
} else {
goto L2_fail2;
}
L6_advance8:;
p = t5 + 1;
t8 = p;
if (t8 < t2) {
goto L7_read9;
} else {
goto L2_fail2;
}
L7_read9:;
t9 = (int64_t)buf[t8];
if ((t9 == 88) | (t9 == 120)) {
goto L8_advance10;
} else {
goto L2_fail2;
}
L8_advance10:;
p = t8 + 1;
t10 = p;
if (t10 + 1 <= t2) {
goto L9_fits11;
} else {
goto L2_fail2;
}
L9_fits11:;
pass_3 = t10;
q = pass_3;
goto L10_scan12;
L10_scan12:;
if (q < t2) {
goto L11_bound13;
} else {
pass_4 = q;
q_2 = pass_4;
goto L12_scanned15;
}
L11_bound13:;
t11 = (int64_t)buf[q];
if ((((t11 >= 48) & (t11 <= 57)) | ((t11 >= 65) & (t11 <= 70))) | ((t11 >= 97) & (t11 <= 102))) {
pass_5 = q + 1;
q = pass_5;
goto L10_scan12;
} else {
pass_6 = q;
q_2 = pass_6;
goto L12_scanned15;
}
L12_scanned15:;
t12 = t10 + 1;
if (q_2 < t12) {
goto L2_fail2;
} else {
goto L14_enough17;
}
L13_exit16:;
caps[1] = p;
*out0 = (int8_t)true;
*out1 = caps[0];
*out2 = caps[1];
return 0;
L14_enough17:;
p = q_2;
if (q_2 > t12) {
goto L16_keep19;
} else {
goto L13_exit16;
}
L15_giveback18:;
t13 = p - 1;
p = t13;
if (t13 > held) {
goto L17_keep20;
} else {
goto L13_exit16;
}
L16_keep19:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t14 = stack + sp * 3;
t14[0] = 0;
t14[1] = p;
t14[2] = t12;
sp += 1;
goto L13_exit16;
L17_keep20:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t15 = stack + sp * 3;
t15[0] = 0;
t15[1] = p;
t15[2] = held;
sp += 1;
goto L13_exit16;
L18_pop21:;
t16 = t6 - 1;
sp = t16;
t17 = stack + t16 * 3;
p = t17[1];
if (t17[0] == 0) {
pass_7 = t17[2];
held = pass_7;
goto L15_giveback18;
} else {
goto L19_dispatch22;
}
L19_dispatch22:;
PPY_UNREACHABLE();
fallback:
return 1;
}
int32_t ppy_regex_3(uint8_t *buf, int64_t buf_len, int64_t pos, int64_t endpos, int8_t *out0, int64_t *out1, int64_t *out2) {
int64_t p;
int64_t sp;
int64_t q;
int64_t q_2;
int64_t held;
int64_t t1;
int64_t t2;
int64_t t3;
int64_t t4;
int64_t stack[12288];
int64_t caps[2];
int64_t t5;
int64_t t6;
int64_t t7;
int64_t t8;
int64_t pass;
int64_t pass_2;
int64_t t9;
int64_t pass_3;
int64_t pass_4;
int64_t t10;
int64_t *t11;
int64_t *t12;
int64_t t13;
int64_t *t14;
int64_t pass_5;
t1 = endpos < 0 ? 0 : endpos;
t2 = t1 > buf_len ? buf_len : t1;
t3 = pos < 0 ? 0 : pos;
t4 = t3 > buf_len ? buf_len : t3;
p = t4;
sp = 0;
caps[0] = -1;
caps[1] = -1;
caps[0] = t4;
t5 = p;
if (t5 < t2) {
goto L4_read7;
} else {
goto L1_fail2;
}
L1_fail2:;
t6 = sp;
if (t6 == 0) {
goto L2_exhausted3;
} else {
goto L15_pop19;
}
L2_exhausted3:;
*out0 = (int8_t)false;
*out1 = -1;
*out2 = -1;
return 0;
L3_success5:;
caps[1] = p;
*out0 = (int8_t)true;
*out1 = caps[0];
*out2 = caps[1];
return 0;
L4_read7:;
t7 = (int64_t)buf[t5];
if ((((t7 >= 65) & (t7 <= 90)) | (t7 == 95)) | ((t7 >= 97) & (t7 <= 122))) {
goto L5_advance8;
} else {
goto L1_fail2;
}
L5_advance8:;
p = t5 + 1;
t8 = p;
if (t8 <= t2) {
goto L6_fits9;
} else {
goto L1_fail2;
}
L6_fits9:;
pass = t8;
q = pass;
goto L7_scan10;
L7_scan10:;
if (q < t2) {
goto L8_bound11;
} else {
pass_2 = q;
q_2 = pass_2;
goto L9_scanned13;
}
L8_bound11:;
t9 = (int64_t)buf[q];
if ((((t9 < 64 ? INT64_C(287948901175001088) : (t9 < 128 ? INT64_C(576460745995190270) : (t9 < 192 ? 0 : 0))) >> (t9 & 63)) & 1) != 0) {
pass_3 = q + 1;
q = pass_3;
goto L7_scan10;
} else {
pass_4 = q;
q_2 = pass_4;
goto L9_scanned13;
}
L9_scanned13:;
if (q_2 < t8) {
goto L1_fail2;
} else {
goto L11_enough15;
}
L10_exit14:;
if (p == t2) {
goto L3_success5;
} else {
goto L1_fail2;
}
L11_enough15:;
p = q_2;
if (q_2 > t8) {
goto L13_keep17;
} else {
goto L10_exit14;
}
L12_giveback16:;
t10 = p - 1;
p = t10;
if (t10 > held) {
goto L14_keep18;
} else {
goto L10_exit14;
}
L13_keep17:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t11 = stack + sp * 3;
t11[0] = 0;
t11[1] = p;
t11[2] = t8;
sp += 1;
goto L10_exit14;
L14_keep18:;
if (!(sp < 4096)) goto fallback; /* regex.stack.ok */
t12 = stack + sp * 3;
t12[0] = 0;
t12[1] = p;
t12[2] = held;
sp += 1;
goto L10_exit14;
L15_pop19:;
t13 = t6 - 1;
sp = t13;
t14 = stack + t13 * 3;
p = t14[1];
if (t14[0] == 0) {
pass_5 = t14[2];
held = pass_5;
goto L12_giveback16;
} else {
goto L16_dispatch20;
}
L16_dispatch20:;
PPY_UNREACHABLE();
fallback:
return 1;
}
One pattern, one matcher¶
WORD = re.compile(rb"[A-Za-z]+")
PAIR = re.compile(rb"(?P<key>\w+)\s*=\s*(\d+)")
def sum_values(text: Buffer[ppy.u8]) -> int:
total = 0
pos = 0
while True:
m = PAIR.search(text, pos)
if m is None:
return total
value = 0
for i in range(m.start(2), m.end(2)):
value = value * 10 + (text[i] - 48)
total += value
pos = m.end()
PAIR.search(text, pos) becomes a regex.search operation in the IR. The
lower-regex pass compiles the pattern into a private function of core
operations, so the LLVM and C backends run it without a regex library.
The match is a local. m is None and if m: narrow it, and m.start(2),
m.end(), and start, end = m.span() are loads.
The matcher backtracks the way re does (ordered alternation, greedy and
lazy repeats, a group's last iteration), so the spans agree byte for byte. A
repeat of one byte class such as [A-Za-z]+ scans its run rather than
pushing per byte.
The same source runs unchanged on CPython, where re accepts the
array.array("B") the buffer is.
Compared with CPython's re and Rust's regex¶
The benchmark makes four passes over 400,000 generated lines (7.9 MB): count
the words, find the longest, sum the name = value pairs, count the hex
numbers. The programs are in compare/:
patterns_bench.ppy,
patterns_re.py, and
patterns.rs with its
Cargo.toml. The site shows them whole. Times are in
milliseconds, best of five passes, mean and spread over five processes.
PPy: a pattern compiled from a bytes literal, search from a position,
m.end(). The same file runs on CPython.
def sum_values(text: Buffer[ppy.u8]) -> int:
total = 0
pos = 0
while True:
m = PAIR.search(text, pos)
if m is None:
return total
value = 0
for i in range(m.start(2), m.end(2)):
value = value * 10 + (text[i] - 48)
total += value
pos = m.end()
CPython re: the idiomatic spelling, finditer and int(m.group(2)).
Rust regex: captures_iter over &[u8], the number parsed from the
captured bytes, built with cargo build --release.
fn sum_values(pair: &Regex, text: &[u8]) -> u64 {
pair.captures_iter(text)
.map(|c| std::str::from_utf8(&c[2]).unwrap().parse::<u64>().unwrap())
.sum()
}
PPy ppy run |
CPython re |
Rust regex |
|
|---|---|---|---|
| count_words | 6.57 ± 0.05 | 107.07 ± 1.27 | 18.23 ± 0.13 |
| longest_word | 6.53 ± 0.05 | 133.05 ± 8.69 | 36.85 ± 2.99 |
| sum_values | 56.70 ± 0.33 | 198.20 ± 1.86 | 65.62 ± 0.79 |
| count_hex | 6.59 ± 0.03 | 10.22 ± 0.10 | 2.33 ± 0.10 |
PPy compiles each pattern into a function of core operations: a backtracker
in which a byte-class repeat scans its run instead of stepping byte by byte.
A match is a few locals rather than an object, so a search loop is a native
loop. That is the whole distance to re, whose matcher is a bytecode
interpreter allocating a match object per hit.
Against Rust, the pattern decides:
[A-Za-z]+is a scan in both engines.\w+\s*=\s*(\d+)makesregexfall back to its slower capturing engine.0x[0-9a-f]+under(?i)is where its lazy DFA wins outright. A literal-prefix prefilter is the obvious next step for the PPy matcher there.
Intel Core Ultra 9 386H; rustc 1.95.0 with regex 1.x, CPython 3.12.13 for
re, PPy on CPython 3.13.13, from a checkout on a native filesystem.
What stays with Python¶
m.group() returns bytes, which has no native form, so a function that calls
it keeps running on Python. Read the buffer between start() and end()
instead.
- Backreferences, lookahead and lookbehind, atomic groups, and possessive
repeats are refused when the pattern is analysed, and
ppy explainnames the construct. - A match that would need more than the matcher's stack of 4096 entries
(
(a|b)*cover a long input without ac) fails a guard, and the function falls back torefor that call.
Read on: Regular expressions · The IR: the regex dialect · Substring search
patterns.ppy is hand-written; there is no .py source and no conversion step.
43_regex/patterns.ppy¶
"""Regular expressions over a byte buffer, compiled to native matchers."""
import array
import re
import time
import ppy
from ppy import Buffer
WORD = re.compile(rb"[A-Za-z]+")
PAIR = re.compile(rb"(?P<key>\w+)\s*=\s*(\d+)")
HEX = re.compile(rb"0x[0-9a-f]+", re.IGNORECASE)
def count_words(text: Buffer[ppy.u8]) -> int:
n = 0
pos = 0
while True:
m = WORD.search(text, pos)
if m is None:
return n
n += 1
pos = m.end()
def longest_word(text: Buffer[ppy.u8]) -> int:
best = 0
pos = 0
while pos < len(text):
m = WORD.search(text, pos)
if not m:
break
start, end = m.span()
best = max(best, end - start)
pos = end
return best
def sum_values(text: Buffer[ppy.u8]) -> int:
total = 0
pos = 0
while True:
m = PAIR.search(text, pos)
if m is None:
return total
value = 0
for i in range(m.start(2), m.end(2)):
value = value * 10 + (text[i] - 48)
total += value
pos = m.end()
def count_hex(text: Buffer[ppy.u8]) -> int:
n = 0
pos = 0
while True:
m = HEX.search(text, pos)
if m is None:
return n
n += 1
pos = m.end()
def is_identifier(text: Buffer[ppy.u8]) -> bool:
return re.fullmatch(rb"[A-Za-z_]\w*", text) is not None
def make_text(lines: int) -> Buffer[ppy.u8]:
"""`lines` lines of `name = value` pairs, words, and hex numbers, the same every run."""
words = [b"alpha", b"beta", b"gamma", b"delta", b"epsilon", b"zeta", b"eta", b"theta"]
parts = []
state = 12345
for i in range(lines):
state = (state * 1103515245 + 12345) % (1 << 31)
word = words[state % len(words)]
parts.append(word + b" = " + str(state % 1000).encode() + b" " + word.upper())
if i % 7 == 0:
parts.append(b"0x" + format(state, "x").encode())
parts.append(b"\n")
return memoryview(array.array("B", b"".join(parts)))
def main() -> None:
text = make_text(50_000)
print(len(text), "bytes")
started = time.perf_counter()
print(count_words(text), longest_word(text), sum_values(text), count_hex(text))
elapsed = time.perf_counter() - started
snake = array.array("B", b"snake_case9")
lives = array.array("B", b"9lives")
print(is_identifier(snake), is_identifier(lives))
print(f"# four passes: {elapsed * 1000:.1f} ms")
main()
Counterpart programs¶
The programs the comparison above measured, each written the way its tool expects. The PPy one is first.
patterns_bench.ppy (PPy)
"""The four passes of `patterns.ppy` over 400,000 lines, timed: the PPY side of the comparison."""
import array
import re
import time
import ppy
from ppy import Buffer
WORD = re.compile(rb"[A-Za-z]+")
PAIR = re.compile(rb"(?P<key>\w+)\s*=\s*(\d+)")
HEX = re.compile(rb"0x[0-9a-f]+", re.IGNORECASE)
def count_words(text: Buffer[ppy.u8]) -> int:
n = 0
pos = 0
while True:
m = WORD.search(text, pos)
if m is None:
return n
n += 1
pos = m.end()
def longest_word(text: Buffer[ppy.u8]) -> int:
best = 0
pos = 0
while pos < len(text):
m = WORD.search(text, pos)
if not m:
break
start, end = m.span()
best = max(best, end - start)
pos = end
return best
def sum_values(text: Buffer[ppy.u8]) -> int:
total = 0
pos = 0
while True:
m = PAIR.search(text, pos)
if m is None:
return total
value = 0
for i in range(m.start(2), m.end(2)):
value = value * 10 + (text[i] - 48)
total += value
pos = m.end()
def count_hex(text: Buffer[ppy.u8]) -> int:
n = 0
pos = 0
while True:
m = HEX.search(text, pos)
if m is None:
return n
n += 1
pos = m.end()
def make_text(lines: int) -> Buffer[ppy.u8]:
"""`lines` lines of `name = value` pairs, words, and hex numbers, the same every run."""
words = [b"alpha", b"beta", b"gamma", b"delta", b"epsilon", b"zeta", b"eta", b"theta"]
parts = []
state = 12345
for i in range(lines):
state = (state * 1103515245 + 12345) % (1 << 31)
word = words[state % len(words)]
parts.append(word + b" = " + str(state % 1000).encode() + b" " + word.upper())
if i % 7 == 0:
parts.append(b"0x" + format(state, "x").encode())
parts.append(b"\n")
return memoryview(array.array("B", b"".join(parts)))
def main() -> None:
text = make_text(400_000)
for _ in range(2):
count_words(text)
longest_word(text)
sum_values(text)
count_hex(text)
print(len(text))
best = [1e9, 1e9, 1e9, 1e9]
answers = [0, 0, 0, 0]
for _ in range(5):
started = time.perf_counter()
answers[0] = count_words(text)
best[0] = min(best[0], (time.perf_counter() - started) * 1000.0)
started = time.perf_counter()
answers[1] = longest_word(text)
best[1] = min(best[1], (time.perf_counter() - started) * 1000.0)
started = time.perf_counter()
answers[2] = sum_values(text)
best[2] = min(best[2], (time.perf_counter() - started) * 1000.0)
started = time.perf_counter()
answers[3] = count_hex(text)
best[3] = min(best[3], (time.perf_counter() - started) * 1000.0)
labels = ["count_words", "longest_word", "sum_values", "count_hex"]
for label, answer, took in zip(labels, answers, best, strict=True):
print(f"# {label}: {took:.2f} ms")
print(answer)
main()
patterns.rs (Rust)
//! The same four passes with Rust's `regex` crate over bytes.
use regex::bytes::{Regex, RegexBuilder};
use std::time::Instant;
fn make_text(lines: u64) -> Vec<u8> {
let words: [&[u8]; 8] = [
b"alpha", b"beta", b"gamma", b"delta", b"epsilon", b"zeta", b"eta", b"theta",
];
let mut out = Vec::new();
let mut state: u64 = 12345;
for i in 0..lines {
state = (state * 1103515245 + 12345) % (1 << 31);
let word = words[(state % 8) as usize];
out.extend_from_slice(word);
out.extend_from_slice(b" = ");
out.extend_from_slice((state % 1000).to_string().as_bytes());
out.extend_from_slice(b" ");
out.extend_from_slice(word.to_ascii_uppercase().as_slice());
if i % 7 == 0 {
out.extend_from_slice(format!("0x{:x}", state).as_bytes());
}
out.push(b'\n');
}
out
}
fn count_words(word: &Regex, text: &[u8]) -> u64 {
word.find_iter(text).count() as u64
}
fn longest_word(word: &Regex, text: &[u8]) -> u64 {
word.find_iter(text).map(|m| m.len() as u64).max().unwrap_or(0)
}
fn sum_values(pair: &Regex, text: &[u8]) -> u64 {
pair.captures_iter(text)
.map(|c| std::str::from_utf8(&c[2]).unwrap().parse::<u64>().unwrap())
.sum()
}
fn count_hex(hex: &Regex, text: &[u8]) -> u64 {
hex.find_iter(text).count() as u64
}
fn timed(label: &str, mut run: impl FnMut() -> u64) -> u64 {
let mut best = f64::INFINITY;
let mut answer = 0;
for _ in 0..5 {
let started = Instant::now();
answer = run();
best = best.min(started.elapsed().as_secs_f64());
}
println!("# {}: {:.2} ms", label, best * 1000.0);
answer
}
fn main() {
let word = Regex::new(r"[A-Za-z]+").unwrap();
let pair = Regex::new(r"(?P<key>\w+)\s*=\s*(\d+)").unwrap();
let hex = RegexBuilder::new(r"0x[0-9a-f]+").case_insensitive(true).build().unwrap();
let text = make_text(400_000);
println!("{}", text.len());
println!("{}", timed("count_words", || count_words(&word, &text)));
println!("{}", timed("longest_word", || longest_word(&word, &text)));
println!("{}", timed("sum_values", || sum_values(&pair, &text)));
println!("{}", timed("count_hex", || count_hex(&hex, &text)));
}
patterns_codon.py (Python)
"""The `re` program as Codon compiles it: the same source, Codon's own `re`."""
import re
import time
WORD = re.compile(rb"[A-Za-z]+")
PAIR = re.compile(rb"(?P<key>\w+)\s*=\s*(\d+)")
HEX = re.compile(rb"0x[0-9a-f]+", re.IGNORECASE)
def count_words(text: bytes) -> int:
return sum(1 for _ in WORD.finditer(text))
def longest_word(text: bytes) -> int:
return max((m.end() - m.start() for m in WORD.finditer(text)), default=0)
def sum_values(text: bytes) -> int:
return sum(int(m.group(2)) for m in PAIR.finditer(text))
def count_hex(text: bytes) -> int:
return sum(1 for _ in HEX.finditer(text))
def make_text(lines: int) -> bytes:
words = [b"alpha", b"beta", b"gamma", b"delta", b"epsilon", b"zeta", b"eta", b"theta"]
parts = []
state = 12345
for i in range(lines):
state = (state * 1103515245 + 12345) % (1 << 31)
word = words[state % len(words)]
parts.append(word + b" = " + str(state % 1000).encode() + b" " + word.upper())
if i % 7 == 0:
parts.append(b"0x" + format(state, "x").encode())
parts.append(b"\n")
return b"".join(parts)
def timed(label, run, text):
best = 1e9
answer = 0
for _ in range(5):
started = time.perf_counter()
answer = run(text)
best = min(best, time.perf_counter() - started)
print(f"# {label}: {best * 1000:.2f} ms")
return answer
def main():
text = make_text(400_000)
print(len(text))
print(timed("count_words", count_words, text))
print(timed("longest_word", longest_word, text))
print(timed("sum_values", sum_values, text))
print(timed("count_hex", count_hex, text))
main()
patterns_re.py (Python)
"""The same four passes with CPython's `re`, written the way `re` is written."""
import re
import time
WORD = re.compile(rb"[A-Za-z]+")
PAIR = re.compile(rb"(?P<key>\w+)\s*=\s*(\d+)")
HEX = re.compile(rb"0x[0-9a-f]+", re.IGNORECASE)
def count_words(text: bytes) -> int:
return sum(1 for _ in WORD.finditer(text))
def longest_word(text: bytes) -> int:
return max((m.end() - m.start() for m in WORD.finditer(text)), default=0)
def sum_values(text: bytes) -> int:
return sum(int(m.group(2)) for m in PAIR.finditer(text))
def count_hex(text: bytes) -> int:
return sum(1 for _ in HEX.finditer(text))
def make_text(lines: int) -> bytes:
words = [b"alpha", b"beta", b"gamma", b"delta", b"epsilon", b"zeta", b"eta", b"theta"]
parts = []
state = 12345
for i in range(lines):
state = (state * 1103515245 + 12345) % (1 << 31)
word = words[state % len(words)]
parts.append(word + b" = " + str(state % 1000).encode() + b" " + word.upper())
if i % 7 == 0:
parts.append(b"0x" + format(state, "x").encode())
parts.append(b"\n")
return b"".join(parts)
def timed(label, run, text):
best = 1e9
answer = 0
for _ in range(5):
started = time.perf_counter()
answer = run(text)
best = min(best, time.perf_counter() - started)
print(f"# {label}: {best * 1000:.2f} ms")
return answer
def main():
text = make_text(400_000)
print(len(text))
print(timed("count_words", count_words, text))
print(timed("longest_word", longest_word, text))
print(timed("sum_values", sum_values, text))
print(timed("count_hex", count_hex, text))
main()
Source: examples/43_regex.