Skip to content

Commit f23f054

Browse files
committed
Added resolver for arrays in the component main
1 parent f4fb24c commit f23f054

File tree

7 files changed

+171
-54
lines changed

7 files changed

+171
-54
lines changed

src/utils/ExpressionHelper.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ class ExpressionVisitor extends ExtendedCircomVisitor<CircomValueType | null> {
121121
ctx: PIdentifierStatementContext,
122122
): CircomValueType | null => {
123123
if (ctx.identifierStatement().idetifierAccess_list().length == 0) {
124-
const variableName =
124+
const variableValue =
125125
this.variableContext[ctx.identifierStatement().ID().getText()];
126126

127-
if (variableName === undefined) {
127+
if (variableValue === undefined) {
128128
this.addError(
129129
`Variable ${ctx.identifierStatement().ID().getText()} is not defined`,
130130
ctx.identifierStatement(),
@@ -133,14 +133,35 @@ class ExpressionVisitor extends ExtendedCircomVisitor<CircomValueType | null> {
133133
return null;
134134
}
135135

136-
return variableName;
136+
return variableValue;
137137
}
138138

139-
this.addError(
140-
"IdentifierStatement is not supported with access references",
141-
ctx,
142-
);
143-
return null;
139+
const reference = ctx
140+
.identifierStatement()
141+
.idetifierAccess_list()
142+
.map((access) => access.getText())
143+
.join("");
144+
145+
if (reference.indexOf(".") !== -1) {
146+
this.addError(
147+
"IdentifierStatement is not supported with access references that are not arrays",
148+
ctx,
149+
);
150+
return null;
151+
}
152+
153+
const variableName = ctx.identifierStatement().ID().getText() + reference;
154+
155+
if (this.variableContext[variableName] === undefined) {
156+
this.addError(
157+
`Variable ${variableName} is not defined`,
158+
ctx.identifierStatement(),
159+
);
160+
161+
return null;
162+
}
163+
164+
return this.variableContext[variableName];
144165
};
145166

146167
visitPUnderscore = (_ctx: PUnderscoreContext): CircomValueType | null => {

src/utils/common.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,44 @@ export function parseSimpleIdentifierList(
1717
return result;
1818
}
1919

20+
export function buildVariableContext(
21+
names: string[],
22+
values: any[],
23+
): VariableContext {
24+
if (names.length !== values.length) {
25+
throw new Error("Names and values must have the same length");
26+
}
27+
28+
const context: VariableContext = {};
29+
30+
for (let i = 0; i < names.length; i++) {
31+
const bindContext = bindVariableContext(
32+
names[i],
33+
getArrayDimensions(values[i]),
34+
values[i],
35+
);
36+
for (const key in bindContext) {
37+
if (bindContext[key] !== null) {
38+
context[key] = bindContext[key];
39+
}
40+
}
41+
}
42+
43+
return context;
44+
}
45+
46+
export function getArrayDimensions(value: any): number[] {
47+
if (Array.isArray(value)) {
48+
return [value.length, ...getArrayDimensions(value[0])];
49+
}
50+
51+
return [];
52+
}
53+
2054
export function bindVariableContext(
2155
variableName: string,
2256
dimensions: number[],
23-
values: any[],
57+
values: any,
2458
): VariableContextWithNull {
2559
const context: VariableContextWithNull = {};
2660

@@ -52,7 +86,7 @@ function parseVariable(value: any, reference: string): bigint {
5286

5387
function getReferenceValueInternal(value: any, reference: number[]): bigint {
5488
if (reference.length === 0) {
55-
return value;
89+
return BigInt(value);
5690
}
5791

5892
return getReferenceValueInternal(value[reference[0]], reference.slice(1));

test/circom-template-inputs-visitor.test.ts

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from "chai";
2-
import { getCircomParser, VariableContext } from "../src";
2+
import { buildVariableContext, getCircomParser, VariableContext } from "../src";
33

4-
import { CircomFileData, Templates } from "./mocks/types";
4+
import { CircomFileData } from "./mocks/types";
55
import { CircomFilesVisitor } from "./mocks/CircomFilesVisitor";
66
import { CircomTemplateInputsVisitor } from "./mocks/CircomTemplateInputsVisitor";
77

@@ -118,17 +118,55 @@ describe("Circom Template Inputs Visitor", () => {
118118
expect(visitor.templateInputs.out.dimension).to.deep.equal([]);
119119
});
120120

121-
it.only("should analyse the ComplexMainComponent.circom circuit", () => {
121+
it("should analyse the ComplexMainComponent.circom circuit", () => {
122122
const data = getData("ComplexMainComponent.circom");
123123

124-
// const visitor = new CircomTemplateInputsVisitor(
125-
// "MainComponent.circom",
126-
// data.,
127-
// {},
128-
// );
129-
//
130-
// visitor.startParse();
131-
//
132-
// console.log(visitor.errors);
124+
const visitor = new CircomTemplateInputsVisitor(
125+
"ComplexMainComponent.circom",
126+
data.templates[data.mainComponentInfo.templateName!].context,
127+
buildVariableContext(
128+
data.templates[data.mainComponentInfo.templateName!].parameters,
129+
data.mainComponentInfo.parameters,
130+
),
131+
);
132+
133+
visitor.startParse();
134+
135+
expect(visitor.errors.length).to.equal(0);
136+
137+
expect(visitor.templateInputs.in1.type).to.equal("input");
138+
expect(visitor.templateInputs.in1.dimension).to.deep.equal([]);
139+
140+
expect(visitor.templateInputs.in2.type).to.equal("input");
141+
expect(visitor.templateInputs.in2.dimension).to.deep.equal([3, 600]);
142+
143+
expect(visitor.templateInputs.out.type).to.equal("output");
144+
expect(visitor.templateInputs.out.dimension).to.deep.equal([]);
145+
});
146+
147+
it("should analyse the AnotherMainComponent.circom circuit", () => {
148+
const data = getData("AnotherMainComponent.circom");
149+
150+
const visitor = new CircomTemplateInputsVisitor(
151+
"AnotherMainComponent.circom",
152+
data.templates[data.mainComponentInfo.templateName!].context,
153+
buildVariableContext(
154+
data.templates[data.mainComponentInfo.templateName!].parameters,
155+
data.mainComponentInfo.parameters,
156+
),
157+
);
158+
159+
visitor.startParse();
160+
161+
expect(visitor.errors.length).to.equal(0);
162+
163+
expect(visitor.templateInputs.in1.type).to.equal("input");
164+
expect(visitor.templateInputs.in1.dimension).to.deep.equal([]);
165+
166+
expect(visitor.templateInputs.in2.type).to.equal("input");
167+
expect(visitor.templateInputs.in2.dimension).to.deep.equal([15, 10]);
168+
169+
expect(visitor.templateInputs.out.type).to.equal("output");
170+
expect(visitor.templateInputs.out.dimension).to.deep.equal([]);
133171
});
134172
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
pragma circom 2.1.6;
2+
3+
template SomeCircuit(p1){
4+
signal input in1;
5+
signal input in2[p1[1] * p1[0]][p1[3] * p1[2]];
6+
7+
signal output out <== in1 * in2[0][0];
8+
}
9+
10+
component main {public [in1]} = SomeCircuit([5, 3, 2, 5]);

test/data/ComplexMainComponent.circom

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ pragma circom 2.1.6;
22

33
template C(p1, p2){
44
signal input in1;
5-
signal input in2[3][p1[1][0][0][0][0][0][0]];
5+
signal input in2[3][p1[1][0][0][0][0][0][0] * p2[0][0][0]];
66

77
signal output out <== in1 * in2[0][0] * p2[0][1][0];
88
}

test/mocks/CircomTemplateInputsVisitor.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
IfRegularElseWithFollowUpIfContext,
1010
IfWithFollowUpIfContext,
1111
ParserErrorItem,
12-
parseSimpleIdentifierList,
1312
PIdentifierStatementContext,
1413
PUnderscoreContext,
1514
SignalDeclarationContext,
@@ -50,8 +49,6 @@ export class CircomTemplateInputsVisitor extends CircomVisitor<void> {
5049
this.templateInputs = {};
5150

5251
this._vars = parameterValues;
53-
54-
this._validateVariableContext();
5552
}
5653

5754
startParse = () => {
@@ -587,30 +584,6 @@ export class CircomTemplateInputsVisitor extends CircomVisitor<void> {
587584
});
588585
};
589586

590-
private _validateVariableContext() {
591-
const templateParameters = parseSimpleIdentifierList(
592-
this.templateContext.simpleIdentifierList(),
593-
);
594-
595-
for (const parameter of templateParameters) {
596-
if (
597-
this._vars[parameter] === undefined ||
598-
this._vars[parameter] === null
599-
) {
600-
this.errors.push({
601-
type: ErrorType.MissingTemplateParameterValue,
602-
context: this.templateContext,
603-
fileIdentifier: this.templateContext.ID().getText(),
604-
message: `Missing value for parameter ${parameter} in template ${this.templateContext.ID().getText()}`,
605-
});
606-
607-
continue;
608-
}
609-
610-
this._declaredVariables[parameter] = true;
611-
}
612-
}
613-
614587
private _resolveIdentifier(
615588
ctx: IdentifierContext,
616589
variableContext: VariableContext = {},

test/utils.ts

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,51 @@
1-
import { bindVariableContext, resolveDimensions } from "../src";
1+
import { expect } from "chai";
2+
3+
import { bindVariableContext } from "../src";
24

35
describe("utils", () => {
46
describe("resolve dimensions", () => {
5-
it.only("should resolve the case: [1]", () => {
6-
console.log(bindVariableContext("a", [1, 2], [[3, 5]]));
7-
console.log(bindVariableContext("a", [1, 2], []));
7+
it("should resolve the case: [1, 2], when value: [[3, 5]]", () => {
8+
expect(bindVariableContext("a", [1, 2], [[3, 5]])).to.deep.equal({
9+
"a[0][0]": 3n,
10+
"a[0][1]": 5n,
11+
});
12+
});
13+
14+
it("should resolve the case: [1, 2], when value: []", () => {
15+
expect(bindVariableContext("a", [1, 2], [])).to.deep.equal({
16+
"a[0][0]": null,
17+
"a[0][1]": null,
18+
});
19+
});
20+
21+
it("should resolve the case: [], when value: 1", () => {
22+
expect(bindVariableContext("a", [], 1)).to.deep.equal({
23+
a: 1n,
24+
});
25+
});
26+
27+
it("should resolve the case: [1], when value: 1", () => {
28+
expect(bindVariableContext("a", [1], 1)).to.deep.equal({
29+
"a[0]": null,
30+
});
31+
});
32+
33+
it("should resolve the case: [2, 2], when value: [[1, 2], [3, 4]]", () => {
34+
expect(
35+
bindVariableContext(
36+
"a",
37+
[2, 2],
38+
[
39+
[1, 2],
40+
[3, 4],
41+
],
42+
),
43+
).to.deep.equal({
44+
"a[0][0]": 1n,
45+
"a[0][1]": 2n,
46+
"a[1][0]": 3n,
47+
"a[1][1]": 4n,
48+
});
849
});
950
});
1051
});

0 commit comments

Comments
 (0)