Skip to content

Commit

Permalink
feat: basic parser with escape
Browse files Browse the repository at this point in the history
  • Loading branch information
Autoparallel committed Dec 19, 2024
1 parent 7457da0 commit 73a5031
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 7 deletions.
4 changes: 2 additions & 2 deletions circuits/json/language.circom
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ template Syntax() {
// signal output SPACE <== 32;
//-Escape-------------------------------------------------------------------------------------//
// - ASCII char: `\`
// signal output ESCAPE <== 92;
signal output ESCAPE <== 92;
//-Number_Remapping---------------------------------------------------------------------------//
signal output NUMBER_START <== 48;
signal output NUMBER_END <== 57;

}

template Command() {
// STATE = [read_write_value, parsing_string, parsing_number]
// STATE = [read_write_value, parsing_string, parsing_number, escape]
signal output START_BRACE[3] <== [1, 0, 0 ]; // Command returned by switch if we hit a start brace `{`
signal output END_BRACE[3] <== [-1, 0, -1 ]; // Command returned by switch if we hit a end brace `}`
signal output START_BRACKET[3] <== [2, 0, 0 ]; // Command returned by switch if we hit a start bracket `[`
Expand Down
18 changes: 15 additions & 3 deletions circuits/json/machine.circom
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ template StateUpdate(MAX_STACK_HEIGHT) {
signal input stack[MAX_STACK_HEIGHT][2];
signal input parsing_string;
signal input parsing_number;
signal input escaped;

signal output next_stack[MAX_STACK_HEIGHT][2];
signal output next_parsing_string;
signal output next_parsing_number;
signal output next_escaped;

component Command = Command();
component Syntax = Syntax();
Expand Down Expand Up @@ -88,6 +90,10 @@ template StateUpdate(MAX_STACK_HEIGHT) {
// * read in a quote `"` *
component readQuote = IsEqual();
readQuote.in <== [byte, Syntax.QUOTE];
// * read in a escape `\` *
component readEscape = IsEqual();
readEscape.in <== [byte, Syntax.ESCAPE];

component readOther = IsZero();
readOther.in <== readDelimeter + readNumber.out + readQuote.out;
//--------------------------------------------------------------------------------------------//
Expand Down Expand Up @@ -145,9 +151,15 @@ template StateUpdate(MAX_STACK_HEIGHT) {
newStack.readColon <== readColon.out;
newStack.readComma <== readComma.out;
// * set all the next state of the parser *
next_stack <== newStack.next_stack;
next_parsing_string <== parsing_string + mulMaskAndOut.out[1];
next_parsing_number <== parsing_number + mulMaskAndOut.out[2];
// b * (y - x) + x --> Simple way of doing a switch with boolean b
for(var i = 0 ; i < MAX_STACK_HEIGHT ; i++) {
next_stack[i][0] <== escaped * (stack[i][0] - newStack.next_stack[i][0]) + newStack.next_stack[i][0];
next_stack[i][1] <== escaped * (stack[i][1] - newStack.next_stack[i][1]) + newStack.next_stack[i][1];
}
next_parsing_string <== escaped * (parsing_string - (parsing_string + mulMaskAndOut.out[1])) + (parsing_string + mulMaskAndOut.out[1]);
next_parsing_number <== escaped * (parsing_number - (parsing_number + mulMaskAndOut.out[2])) + (parsing_number + mulMaskAndOut.out[2]);
// Toggle escaped if read
next_escaped <== readEscape.out * (1 - escaped);
//--------------------------------------------------------------------------------------------//
}

Expand Down
22 changes: 22 additions & 0 deletions circuits/json/parser.circom
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,41 @@ template Parser(DATA_BYTES, MAX_STACK_HEIGHT) {
}
State[0].parsing_string <== 0;
State[0].parsing_number <== 0;
State[0].escaped <== 0;

// Debugging
for(var i = 0; i<MAX_STACK_HEIGHT; i++) {
log("State[", 0, "].next_stack[", i,"] = [",State[0].next_stack[i][0], "][", State[0].next_stack[i][1],"]" );
}
log("State[", 0, "].next_parsing_string =", State[0].next_parsing_string);
log("State[", 0, "].next_parsing_number =", State[0].next_parsing_number);
log("State[", 0, "].next_escaped =", State[0].next_escaped);
log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) {
State[data_idx] = StateUpdate(MAX_STACK_HEIGHT);
State[data_idx].byte <== data[data_idx];
State[data_idx].stack <== State[data_idx - 1].next_stack;
State[data_idx].parsing_string <== State[data_idx - 1].next_parsing_string;
State[data_idx].parsing_number <== State[data_idx - 1].next_parsing_number;
State[data_idx].escaped <== State[data_idx - 1].next_escaped;

// Debugging
for(var i = 0; i<MAX_STACK_HEIGHT; i++) {
log("State[", data_idx, "].next_stack[", i,"] = [",State[data_idx].next_stack[i][0], "][", State[data_idx].next_stack[i][1],"]" );
}
log("State[", data_idx, "].next_parsing_string =", State[data_idx].next_parsing_string);
log("State[", data_idx, "].next_parsing_number =", State[data_idx].next_parsing_number);
log("State[", data_idx, "].next_escaped =", State[data_idx].next_escaped);
log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}

// Constrain to have valid JSON
State[DATA_BYTES - 1].next_parsing_string === 0;
State[DATA_BYTES - 1].next_parsing_number === 0;
State[DATA_BYTES - 1].next_escaped === 0;
for(var i = 0; i < MAX_STACK_HEIGHT; i++) {
State[DATA_BYTES - 1].next_stack[i] === [0,0];
}

}
19 changes: 17 additions & 2 deletions circuits/test/json/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe("JSON Parser", () => {

it(`array only input`, async () => {
let filename = "array_only";
let [input, keyUnicode, output] = readJSONInputFile(`${filename}.json`, [0]);
let [input, keyUnicode, output] = readJSONInputFile(`${filename}.json`, []);

circuit = await circomkit.WitnessTester(`Parser`, {
file: "json/parser",
Expand All @@ -20,7 +20,22 @@ describe("JSON Parser", () => {

it(`object input`, async () => {
let filename = "value_object";
let [input, keyUnicode, output] = readJSONInputFile(`${filename}.json`, ["a"]);
let [input, keyUnicode, output] = readJSONInputFile(`${filename}.json`, []);

circuit = await circomkit.WitnessTester(`Parser`, {
file: "json/parser",
template: "Parser",
params: [input.length, 3],
});

await circuit.expectPass({
data: input
});
});

it(`string_escape input`, async () => {
let filename = "string_escape";
let [input, keyUnicode, output] = readJSONInputFile(`${filename}.json`, []);

circuit = await circomkit.WitnessTester(`Parser`, {
file: "json/parser",
Expand Down

0 comments on commit 73a5031

Please sign in to comment.