Skip to content

Commit

Permalink
good save state
Browse files Browse the repository at this point in the history
  • Loading branch information
Autoparallel committed Aug 14, 2024
1 parent e553e04 commit a79fd4e
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 31 deletions.
2 changes: 1 addition & 1 deletion circuits.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"template": "Extract",
"params": [
12,
2
3
]
},
"value_array": {
Expand Down
8 changes: 4 additions & 4 deletions circuits/language.circom
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ template Syntax() {

template Command() {
// STATE = [pushpop, stack_val, parsing_string, parsing_number]
signal output NOTHING[4] <== [0, 0, 0, 0 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key
signal output NOTHING[4] <== [0, 0, 0, -1 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key
signal output START_BRACE[4] <== [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{`
signal output END_BRACE[4] <== [-1, 1, 0, 0 ]; // Command returned by switch if we hit a end brace `}`
signal output END_BRACE[4] <== [-1, 1, 0, -1 ]; // Command returned by switch if we hit a end brace `}`
signal output START_BRACKET[4] <== [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket)
signal output END_BRACKET[4] <== [-1, 2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]`
signal output END_BRACKET[4] <== [-1, 2, 0, -1 ]; // Command returned by switch if we hit a start bracket `]`
signal output QUOTE[4] <== [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"`
signal output COLON[4] <== [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:`
signal output COMMA[4] <== [-1, 4, 0, -1 ]; // Command returned by switch if we hit a comma `,`
signal output NUMBER[4] <== [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57)
signal output NUMBER[4] <== [0, 256, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57)
}
72 changes: 46 additions & 26 deletions circuits/parser.circom
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,16 @@ template StateUpdate(MAX_STACK_HEIGHT) {
component numeral_range_check = InRange(8);
numeral_range_check.in <== byte;
numeral_range_check.range <== [48, 57]; // ASCII NUMERALS
matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; // IF (NOT is_number) THEN byte ELSE 256
log("isNumeral:", numeral_range_check.out);
signal IS_NUMBER <== numeral_range_check.out * Syntax.NUMBER;
matcher.case <== (1 - numeral_range_check.out) * byte + IS_NUMBER; // IF (NOT is_number) THEN byte ELSE 256

// * get the instruction mask based on current state *
component mask = StateToMask(MAX_STACK_HEIGHT);
mask.in <== parsing_state;
// mask.in <== parsing_state;
mask.in <== [matcher.out[0],matcher.out[1],parsing_string,parsing_number]; // TODO: This is awkward. Things need to be rewritten


// * multiply the mask array elementwise with the instruction array *
component mulMaskAndOut = ArrayMul(4);
mulMaskAndOut.lhs <== mask.out;
Expand All @@ -51,6 +57,7 @@ template StateUpdate(MAX_STACK_HEIGHT) {
component addToState = ArrayAdd(4);
addToState.lhs <== parsing_state;
addToState.rhs <== mulMaskAndOut.out;

// * set the new state *
component newStack = RewriteStack(MAX_STACK_HEIGHT);
newStack.pointer <== pointer;
Expand All @@ -61,26 +68,12 @@ template StateUpdate(MAX_STACK_HEIGHT) {
next_stack <== newStack.next_stack;
next_parsing_string <== addToState.out[2];
next_parsing_number <== addToState.out[3];
//--------------------------------------------------------------------------------------------//

//--------------------------------------------------------------------------------------------//
// // DEBUGGING: internal state
// for(var i = 0; i<7; i++) {
// log("------------------------------------------");
// log(">>>> parsing_state[",i,"]: ", parsing_state[i]);
// log(">>>> mask[",i,"] : ", mask.out[i]);
// log(">>>> command[",i,"] : ", matcher.out[i]);
// log(">>>> addToState[",i,"] : ", addToState.out[i]);
// for(var i = 0; i < 4; i++) {
// log("matcher.out[",i,"]: ", matcher.out[i]);
// log("mask.out[",i,"]: ", mask.out[i]);
// log("mulMaskAndOut[",i,"]: ", mulMaskAndOut.out[i]);
// }
// Debugging
// log("next_pointer ", "= ", next_pointer);
// for(var i = 0; i<4; i++) {
// log("next_stack[", i,"] ", "= ", next_stack[i]);
// }
// log("next_parsing_string", "= ", next_parsing_string);
// log("next_parsing_number", "= ", next_parsing_number);
// log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
//--------------------------------------------------------------------------------------------//

//--------------------------------------------------------------------------------------------//
//-Constraints--------------------------------------------------------------------------------//
Expand All @@ -98,6 +91,7 @@ template StateUpdate(MAX_STACK_HEIGHT) {
}

template StateToMask(n) {
// TODO: Probably need to assert things are bits where necessary.
signal input in[4];
signal output out[4];

Expand All @@ -106,18 +100,44 @@ template StateToMask(n) {
signal parsing_string <== in[2];
signal parsing_number <== in[3];

// `pushpop` can change: IF NOT `parsing_string`
out[0] <== (1 - parsing_string);
// `pushpop` can change: IF NOT `parsing_string`
out[0] <== (1 - parsing_string) * (1 - parsing_number);

// `stack_val`: IF NOT `parsing_string` OR
// TODO: `parsing_array`
out[1] <== (1 - parsing_string);
// `stack_val`can change: IF NOT `parsing_string`
out[1] <== (1 - parsing_string) * (1- parsing_number);

// `parsing_string` can change:
out[2] <== 1 - 2 * parsing_string;

// `parsing_number` can change:
out[3] <== (1 - parsing_string) * (- 2 * parsing_number);
component isDelimeter = InRange(8);
isDelimeter.in <== stack_val;
isDelimeter.range[0] <== 1;
isDelimeter.range[1] <== 4;
component isNumber = IsEqual();
isNumber.in <== [stack_val, 256];
component isParsingString = IsEqual();
isParsingString.in[0] <== parsing_string;
isParsingString.in[1] <== 1;
component isParsingNumber = IsEqual();
isParsingNumber.in[0] <== parsing_number;
isParsingNumber.in[1] <== 1;
component toParseNumber = Switch(16);
// TODO: Could combine this into something that returns arrays so that we can set the mask more easily.
toParseNumber.branches <== [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
toParseNumber.vals <== [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0];
component stateToNum = Bits2Num(4);
stateToNum.in <== [isParsingString.out, isParsingNumber.out, isNumber.out, isDelimeter.out];
// 1 2 4 8
toParseNumber.case <== stateToNum.out;
log("isNumber: ", isNumber.out);
log("isParsingString: ", isParsingString.out);
log("isParsingNumber: ", isParsingNumber.out);
log("isDelimeter: ", isDelimeter.out);
log("stateToNum: ", stateToNum.out);
log("toParseNumber: ", toParseNumber.out);

out[3] <== toParseNumber.out;
}

template GetTopOfStack(n) {
Expand Down
91 changes: 91 additions & 0 deletions circuits/test/parser/values.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { circomkit, WitnessTester } from "../common";
import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.';

describe("StateUpdate :: Values", () => {
let circuit: WitnessTester<
["byte", "pointer", "stack", "parsing_string", "parsing_number"],
["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"]
>;
before(async () => {
circuit = await circomkit.WitnessTester(`GetTopOfStack`, {
file: "circuits/parser",
template: "StateUpdate",
params: [4],
});
console.log("#constraints:", await circuit.getConstraintCount());
});
function generatePassCase(input: any, expected: any, desc: string) {
const description = Object.entries(input)
.map(([key, value]) => `${key} = ${value}`)
.join(", ");

it(`(valid) witness: ${description}\n${desc}`, async () => {
await circuit.expectPass(input, expected);
});
}

function generateFailCase(input: any, desc: string) {
const description = Object.entries(input)
.map(([key, value]) => `${key} = ${value}`)
.join(", ");

it(`(invalid) witness: ${description}\n${desc}`, async () => {
await circuit.expectFail(input);
});
}


//-TEST_1----------------------------------------------------------//
// idea: Read a number value after a key in an object.
// state: pointer == 2, stack == [1,3,0,0]
// read: `0`
// expect: pointer --> 2
// stack --> [1,3,0,0]
// parsing_number --> 1
let read_number = { ...INITIAL_IN };
read_number.pointer = 2;
read_number.stack = [1, 3, 0, 0];
read_number.byte = Numbers.ZERO;
let read_number_out = { ...INITIAL_OUT };
read_number_out.next_pointer = 2;
read_number_out.next_stack = [1, 3, 0, 0];
read_number_out.next_parsing_number = 1;
generatePassCase(read_number, read_number_out, ">>>> `0` read");

//-TEST_2----------------------------------------------------------//
// idea: Inside a number value after a key in an object.
// state: pointer == 2, stack == [1,3,0,0], parsing_number == 1
// read: `,`
// expect: pointer --> 2
// stack --> [1,3,0,0]
// parsing_number --> 0
let inside_number = { ...INITIAL_IN };
inside_number.pointer = 2;
inside_number.stack = [1, 3, 0, 0];
inside_number.parsing_number = 1;
inside_number.byte = Delimiters.COMMA;
let inside_number_out = { ...INITIAL_OUT };
inside_number_out.next_pointer = 2;
inside_number_out.next_stack = [1, 3, 0, 0];
generatePassCase(inside_number, inside_number_out, ">>>> `,` read");

// TODO: Note that reading a space while reading a number will not throw an error!

//-TEST_2----------------------------------------------------------//
// idea: Inside a number value after a key in an object.
// state: pointer == 2, stack == [1,3,0,0], parsing_number == 1
// read: `1`
// expect: pointer --> 2
// stack --> [1,3,0,0]
// parsing_number --> 0
let inside_number_continue = { ...INITIAL_IN };
inside_number_continue.pointer = 2;
inside_number_continue.stack = [1, 3, 0, 0];
inside_number_continue.parsing_number = 1;
inside_number_continue.byte = Numbers.ONE;
let inside_number_continue_out = { ...INITIAL_OUT };
inside_number_continue_out.next_pointer = 2;
inside_number_continue_out.next_stack = [1, 3, 0, 0];
inside_number_continue_out.next_parsing_number = 1;
generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read");
});

0 comments on commit a79fd4e

Please sign in to comment.