diff --git a/circuits/parser.circom b/circuits/parser.circom index b36e2fa..dd96289 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -16,6 +16,9 @@ The key ingredients of `parser` are: `parser` brings in many functions from the `utils` module and `language`. The inclusion of `langauge` allows for this file to (eventually) be generic over a grammar for different applications (e.g., HTTP, YAML, TOML, etc.). + +## Testing +Tests for this module are located in the files: `circuits/test/parser/*.test.ts */ pragma circom 2.1.9; @@ -64,6 +67,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { component readNumber = InRange(8); readNumber.in <== byte; readNumber.range <== [48, 57]; // This is the range where ASCII digits are + // * read in a quote `"` * component readQuote = IsEqual(); readQuote.in <== [byte, Syntax.QUOTE]; //--------------------------------------------------------------------------------------------// @@ -164,8 +168,8 @@ template StateToMask(n) { signal readNumberNotParsingNumber <== (1 - parsing_number) * readNumber; signal notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber <== (1 - parsing_string) * (parsingNumberReadDelimeter + readNumberNotParsingNumber); // 10 above ^^^^^^^^^^^^^^^^^ 4 above ^^^^^^^^^^^^^^^^^^ - signal temp <== parsing_number * (1 - readNumber) ; - signal parsingNumberNotReadNumberNotReadDelimeter <== temp * (1-readDelimeter); + signal parsingNumberNotReadNumber <== parsing_number * (1 - readNumber) ; + signal parsingNumberNotReadNumberNotReadDelimeter <== parsingNumberNotReadNumber * (1-readDelimeter); out[2] <== notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber + parsingNumberNotReadNumberNotReadDelimeter; // Sorry about the long names, but they hopefully read clearly! } @@ -193,6 +197,9 @@ template GetTopOfStack(n) { } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the InRange work (could be changed) +/* + +*/ template RewriteStack(n) { assert(n < 2**8); signal input stack[n][2]; @@ -224,7 +231,6 @@ template RewriteStack(n) { //--------------------------------------------------------------------------------------------// // * composite signals * - signal readEndChar <== readEndBrace + readEndBracket; signal readCommaInArray <== readComma * inArray.out; signal readCommaNotInArray <== readComma * (1 - inArray.out); //--------------------------------------------------------------------------------------------// @@ -250,7 +256,7 @@ template RewriteStack(n) { signal second_index_clear[n]; for(var i = 0; i < n; i++) { next_stack[i][0] <== stack[i][0] + indicator[i].out * stack_change_value[0]; - second_index_clear[i] <== stack[i][1] * readEndChar; + second_index_clear[i] <== stack[i][1] * (readEndBrace + readEndBracket); // Checking if we read some end char next_stack[i][1] <== stack[i][1] + indicator[i].out * (stack_change_value[1] - second_index_clear[i]); } //--------------------------------------------------------------------------------------------// diff --git a/circuits/utils.circom b/circuits/utils.circom index 1112eba..ed0f485 100644 --- a/circuits/utils.circom +++ b/circuits/utils.circom @@ -1,12 +1,42 @@ +/* +# `utils` +This module consists of helper templates for convencience. +It mostly extends the `bitify` and `comparators` modules from Circomlib. + +## Layout +The key ingredients of `utils` are: + - `ASCII`: Verify if a an input array contains valid ASCII values (e.g., u8 vals). + - `IsEqualArray`: Check if two arrays are equal component by component. + - `Contains`: Check if an element is contained in a given array. + - `ArrayAdd`: Add two arrays together component by component. + - `ArrayMul`: Multiply two arrays together component by component. + - `GenericArrayAdd`: Add together an arbitrary amount of arrays. + - `ScalarArrayMul`: Multiply each array element by a scalar value. + - `InRange`: Check if a given number is in a given range. + - `Switch`: Return a scalar value given a specific case. + - `SwitchArray`: Return an array given a specific case. + + +## Testing +Tests for this module are located in the file: `./test/utils/utils.test.ts` +*/ + pragma circom 2.1.9; include "circomlib/circuits/bitify.circom"; include "circomlib/circuits/comparators.circom"; + + /* -All tests for this file are located in: `./test/utils/utils.test.ts` -*/ +This template passes if a given array contains only valid ASCII values (e.g., u8 vals). + +# Params: + - `n`: the length of the array +# Inputs: + - `in[n]`: array to check +*/ template ASCII(n) { signal input in[n]; @@ -18,12 +48,16 @@ template ASCII(n) { } /* -This function is an indicator for two equal array inputs. +This template is an indicator for two equal array inputs. + +# Params: + - `n`: the length of arrays to compare # Inputs: -- `n`: the length of arrays to compare -- `in[2][n]`: two arrays of `n` numbers -- `out`: either `0` or `1` + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out`: either `0` or `1` - `1` if `in[0]` is equal to `in[1]` as arrays (i.e., component by component) - `0` otherwise */ @@ -50,20 +84,24 @@ template IsEqualArray(n) { // TODO: There should be a way to have the below assertion come from the field itself. /* -This function is an indicator for if an array contains an element. +This template is an indicator for if an array contains an element. + +# Params: + - `n`: the size of the array to search through # Inputs: -- `n`: the size of the array to search through -- `in`: a number -- `array[n]`: the array we want to search through -- `out`: either `0` or `1` + - `in`: a number + - `array[n]`: the array we want to search through + +# Outputs: + - `out`: either `0` or `1` - `1` if `in` is found inside `array` - `0` otherwise */ template Contains(n) { assert(n > 0); /* - If `n = p` for this large `p`, then it could be that this function + If `n = p` for this large `p`, then it could be that this template returns the wrong value if every element in `array` was equal to `in`. This is EXTREMELY unlikely and iterating this high is impossible anyway. But it is better to check than miss something, so we bound it by `2**254` for now. @@ -89,6 +127,18 @@ template Contains(n) { out <== 1 - someEqual.out; } +/* +This template adds two arrays component by component. + +# Params: + - `n`: the length of arrays to compare + +# Inputs: + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out[n]`: the array sum value +*/ template ArrayAdd(n) { signal input lhs[n]; signal input rhs[n]; @@ -99,6 +149,18 @@ template ArrayAdd(n) { } } +/* +This template multiplies two arrays component by component. + +# Params: + - `n`: the length of arrays to compare + +# Inputs: + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out[n]`: the array multiplication value +*/ template ArrayMul(n) { signal input lhs[n]; signal input rhs[n]; @@ -109,6 +171,19 @@ template ArrayMul(n) { } } +/* +This template multiplies two arrays component by component. + +# Params: + - `m`: the length of the arrays to add + - `n`: the number of arrays to add + +# Inputs: + - `arrays[m][n]`: `n` arrays of `m` numbers + +# Outputs: + - `out[m]`: the sum of all the arrays +*/ template GenericArrayAdd(m,n) { signal input arrays[n][m]; signal output out[m]; @@ -122,6 +197,18 @@ template GenericArrayAdd(m,n) { out <== accum; } +/* +This template multiplies each component of an array by a scalar value. + +# Params: + - `n`: the length of the array + +# Inputs: + - `array[n]`: an array of `n` numbers + +# Outputs: + - `out[n]`: the scalar multiplied array +*/ template ScalarArrayMul(n) { signal input array[n]; signal input scalar; @@ -132,6 +219,20 @@ template ScalarArrayMul(n) { } } +/* +This template checks if a given `n`-bit value is contained in a range of `n`-bit values + +# Params: + - `n`: the number of bits to use + +# Inputs: + - `range[2]`: the lower and upper bound of the array, respectively + +# Outputs: + - `out`: either `0` or `1` + - `1` if `in` is within the range + - `0` otherwise +*/ template InRange(n) { signal input in; signal input range[2]; @@ -147,44 +248,42 @@ template InRange(n) { } /* -This function is creates an exhaustive switch statement from `0` up to `n`. +This template is creates an exhaustive switch statement from a list of branch values. +# Params: + - `n`: the number of switch cases # Inputs: -- `m`: the number of switch cases -- `n`: the output array length -- `case`: which case of the switch to select -- `branches[m]`: the values that enable taking different branches in the switch + - `case`: which case of the switch to select + - `branches[n]`: the values that enable taking different branches in the switch (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) -- `vals[m][n]`: the value that is emitted for a given switch case + - `vals[n]`: the value that is emitted for a given switch case (e.g., `val[i]` array is emitted on `case == `branch[i]`) # Outputs -- `match`: is set to `0` if `case` does not match on any of `branches` -- `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) + - `match`: is set to `0` if `case` does not match on any of `branches` + - `out[n]`: the selected output value if one of `branches` is selected (will be `0` otherwise) + ^^^^^^ BEWARE OF THIS FACT ABOVE! */ -template SwitchArray(m, n) { - assert(m > 0); +template Switch(n) { assert(n > 0); signal input case; - signal input branches[m]; - signal input vals[m][n]; + signal input branches[n]; + signal input vals[n]; signal output match; - signal output out[n]; + signal output out; // Verify that the `case` is in the possible set of branches - component indicator[m]; - component matchChecker = Contains(m); - signal component_out[m][n]; - var sum[n]; - for(var i = 0; i < m; i++) { + component indicator[n]; + component matchChecker = Contains(n); + signal temp_val[n]; + var sum; + for(var i = 0; i < n; i++) { indicator[i] = IsZero(); indicator[i].in <== case - branches[i]; matchChecker.array[i] <== 1 - indicator[i].out; - for(var j = 0; j < n; j++) { - component_out[i][j] <== indicator[i].out * vals[i][j]; - sum[j] += component_out[i][j]; - } + temp_val[i] <== indicator[i].out * vals[i]; + sum += temp_val[i]; } matchChecker.in <== 0; match <== matchChecker.out; @@ -192,29 +291,52 @@ template SwitchArray(m, n) { out <== sum; } -template Switch(n) { +/* +This template is creates an exhaustive switch statement from a list of branch values. +# Params: + - `m`: the number of switch cases + - `n`: the output array length + +# Inputs: + + - `case`: which case of the switch to select + - `branches[m]`: the values that enable taking different branches in the switch + (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) + - `vals[m][n]`: the value that is emitted for a given switch case + (e.g., `val[i]` array is emitted on `case == `branch[i]`) + +# Outputs + - `match`: is set to `0` if `case` does not match on any of `branches` + - `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) + ^^^^^^ BEWARE OF THIS FACT ABOVE! +*/ +template SwitchArray(m, n) { + assert(m > 0); assert(n > 0); signal input case; - signal input branches[n]; - signal input vals[n]; + signal input branches[m]; + signal input vals[m][n]; signal output match; - signal output out; + signal output out[n]; // Verify that the `case` is in the possible set of branches - component indicator[n]; - component matchChecker = Contains(n); - signal temp_val[n]; - var sum; - for(var i = 0; i < n; i++) { + component indicator[m]; + component matchChecker = Contains(m); + signal component_out[m][n]; + var sum[n]; + for(var i = 0; i < m; i++) { indicator[i] = IsZero(); indicator[i].in <== case - branches[i]; matchChecker.array[i] <== 1 - indicator[i].out; - temp_val[i] <== indicator[i].out * vals[i]; - sum += temp_val[i]; + for(var j = 0; j < n; j++) { + component_out[i][j] <== indicator[i].out * vals[i][j]; + sum[j] += component_out[i][j]; + } } matchChecker.in <== 0; match <== matchChecker.out; out <== sum; -} \ No newline at end of file +} +