Skip to content

Commit b54cd29

Browse files
feat: tested succinct regex
1 parent 0840458 commit b54cd29

File tree

5 files changed

+51
-41
lines changed

5 files changed

+51
-41
lines changed

circom/circuits/regex_helpers.circom

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,48 +57,57 @@ template CheckByteRangeTransition() {
5757
out <== MultiAND(4)([isCurrentState, isNextState, isByteValid[0], isByteValid[1]]);
5858
}
5959

60-
template CheckByteTransitionWithCapture() {
60+
template CheckByteTransitionWithCapture(numCaptureGroups) {
6161
signal input currState;
6262
signal input nextState;
6363
signal input byte;
64-
signal input captureGroupId;
65-
signal input captureGroupStart;
64+
signal input captureGroupId[numCaptureGroups];
65+
signal input captureGroupStart[numCaptureGroups];
6666

6767
signal input inCurrState;
6868
signal input inNextState;
6969
signal input inByte;
70-
signal input inCaptureGroupId;
71-
signal input inCaptureGroupStart;
70+
signal input inCaptureGroupId[numCaptureGroups];
71+
signal input inCaptureGroupStart[numCaptureGroups];
7272
signal output out;
7373

74-
signal isCaptureGroupEqual <== IsEqual()([captureGroupId, inCaptureGroupId]);
75-
signal isCaptureGroupStartEqual <== IsEqual()([captureGroupStart, inCaptureGroupStart]);
74+
component isCaptureGroupEqual = MultiAND(numCaptureGroups);
75+
component isCaptureGroupStartEqual = MultiAND(numCaptureGroups);
76+
77+
for (var i = 0; i < numCaptureGroups; i++) {
78+
isCaptureGroupEqual.in[i] <== IsEqual()([captureGroupId[i], inCaptureGroupId[i]]);
79+
isCaptureGroupStartEqual.in[i] <== IsEqual()([captureGroupStart[i], inCaptureGroupStart[i]]);
80+
}
7681
signal isValidTransition <== CheckByteTransition()(currState, nextState, byte, inCurrState, inNextState, inByte);
7782

78-
out <== MultiAND(3)([isValidTransition, isCaptureGroupEqual, isCaptureGroupStartEqual]);
83+
out <== MultiAND(3)([isValidTransition, isCaptureGroupEqual.out, isCaptureGroupStartEqual.out]);
7984
}
8085

81-
template CheckByteRangeTransitionWithCapture() {
86+
template CheckByteRangeTransitionWithCapture(numCaptureGroups) {
8287
signal input currState;
8388
signal input nextState;
8489
signal input byteStart;
8590
signal input byteEnd;
86-
signal input captureGroupId;
87-
signal input captureGroupStart;
91+
signal input captureGroupId[numCaptureGroups];
92+
signal input captureGroupStart[numCaptureGroups];
8893

8994
signal input inCurrState;
9095
signal input inNextState;
9196
signal input inByte;
92-
signal input inCaptureGroupId;
93-
signal input inCaptureGroupStart;
97+
signal input inCaptureGroupId[numCaptureGroups];
98+
signal input inCaptureGroupStart[numCaptureGroups];
9499

95100
signal output out;
96101

97-
signal isCaptureGroupEqual <== IsEqual()([captureGroupId, inCaptureGroupId]);
98-
signal isCaptureGroupStartEqual <== IsEqual()([captureGroupStart, inCaptureGroupStart]);
102+
component isCaptureGroupEqual = MultiAND(numCaptureGroups);
103+
component isCaptureGroupStartEqual = MultiAND(numCaptureGroups);
104+
for (var i = 0; i < numCaptureGroups; i++) {
105+
isCaptureGroupEqual.in[i] <== IsEqual()([captureGroupId[i], inCaptureGroupId[i]]);
106+
isCaptureGroupStartEqual.in[i] <== IsEqual()([captureGroupStart[i], inCaptureGroupStart[i]]);
107+
}
99108
signal isValidTransition <== CheckByteRangeTransition()(currState, nextState, byteStart, byteEnd, inCurrState, inNextState, inByte);
100109

101-
out <== MultiAND(3)([isValidTransition, isCaptureGroupEqual, isCaptureGroupStartEqual]);
110+
out <== MultiAND(3)([isValidTransition, isCaptureGroupEqual.out, isCaptureGroupStartEqual.out]);
102111
}
103112

104113
template CaptureSubstring(maxBytes, maxSubstringBytes, captureId) {

compiler/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zk-email/zk-regex-compiler",
3-
"version": "2.0.0-alpha.2",
3+
"version": "2.0.0-alpha.3",
44
"description": "A compiler to generate a regex verification circuit in circom from a user-defined regex. Please check [zk-regex](https://github.com/zkemail/zk-regex/tree/main) for the detail.",
55
"repository": {
66
"type": "git",

compiler/src/nfa/codegen/noir.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ pub fn generate_noir_code(
299299
)
300300
);
301301
code.push_str(&format!(" haystack,\n"));
302-
code.push_str(&format!(" capture_group_{}_ids,\n", i));
303-
code.push_str(&format!(" capture_group_{}_starts,\n", i));
302+
code.push_str(&format!(" capture_group_{}_id,\n", i));
303+
code.push_str(&format!(" capture_group_{}_start,\n", i));
304304
code.push_str(&format!(
305305
" capture_group_start_indices[{}] - (match_start as Field),\n",
306306
i - 1

compiler/src/nfa/epsilon.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,31 +64,32 @@ impl NFAGraph {
6464
.insert(actual_target);
6565

6666
// Collect all captures for the new transition: state --byte--> actual_target
67-
68-
// 1. Captures from the epsilon path leading from `state` up to `r_state`
69-
// (before the byte transition at `r_state`).
70-
for &(_original_epsilon_source_state, capture_info) in &closure.captures
67+
let captures_for_this_transition = new_captures[state]
68+
.entry(actual_target)
69+
.or_insert_with(BTreeSet::new);
70+
71+
// 1. Add START events from the epsilon path leading from `state` up to `r_state`
72+
// (before the byte transition at `r_state`).
73+
// `closure` is `closures[state]`.
74+
for &(_original_epsilon_source_state, (group_id, is_start_event)) in
75+
&closure.captures
7176
{
72-
new_captures[state]
73-
.entry(actual_target)
74-
.or_insert_with(BTreeSet::new)
75-
.insert(capture_info);
77+
if is_start_event {
78+
// This is a START event for group_id
79+
captures_for_this_transition.insert((group_id, true));
80+
}
7681
}
7782

78-
// 2. If `actual_target` itself leads to an accept state via an
79-
// epsilon path, and that epsilon path has captures, those captures
80-
// also belong to the new transition `state --byte--> actual_target`.
83+
// 2. Add END events from the epsilon path starting FROM `actual_target`
84+
// (after `byte` is consumed and actual_target is reached).
85+
// `closure_of_actual_target` is `closures[actual_target]`.
8186
let closure_of_actual_target = &closures[actual_target];
82-
if closure_of_actual_target.is_accept {
83-
for &(
84-
_orig_eps_src_from_target_closure,
85-
cap_info_from_target_closure,
86-
) in &closure_of_actual_target.captures
87-
{
88-
new_captures[state]
89-
.entry(actual_target)
90-
.or_insert_with(BTreeSet::new)
91-
.insert(cap_info_from_target_closure);
87+
for &(_orig_eps_src_from_target_closure, (group_id, is_start_event)) in
88+
&closure_of_actual_target.captures
89+
{
90+
if !is_start_event {
91+
// This is an END event for group_id
92+
captures_for_this_transition.insert((group_id, false));
9293
}
9394
}
9495
}

noir/src/utils/transitions.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn check_transition_with_captures<let TABLE_SIZE: u32, let NUM_CAPTURE_GROUP
4949
let key = current_state + haystack_byte as Field * R as Field + next_state * R_SQUARED as Field;
5050

5151
let (is_valid, capture_starts_from_table, capture_participations_from_table) =
52-
unpack_sparse_value(key, table);
52+
unpack_sparse_value::<TABLE_SIZE, NUM_CAPTURE_GROUPS>(key, table);
5353

5454
let mut error_accumulator: Field = 1 - is_valid;
5555
let base_weight: Field = 3;

0 commit comments

Comments
 (0)