From cdf9c13622fc7a950f29ad4d7cc473eac2bfaddf Mon Sep 17 00:00:00 2001 From: Kyryl R Date: Thu, 31 Oct 2024 16:50:37 +0200 Subject: [PATCH 1/5] Added support for new parser --- src/cache/BaseCache.ts | 6 +- src/cache/schemas/compile-schemas.ts | 12 +- src/core/dependencies/CircomFilesResolver.ts | 2 +- .../dependencies/parser/CircomFilesParser.ts | 33 +- .../dependencies/parser/CircomFilesVisitor.ts | 157 ++-- .../parser/CircomTemplateInputsVisitor.ts | 719 ++++++++++++++---- src/core/dependencies/parser/VisitorError.ts | 22 + .../dependencies/circom-files-resolver.ts | 6 +- .../parser/circom-files-visitor.ts | 56 +- .../core/dependencies/file-parser.test.ts | 63 +- 10 files changed, 806 insertions(+), 270 deletions(-) create mode 100644 src/core/dependencies/parser/VisitorError.ts diff --git a/src/cache/BaseCache.ts b/src/cache/BaseCache.ts index f213e72..f064c42 100644 --- a/src/cache/BaseCache.ts +++ b/src/cache/BaseCache.ts @@ -115,7 +115,11 @@ export class BaseCache { public async writeToFile(cacheFilePath: string) { const jsonContent = JSON.stringify( this._cache, - (_key, value) => { + (key, value) => { + if (key === "context") { + return; + } + if (typeof value === "bigint") { return { __bigintval__: value.toString() }; } diff --git a/src/cache/schemas/compile-schemas.ts b/src/cache/schemas/compile-schemas.ts index be4198e..43e2dcc 100644 --- a/src/cache/schemas/compile-schemas.ts +++ b/src/cache/schemas/compile-schemas.ts @@ -1,14 +1,14 @@ -import { BigIntOrNestedArray } from "@distributedlab/circom-parser"; +import { CircomValueType } from "@distributedlab/circom-parser"; import { z } from "zod"; /** - * {@link https://github.com/colinhacks/zod | Zod} schema for defining a recursive type {@link BigIntOrNestedArray}. + * {@link https://github.com/colinhacks/zod | Zod} schema for defining a recursive type {@link CircomValueType}. * * This schema allows for either a `BigInt` value or an array that contains * other `BigInt` values or nested arrays of `BigInt` values, recursively. */ -export const BigIntOrNestedArraySchema: z.ZodType = z.lazy(() => - z.union([z.bigint(), BigIntOrNestedArraySchema.array()]), +export const CircomValueTypeSchema: z.ZodType = z.lazy(() => + z.union([z.bigint(), CircomValueTypeSchema.array()]), ); export const SignalTypeSchema = z.literal("Input").or(z.literal("Output")).or(z.literal("Intermediate")); @@ -35,7 +35,7 @@ export const TemplatesSchema = z.record(z.string(), TemplateSchema); export const MainComponentSchema = z.object({ templateName: z.union([z.string(), z.null()]), publicInputs: z.string().array(), - parameters: BigIntOrNestedArraySchema.array(), + parameters: CircomValueTypeSchema.array(), }); export const ParsedCircomFileDataSchema = z.object({ @@ -53,7 +53,7 @@ export const SignalInfoSchema = z.object({ }); export const ResolvedMainComponentDataSchema = z.object({ - parameters: z.record(z.string(), BigIntOrNestedArraySchema), + parameters: z.record(z.string(), CircomValueTypeSchema), signals: SignalInfoSchema.array(), }); diff --git a/src/core/dependencies/CircomFilesResolver.ts b/src/core/dependencies/CircomFilesResolver.ts index 357612e..d0d0b28 100644 --- a/src/core/dependencies/CircomFilesResolver.ts +++ b/src/core/dependencies/CircomFilesResolver.ts @@ -250,7 +250,7 @@ export class CircomFilesResolver { ); const parsedInputs = this._parser.parseTemplateInputs( - fileWithTemplate.absolutePath, + fileWithTemplate, templateName, mainComponentData.parameters, ); diff --git a/src/core/dependencies/parser/CircomFilesParser.ts b/src/core/dependencies/parser/CircomFilesParser.ts index 46d0530..1509097 100644 --- a/src/core/dependencies/parser/CircomFilesParser.ts +++ b/src/core/dependencies/parser/CircomFilesParser.ts @@ -1,11 +1,12 @@ -import { getCircomParser, ParserError, BigIntOrNestedArray } from "@distributedlab/circom-parser"; +import { CircomValueType, getCircomParser, ParserError } from "@distributedlab/circom-parser"; import { CircomFilesVisitor } from "./CircomFilesVisitor"; import { CircomTemplateInputsVisitor } from "./CircomTemplateInputsVisitor"; import { CircuitsCompileCache } from "../../../cache"; import { Reporter } from "../../../reporter"; -import { InputData, ResolvedFileData } from "../../../types/core"; +import { VisitorError } from "../parser/VisitorError"; +import { CircomResolvedFile, InputData, ResolvedFileData } from "../../../types/core"; /** * A parser class for handling Circom files and extracting relevant data. @@ -48,7 +49,7 @@ export class CircomFilesParser { const parser = getCircomParser(fileContent); - const circomFilesVisitor = new CircomFilesVisitor(); + const circomFilesVisitor = new CircomFilesVisitor(absolutePath); Reporter!.verboseLog("circom-files-parser", "Parsing '%s' file", [absolutePath]); @@ -60,6 +61,10 @@ export class CircomFilesParser { circomFilesVisitor.visit(context); + if (circomFilesVisitor.errors.length > 0) { + throw new VisitorError(circomFilesVisitor.errors); + } + this._cache.set(contentHash, { parsedFileData: circomFilesVisitor.fileData }); return { parsedFileData: circomFilesVisitor.fileData }; @@ -73,29 +78,29 @@ export class CircomFilesParser { * parsing errors and throws a `ParserError` if any issues are encountered. * The structured input data associated with the specified template is then returned. * - * @param absolutePath The absolute path to the Circom file containing the template + * @param circomResolvedFile The resolved Circom file data containing the template to parse * @param templateName The name of the template whose inputs are being parsed * @param parameterValues A record of parameter values used for template input resolution * @returns A structured record of input data for the specified template * @throws ParserError If any parsing issues occur while processing the template inputs */ public parseTemplateInputs( - absolutePath: string, + circomResolvedFile: CircomResolvedFile, templateName: string, - parameterValues: Record, + parameterValues: Record, ): Record { - const parser = getCircomParser(absolutePath); + const circomTemplateInputsVisitor = new CircomTemplateInputsVisitor( + circomResolvedFile.absolutePath, + circomResolvedFile.fileData.parsedFileData.templates[templateName].context, + parameterValues, + ); - const circomTemplateInputsVisitor = new CircomTemplateInputsVisitor(templateName, parameterValues); - - const context = parser.circuit(); + circomTemplateInputsVisitor.startParse(); - if (parser.hasAnyErrors()) { - throw new ParserError(parser.getAllErrors()); + if (circomTemplateInputsVisitor.errors.length > 0) { + throw new VisitorError(circomTemplateInputsVisitor.errors); } - circomTemplateInputsVisitor.visit(context); - return circomTemplateInputsVisitor.templateInputs; } diff --git a/src/core/dependencies/parser/CircomFilesVisitor.ts b/src/core/dependencies/parser/CircomFilesVisitor.ts index 54992c7..27abd0d 100644 --- a/src/core/dependencies/parser/CircomFilesVisitor.ts +++ b/src/core/dependencies/parser/CircomFilesVisitor.ts @@ -1,17 +1,17 @@ import { CircomVisitor, ComponentMainDeclarationContext, - IncludeDeclarationContext, - ParserError, - PragmaDeclarationContext, - CircomExpressionVisitor, - TemplateDeclarationContext, - SignalDeclarationContext, - parseIdentifier, - IdentifierContext, + ExpressionListContext, + IncludeDefinitionContext, + parseSimpleIdentifierList, + PragmaInvalidVersionContext, + PragmaVersionContext, + PublicInputsDefinitionContext, + TemplateDefinitionContext, + ExpressionHelper, } from "@distributedlab/circom-parser"; -import { CircomFileData } from "../../../types/core"; +import { CircomFileData, CircuitResolutionError, ErrorType } from "../../../types/core"; /** * Class responsible for gathering comprehensive information from a Circom file. @@ -23,9 +23,9 @@ import { CircomFileData } from "../../../types/core"; */ export class CircomFilesVisitor extends CircomVisitor { fileData: CircomFileData; - currentTemplate: string | null; + errors: CircuitResolutionError[] = []; - constructor() { + constructor(public fileIdentifier: string) { super(); this.fileData = { @@ -38,111 +38,96 @@ export class CircomFilesVisitor extends CircomVisitor { }, templates: {}, }; - this.currentTemplate = null; } - visitPragmaDeclaration = (ctx: PragmaDeclarationContext) => { - let isCustom: boolean = false; - let compilerVersion: string = ""; - - ctx.CUSTOM_TEMPLATES() ? (isCustom = true) : (isCustom = false); + visitPragmaVersion = (ctx: PragmaVersionContext) => { + this.fileData.pragmaInfo.compilerVersion = ctx.VERSION().getText(); + }; - if (ctx.VERSION()) { - compilerVersion = ctx.VERSION().getText(); - } + visitPragmaInvalidVersion = (ctx: PragmaInvalidVersionContext) => { + this.errors.push({ + type: ErrorType.InvalidPragmaVersion, + context: ctx, + fileIdentifier: this.fileIdentifier, + }); + }; - this.fileData.pragmaInfo = { isCustom, compilerVersion }; + visitPragmaCustomTemplates = () => { + this.fileData.pragmaInfo.isCustom = true; }; - visitIncludeDeclaration = (ctx: IncludeDeclarationContext) => { + visitIncludeDefinition = (ctx: IncludeDefinitionContext) => { this.fileData.includes.push(ctx.STRING().getText().slice(1, -1)); }; - visitTemplateDeclaration = (ctx: TemplateDeclarationContext) => { + visitTemplateDefinition = (ctx: TemplateDefinitionContext) => { if (ctx.ID().getText() in this.fileData.templates) { - throw new ParserError({ - message: `Template name ${ctx.ID().getText()} is already in use`, - line: ctx.start.line, - column: ctx.start.column, + this.errors.push({ + type: ErrorType.TemplateAlreadyUsed, + context: ctx, + fileIdentifier: this.fileIdentifier, + templateIdentifier: ctx.ID().getText(), + message: `Template name ${ctx.ID().getText()} (${ctx.start.line}:${ctx.start.column}) is already in use`, }); - } - - this.currentTemplate = ctx.ID().getText(); - - const parameters: string[] = []; - if (ctx.args() && ctx.args().ID_list()) { - ctx - .args() - .ID_list() - .forEach((arg) => { - parameters.push(arg.getText()); - }); + return; } - this.fileData.templates[this.currentTemplate] = { - inputs: {}, - parameters: parameters, + this.fileData.templates[ctx.ID().getText()] = { + parameters: parseSimpleIdentifierList(ctx._argNames), isCustom: !!ctx.CUSTOM(), + parallel: !!ctx.PARALLEL(), + context: ctx, }; - ctx - .templateBlock() - .templateStmt_list() - .forEach((stmt) => { - this.visitChildren(stmt); - }); - - this.currentTemplate = null; + return; }; - visitSignalDeclaration = (ctx: SignalDeclarationContext) => { - if (this.currentTemplate) { - const signalDefinition = ctx.signalDefinition(); + visitBody = () => { + return; + }; - let signalType = "intermediate"; + visitComponentMainDeclaration = (ctx: ComponentMainDeclarationContext) => { + this.fileData.mainComponentInfo.templateName = ctx.ID().getText(); - if (signalDefinition.SIGNAL_TYPE()) { - signalType = signalDefinition.SIGNAL_TYPE().getText(); - } + if (ctx.publicInputsDefinition()) this.visit(ctx.publicInputsDefinition()); + if (ctx._argValues) this.visit(ctx._argValues); + }; - [signalDefinition.identifier(), ...ctx.identifier_list()].forEach((identifier) => - this._saveInputData(identifier, signalType), - ); + visitPublicInputsDefinition = (ctx: PublicInputsDefinitionContext) => { + for (const input of ctx._publicInputs.ID_list()) { + this.fileData.mainComponentInfo.publicInputs.push(input.getText()); } }; - visitComponentMainDeclaration = (ctx: ComponentMainDeclarationContext) => { - this.fileData.mainComponentInfo.templateName = ctx.ID().getText(); + visitExpressionList = (ctx: ExpressionListContext) => { + const expressionHelper = new ExpressionHelper(this.fileIdentifier); - if (ctx.publicInputsList() && ctx.publicInputsList().args() && ctx.publicInputsList().args().ID_list()) { - ctx - .publicInputsList() - .args() - .ID_list() - .forEach((input) => { - this.fileData.mainComponentInfo.publicInputs.push(input.getText()); + for (let i = 0; i < ctx.expression_list().length; i++) { + const [value, errors] = expressionHelper.setExpressionContext(ctx.expression(i)).parseExpression(); + + if (value === null) { + this.errors.push({ + type: ErrorType.FailedToResolveMainComponentParameter, + context: ctx.expression(i), + fileIdentifier: this.fileIdentifier, + linkedParserErrors: errors, + message: `Failed to parse array parameter with index ${i}. Parameter: ${ctx.expression(i).getText()} (${ctx.expression(i).start.line}:${ctx.expression(i).start.column})`, }); - } - if (ctx.expressionList() && ctx.expressionList().expression_list()) { - const expressionVisitor = new CircomExpressionVisitor(false); + continue; + } - ctx - .expressionList() - .expression_list() - .forEach((expression) => { - this.fileData.mainComponentInfo.parameters.push(expressionVisitor.visitExpression(expression)); + if (errors.length > 0) { + this.errors.push({ + type: ErrorType.InternalExpressionHelperError, + context: ctx.expression(i), + fileIdentifier: this.fileIdentifier, + linkedParserErrors: errors, }); + } + + this.fileData.mainComponentInfo.parameters.push(value); } }; - - private _saveInputData(identifier: IdentifierContext, signalType: string) { - const parsedData = parseIdentifier(identifier); - - this.fileData.templates[this.currentTemplate!].inputs[parsedData.name] = { - dimension: parsedData.dimension, - type: signalType, - }; - } } diff --git a/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts b/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts index 4762378..e75094f 100644 --- a/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts +++ b/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts @@ -1,22 +1,31 @@ import { + CircomValueType, CircomVisitor, - CircomExpressionVisitor, - TemplateDeclarationContext, - SignalDeclarationContext, - BigIntOrNestedArray, + ExpressionContext, + ExpressionHelper, IdentifierContext, - Variables, + IfRegularContext, + IfRegularElseRegularContext, + IfRegularElseWithFollowUpIfContext, + IfWithFollowUpIfContext, + ParserErrorItem, + ParserRuleContext, + parseSimpleIdentifierList, + PIdentifierStatementContext, + PUnderscoreContext, + SignalDeclarationContext, + SignalIdentifierContext, + SignalIdentifierListContext, + SubsAssignmentWithOperationContext, + SubsIcnDecOperationContext, + SubsLeftAssignmentContext, + TemplateDefinitionContext, VarDeclarationContext, - VarDefinitionContext, - RhsValueContext, - TemplateStmtContext, - BlockInstantiationExpressionContext, - DotExpressionContext, + VariableContext, + VarIdentifierContext, } from "@distributedlab/circom-parser"; -import { InputData } from "../../../types/core"; -import { HardhatZKitError } from "../../../errors"; -import { Reporter } from "../../../reporter"; +import { CircuitResolutionError, ErrorType, IdentifierObject, InputData } from "../../../types/core"; /** * Visitor class for the {@link https://www.npmjs.com/package/@distributedlab/circom-parser | @distributedlab/circom-parser} package. @@ -33,200 +42,650 @@ import { Reporter } from "../../../reporter"; */ export class CircomTemplateInputsVisitor extends CircomVisitor { templateInputs: Record; + errors: CircuitResolutionError[] = []; - vars: Variables = {}; + private _vars: VariableContext = {}; + private _declaredVariables: Record = {}; constructor( - private readonly _templateName: string, - private readonly _parameterValues: Record, + public readonly fileIdentifier: string, + public readonly templateContext: TemplateDefinitionContext, + public readonly parameterValues: Record, ) { super(); this.templateInputs = {}; - for (const key of Object.keys(this._parameterValues)) { - this.vars[key] = { - value: this._parameterValues[key], - }; - } + this._vars = parameterValues; + + this._validateVariableContext(); } - visitTemplateDeclaration = (ctx: TemplateDeclarationContext) => { - if (ctx.ID().getText() === this._templateName) { - ctx - .templateBlock() - .templateStmt_list() - .forEach((stmt) => { - this.visitTemplateStmt(stmt); + startParse = () => { + this.visit(this.templateContext); + }; + + visitVarDeclaration = (ctx: VarDeclarationContext) => { + if (ctx.LP() && ctx.RP()) { + this.errors.push({ + type: ErrorType.VarTupleLikeDeclarationNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Tuple-like declarations are not supported (${ctx.start.line}:${ctx.start.column})`, + }); + + return; + } + + this.visitChildren(ctx); + }; + + visitSignalDeclaration = (ctx: SignalDeclarationContext) => { + if (ctx.LP() && ctx.RP()) { + this.errors.push({ + type: ErrorType.VarTupleLikeDeclarationNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Tuple-like declarations are not supported (${ctx.start.line}:${ctx.start.column})`, + }); + + return; + } + + this.visitChildren(ctx); + + return; + }; + + visitSignalIdentifier = (ctx: SignalIdentifierContext) => { + const base = ctx.identifier(); + const baseName = base.ID(); + const resolvedDimensions: number[] = []; + + for (const dimension of base.arrayDimension_list()) { + const [dimensionValue, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(dimension.expression()) + .setVariableContext(this._vars) + .parseExpression(); + + if (dimensionValue === null) { + this.errors.push({ + type: ErrorType.SignalDimensionResolution, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the signal dimension ${dimension.getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, }); + + return; + } + + if (Array.isArray(dimensionValue)) { + this.errors.push({ + type: ErrorType.SignalDimensionResolution, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Invalid signal dimension value ${dimension.getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return; + } + + resolvedDimensions.push(Number(dimensionValue)); + } + + if (!ctx.parentCtx && !(ctx.parentCtx! instanceof SignalIdentifierListContext)) { + throw new Error("INTERNAL ERROR: SignalIdentifier should have a SignalIdentifierListContext as a parent"); + } + + if (!ctx.parentCtx!.parentCtx && !(ctx.parentCtx!.parentCtx! instanceof SignalDeclarationContext)) { + throw new Error("INTERNAL ERROR: SignalIdentifier should have a SignalDeclarationContext as a parent of parent"); + } + + const signalDeclarationContext = ctx.parentCtx!.parentCtx as SignalDeclarationContext; + + let signalType = "intermediate"; + if (signalDeclarationContext.signalHeader().SIGNAL_TYPE()) { + signalType = signalDeclarationContext.signalHeader().SIGNAL_TYPE().getText(); } + + this.templateInputs[baseName.getText()] = { + type: signalType, + dimension: resolvedDimensions, + }; + }; + + /** + * We skip this context to avoid visiting unrelated VarIdentifierContext + */ + visitComponentDeclaration = () => { + return; }; - visitTemplateStmt = (ctx: TemplateStmtContext) => { - if (ctx.signalDeclaration()) { - this.visit(ctx.signalDeclaration()); + visitIfWithFollowUpIf = (ctx: IfWithFollowUpIfContext) => { + const [condition, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(ctx._cond) + .setVariableContext(this._vars) + .parseExpression(); + if (!this._validateCondition(condition, linkedErrors, ctx, ctx._cond)) { return; } - if (ctx.identifier() && ctx.ASSIGNMENT() && ctx.expression(0)) { - const id = ctx.identifier().ID(0).getText(); - const value = new CircomExpressionVisitor(true, this.vars).visitExpression(ctx.expression(0)); + if (condition === 1n) { + this.visitChildren(ctx); + } + }; + + visitIfRegular = (ctx: IfRegularContext) => { + const [condition, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(ctx._cond) + .setVariableContext(this._vars) + .parseExpression(); + + if (!this._validateCondition(condition, linkedErrors, ctx, ctx._cond)) { + return; + } + + if (condition === 1n) { + this.visitChildren(ctx); + } + }; + + visitIfRegularElseWithFollowUpIf = (ctx: IfRegularElseWithFollowUpIfContext) => { + const [condition, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(ctx._cond) + .setVariableContext(this._vars) + .parseExpression(); + + if (!this._validateCondition(condition, linkedErrors, ctx, ctx._cond)) { + return; + } + + if (condition === 1n) { + this.visit(ctx.regularStatements()); + } else { + this.visit(ctx.ifStatements()); + } + }; + + visitIfRegularElseRegular = (ctx: IfRegularElseRegularContext) => { + const [condition, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(ctx._cond) + .setVariableContext(this._vars) + .parseExpression(); + + if (!this._validateCondition(condition, linkedErrors, ctx, ctx._cond)) { + return; + } + + if (condition === 1n) { + this.visit(ctx.regularStatements(0)); + } else { + this.visit(ctx.regularStatements(1)); + } + }; + + /** + * We are sure that identifier defined below is a variable + */ + visitVarIdentifier = (ctx: VarIdentifierContext) => { + const identifierObjects = this._resolveIdentifier(ctx.identifier(), this._vars); + + if (identifierObjects === null) { + this.errors.push({ + type: ErrorType.FailedToResolveIdentifier, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the identifier ${ctx.identifier().getText()} (${ctx.start.line}:${ctx.start.column})`, + }); + + return; + } + + for (const identifierObject of identifierObjects) { + this._declaredVariables[identifierObject.name] = true; + } + + if (ctx._rhs) { + const [value, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(ctx._rhs) + .setVariableContext(this._vars) + .parseExpression(); + + if (value === null) { + this.errors.push({ + type: ErrorType.FailedToResolveIdentifierValue, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the identifier value ${ctx._rhs.getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return; + } if (Array.isArray(value)) { - throw new HardhatZKitError(`Currently, only single value assignment is supported - ${value}`); + this.errors.push({ + type: ErrorType.VarArraysNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the identifier value ${ctx._rhs.getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return; } - this.vars[id] = { - value: value, - }; + this._vars[identifierObjects[0].name] = value; + this._declaredVariables[identifierObjects[0].name] = true; + } + }; + visitSubsLeftAssignment = (ctx: SubsLeftAssignmentContext) => { + if (ctx.LEFT_ASSIGNMENT() || ctx.LEFT_CONSTRAINT()) { return; } - if (!ctx.IF()) { - this.visitChildren(ctx); + const primaryExpression = ctx._lhs.primaryExpression(); + + if (!primaryExpression) { + this.errors.push({ + type: ErrorType.InvalidLeftAssignment, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Expected to assign value to an identifier (${ctx.start.line}:${ctx.start.column})`, + }); + + return; + } + if (primaryExpression instanceof PUnderscoreContext) { return; } - const result = new CircomExpressionVisitor(true, this.vars).visitExpression(ctx.parExpression().expression()); + if (!(primaryExpression instanceof PIdentifierStatementContext)) { + this.errors.push({ + type: ErrorType.InvalidLeftAssignment, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Expected to assign value to an identifier (${ctx.start.line}:${ctx.start.column})`, + }); + + return; + } + + const identifierStatement = primaryExpression.identifierStatement(); + + if (identifierStatement.idetifierAccess_list().length > 0) { + this.errors.push({ + type: ErrorType.ComplexAccessNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Complex assignment to an identifier is not supported (${ctx.start.line}:${ctx.start.column})`, + }); - if (Array.isArray(result)) { - throw new HardhatZKitError( - `Currently, only single value assignment is supported as a result inside if statement - ${result}`, - ); + return; } - if (result === 1n) { - this.visitTemplateStmt(ctx.templateStmt(0)); + const [value, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(ctx._rhs) + .setVariableContext(this._vars) + .parseExpression(); + + if (value === null) { + this.errors.push({ + type: ErrorType.FailedToResolveIdentifierValue, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the identifier value ${ctx._rhs.getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); return; } - if (ctx.ELSE()) { - this.visitTemplateStmt(ctx.templateStmt(1)); + if (Array.isArray(value)) { + this.errors.push({ + type: ErrorType.VarArraysNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the identifier value ${ctx._rhs.getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); return; } + + this._vars[identifierStatement.ID().getText()] = value; + this._declaredVariables[identifierStatement.ID().getText()] = true; + + return; }; - visitSignalDeclaration = (ctx: SignalDeclarationContext) => { - const signalDefinition = ctx.signalDefinition(); + visitSubsAssignmentWithOperation = (ctx: SubsAssignmentWithOperationContext) => { + const identifierStatement = ctx.identifierStatement(); - let signalType = "intermediate"; + if (identifierStatement.idetifierAccess_list().length > 0) { + this.errors.push({ + type: ErrorType.ComplexAccessNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Complex assignment to an identifier is not supported (${ctx.start.line}:${ctx.start.column})`, + }); + + return; + } + + const [value, linkedErrors] = new ExpressionHelper(this.fileIdentifier) + .setExpressionContext(ctx._rhs) + .setVariableContext(this._vars) + .parseExpression(); - if (signalDefinition.SIGNAL_TYPE()) { - signalType = signalDefinition.SIGNAL_TYPE().getText(); + if (value === null) { + this.errors.push({ + type: ErrorType.FailedToResolveIdentifierValue, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the identifier value ${ctx._rhs.getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return; + } + + if (Array.isArray(value)) { + this.errors.push({ + type: ErrorType.InvalidLeftAssignment, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Cannot perform operation on an array (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return; + } + + const assigneeName = identifierStatement.ID().getText(); + + if (!this._declaredVariables[assigneeName]) { + this.errors.push({ + type: ErrorType.AssigneeNotDeclared, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Assignee ${assigneeName} is not declared (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return; + } + + if (Array.isArray(this._vars[assigneeName])) { + this.errors.push({ + type: ErrorType.VarArraysNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Cannot perform operation on an array (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return; } - [signalDefinition.identifier(), ...ctx.identifier_list()].forEach((identifier) => - this._saveInputData(identifier, signalType), - ); + switch (ctx.ASSIGNMENT_WITH_OP().getText()) { + case "+=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) + value; + break; + case "-=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) - value; + break; + case "*=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) * value; + break; + case "**=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) ** value; + break; + case "/=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) / value; + break; + case "\\\\=": + this.errors.push({ + type: ErrorType.QUOOperationNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `QUO operation is not supported (${ctx.start.line}:${ctx.start.column})`, + }); + break; + case "%=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) % value; + break; + case "<<=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) << value; + break; + case ">>=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) >> value; + break; + case "&=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) & value; + break; + case "^=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) ^ value; + break; + case "|=": + this._vars[assigneeName] = BigInt(this._vars[assigneeName] as any) | value; + break; + default: + this.errors.push({ + type: ErrorType.ReachedUnkownOperation, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Invalid operation type ${ctx.ASSIGNMENT_WITH_OP().getText()} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + break; + } }; - visitVarDeclaration = (ctx: VarDeclarationContext) => { - const vars = this._parseVarDefinition(ctx.varDefinition()); + visitSubsIcnDecOperation = (ctx: SubsIcnDecOperationContext) => { + const identifierStatement = ctx.identifierStatement(); + + if (identifierStatement.idetifierAccess_list().length > 0) { + this.errors.push({ + type: ErrorType.ComplexAccessNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Complex assignment to an identifier is not supported (${ctx.start.line}:${ctx.start.column})`, + }); - if (!ctx.ASSIGNMENT()) return; + return; + } + + const assigneeName = identifierStatement.ID().getText(); + + if (!this._declaredVariables[assigneeName]) { + this.errors.push({ + type: ErrorType.AssigneeNotDeclared, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Assignee ${assigneeName} is not declared (${ctx.start.line}:${ctx.start.column})`, + }); + + return; + } + + if (Array.isArray(this._vars[assigneeName])) { + this.errors.push({ + type: ErrorType.VarArraysNotSupported, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Cannot perform operation on an array (${ctx.start.line}:${ctx.start.column})`, + }); - const results = this._parseRHSValue(ctx.rhsValue()); + return; + } - if (vars.length !== results.length) { - throw new HardhatZKitError(`Mismatch between variable definitions and values - ${ctx.getText()}`); + switch (ctx.SELF_OP().getText()) { + case "++": + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this._vars[assigneeName]++; + break; + case "--": + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this._vars[assigneeName]--; + break; + default: + this.errors.push({ + type: ErrorType.ReachedUnkownOperation, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Invalid operation type ${ctx.SELF_OP().getText()} (${ctx.start.line}:${ctx.start.column})`, + }); + break; } + }; - vars.forEach((varName, index) => { - this.vars[varName] = { - value: results[index], - }; + visitSubsInvalidIcnDecOperation = (ctx: SubsIcnDecOperationContext) => { + this.errors.push({ + type: ErrorType.InvalidIncDecOperation, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Prefix increment/decrement operations are not allowed (${ctx.start.line}:${ctx.start.column})`, }); }; - private _parseVarDefinition(ctx: VarDefinitionContext): string[] { - return ctx.identifier_list().map((identifier) => identifier.ID(0).getText()); + private _validateVariableContext() { + const templateParameters = parseSimpleIdentifierList(this.templateContext.simpleIdentifierList()); + + for (const parameter of templateParameters) { + if (this._vars[parameter] === undefined || this._vars[parameter] === null) { + this.errors.push({ + type: ErrorType.MissingTemplateParameterValue, + context: this.templateContext, + fileIdentifier: this.templateContext.ID().getText(), + message: `Missing value for parameter ${parameter} in template ${this.templateContext.ID().getText()}`, + }); + + continue; + } + + this._declaredVariables[parameter] = true; + } } - private _parseRHSValue(ctx: RhsValueContext): bigint[] { - const expressionVisitor = new CircomExpressionVisitor(true, this.vars); - - /** - * Due to the filtering below following expressions are skipped during the input signals resolution: - * - * ```circom - * var var1 = functionCall(); - * var var2 = component.out; - * ``` - */ - if (ctx.expression()) { - if ( - ctx.expression() instanceof BlockInstantiationExpressionContext || - ctx.expression() instanceof DotExpressionContext - ) { - Reporter?.reportUnsupportedExpression(this._templateName, ctx.expression()); - - return [0n]; + private _resolveIdentifier(ctx: IdentifierContext, variableContext: VariableContext = {}): IdentifierObject[] | null { + const baseName = ctx.ID().getText(); + + const expressionHelper = new ExpressionHelper(this.fileIdentifier); + + let result: IdentifierObject[] | null = []; + const resolvedDimensions: bigint[] = []; + + for (let i = 0; i < ctx.arrayDimension_list().length; i++) { + const [dimension, linkedErrors] = expressionHelper + .setExpressionContext(ctx.arrayDimension(i).expression()) + .setVariableContext(variableContext) + .parseExpression(); + + if (dimension === null) { + this.errors.push({ + type: ErrorType.InvalidIdentifierDimensionValue, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Invalid dimension type for identifier ${baseName} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + result = null; + + continue; } - const expressionResult = expressionVisitor.visitExpression(ctx.expression()); + if (Array.isArray(dimension)) { + this.errors.push({ + type: ErrorType.InvalidIdentifierDimensionValue, + context: ctx, + fileIdentifier: this.fileIdentifier, + message: `Invalid dimension value for identifier ${baseName} (${ctx.start.line}:${ctx.start.column})`, + linkedParserErrors: linkedErrors, + }); + + result = null; - if (Array.isArray(expressionResult)) { - throw new HardhatZKitError(`Currently, only single value assignment is supported - ${expressionResult}`); + continue; } - return [expressionResult]; + if (result) { + resolvedDimensions.push(dimension); + } } - if (ctx.expressionList()) { - const expressionsResult: bigint[] = []; + if (result === null) return null; - ctx - .expressionList() - .expression_list() - .forEach((expression) => { - const expressionResult = expressionVisitor.visitExpression(expression); + result = [{ name: baseName }]; - if (Array.isArray(expressionResult)) { - throw new HardhatZKitError(`Currently, only single value assignment is supported - ${expressionResult}`); - } + for (let i = 0; i < resolvedDimensions.length; i++) { + const intermediateResult: IdentifierObject[] = []; - expressionsResult.push(expressionResult); - }); + for (let j = 0; j < resolvedDimensions[i]; j++) { + for (const res of result) { + intermediateResult.push({ + name: `${res.name}[${j}]`, + arrayAccess: [...(res.arrayAccess || []), j], + }); + } + } - return expressionsResult; + result = intermediateResult; } - throw new HardhatZKitError(`RHS value as function call is not supported - ${ctx.getText()}`); + return result; } - private _saveInputData(identifier: IdentifierContext, signalType: string) { - const parsedData = this._parseIdentifier(identifier); - - this.templateInputs[parsedData.name] = { - dimension: parsedData.dimension, - type: signalType, - }; - } + private _validateCondition( + condition: CircomValueType | null, + linkedErrors: ParserErrorItem[], + parentContext: ParserRuleContext, + expressionContext: ExpressionContext, + ): boolean { + if (condition === null) { + this.errors.push({ + type: ErrorType.FailedToResolveIfCondition, + context: parentContext, + fileIdentifier: this.fileIdentifier, + message: `Failed to resolve the if condition ${expressionContext.getText()} (${expressionContext.start.line}:${expressionContext.start.column})`, + linkedParserErrors: linkedErrors, + }); + + return false; + } - private _parseIdentifier(identifier: IdentifierContext) { - const inputDimension: string[] = []; + if (Array.isArray(condition)) { + this.errors.push({ + type: ErrorType.InvalidConditionReturnedValue, + context: parentContext, + fileIdentifier: this.fileIdentifier, + message: `Value returned from the condition is an array ${expressionContext.getText()} (${expressionContext.start.line}:${expressionContext.start.column})`, + linkedParserErrors: linkedErrors, + }); - identifier.arrayDimension_list().forEach((dimension) => { - const expressionVisitor = new CircomExpressionVisitor(true, this.vars); - const expressionResult = expressionVisitor.visitExpression(dimension.expression()); + return false; + } - if (Array.isArray(expressionResult)) { - throw new HardhatZKitError( - `Invalid expression result value during inputs expression resolving - ${expressionResult}`, - ); - } + if (condition !== 1n && condition !== 0n) { + this.errors.push({ + type: ErrorType.InvalidConditionReturnedValue, + context: parentContext, + fileIdentifier: this.fileIdentifier, + message: `Value returned from the condition is not a boolean ${expressionContext.getText()} (${expressionContext.start.line}:${expressionContext.start.column})`, + linkedParserErrors: linkedErrors, + }); - inputDimension.push(expressionResult.toString()); - }); + return false; + } - return { - name: identifier.ID(0).getText(), - dimension: inputDimension, - }; + return true; } } diff --git a/src/core/dependencies/parser/VisitorError.ts b/src/core/dependencies/parser/VisitorError.ts new file mode 100644 index 0000000..fdc290b --- /dev/null +++ b/src/core/dependencies/parser/VisitorError.ts @@ -0,0 +1,22 @@ +import { CircuitResolutionError } from "../../../types/core/dependencies"; + +export class VisitorError extends Error { + public errors: CircuitResolutionError[]; + + constructor(args: CircuitResolutionError[] | CircuitResolutionError) { + super(); + + let firstError: CircuitResolutionError; + + if (Array.isArray(args)) { + this.errors = args; + firstError = args[0]; + } else { + this.errors = [args]; + firstError = args; + } + + this.message = + firstError.message || `Visitor error at ${firstError.context.start.line}:${firstError.context.start.column}`; + } +} diff --git a/src/types/core/dependencies/circom-files-resolver.ts b/src/types/core/dependencies/circom-files-resolver.ts index 530e1a6..54d00cb 100644 --- a/src/types/core/dependencies/circom-files-resolver.ts +++ b/src/types/core/dependencies/circom-files-resolver.ts @@ -1,6 +1,6 @@ import { LibraryInfo } from "hardhat/types/builtin-tasks"; -import { BigIntOrNestedArray } from "@distributedlab/circom-parser"; +import { CircomValueType } from "@distributedlab/circom-parser"; import { CircomFileData } from "./parser/circom-files-visitor"; @@ -20,13 +20,13 @@ export type VisibilityType = "Public" | "Private"; export type SignalInfo = { name: string; - dimension: string[]; + dimension: number[]; type: SignalType; visibility: VisibilityType; }; export type ResolvedMainComponentData = { - parameters: Record; + parameters: Record; signals: SignalInfo[]; }; diff --git a/src/types/core/dependencies/parser/circom-files-visitor.ts b/src/types/core/dependencies/parser/circom-files-visitor.ts index 43bec4d..0d6d330 100644 --- a/src/types/core/dependencies/parser/circom-files-visitor.ts +++ b/src/types/core/dependencies/parser/circom-files-visitor.ts @@ -1,14 +1,64 @@ -import { MainComponent, PragmaComponent } from "@distributedlab/circom-parser"; +import { + CircomValueType, + ParserErrorItem, + ParserRuleContext, + TemplateDefinitionContext, +} from "@distributedlab/circom-parser"; + +export enum ErrorType { + SignalDimensionResolution, + TemplateAlreadyUsed, + InvalidPragmaVersion, + FailedToResolveMainComponentParameter, + InternalExpressionHelperError, + MissingTemplateParameterValue, + InvalidIdentifierDimensionValue, + FailedToResolveIdentifier, + FailedToResolveIdentifierValue, + VarArraysNotSupported, + VarTupleLikeDeclarationNotSupported, + FailedToResolveIfCondition, + InvalidConditionReturnedValue, + InvalidLeftAssignment, + ComplexAccessNotSupported, + AssigneeNotDeclared, + QUOOperationNotSupported, + ReachedUnkownOperation, + InvalidIncDecOperation, +} export type InputData = { - dimension: string[]; type: string; + dimension: number[]; }; +export type IdentifierObject = { + name: string; + arrayAccess?: number[]; +}; + +export type CircuitResolutionError = { + type: ErrorType; + fileIdentifier: string; + context: ParserRuleContext; + message?: string; + templateIdentifier?: string; + linkedParserErrors?: ParserErrorItem[]; +}; + +export type MainComponent = { + templateName: string | null; + publicInputs: string[]; + parameters: CircomValueType[]; +}; + +export type PragmaComponent = { isCustom: boolean; compilerVersion: string }; + export type Template = { - inputs: Record; parameters: string[]; isCustom: boolean; + parallel: boolean; + context: TemplateDefinitionContext; }; export type Templates = { diff --git a/test/unit/core/dependencies/file-parser.test.ts b/test/unit/core/dependencies/file-parser.test.ts index c91e8d1..95c72cd 100644 --- a/test/unit/core/dependencies/file-parser.test.ts +++ b/test/unit/core/dependencies/file-parser.test.ts @@ -4,14 +4,14 @@ import { expect } from "chai"; import { createNonCryptographicHashBasedIdentifier } from "hardhat/internal/util/hash"; import { useEnvironment } from "@test-helpers"; -import { CircomFilesParser } from "@src/core"; +import { CircomFilesParser, CircomFilesVisitor, CircomTemplateInputsVisitor } from "@src/core"; import { TASK_CIRCUITS_COMPILE, ZKIT_SCOPE_NAME } from "@src/task-names"; import { getNormalizedFullPath } from "@src/utils/path-utils"; import { CIRCUITS_COMPILE_CACHE_FILENAME } from "@src/constants"; import { createCircuitsCompileCache } from "@src/cache"; import { createReporter } from "@src/reporter"; - -import { ResolvedFileData, ResolvedMainComponentData } from "@src/types/core"; +import { ResolvedFileData } from "@src/types/core"; +import { getCircomParser, VariableContext } from "@distributedlab/circom-parser"; describe("CircomFilesParser", () => { describe("parse", () => { @@ -79,33 +79,44 @@ describe("CircomFilesParser", () => { }); it("should correctly parse data and resolve var definitions and if statements", async function () { - const mainComponentData: ResolvedMainComponentData = { - parameters: { - SIGNATURE_TYPE: 8n, - DG_HASH_TYPE: 8n, - DOCUMENT_TYPE: 512n, - EC_BLOCK_NUMBER: 256n, - EC_SHIFT: 2n, - DG1_SHIFT: 0n, - AA_SIGNATURE_ALGO: 17n, - DG15_SHIFT: 64n, - DG15_BLOCK_NUMBER: 64n, - AA_SHIFT: 256n, - }, - signals: [], + const mainComponentData: VariableContext = { + SIGNATURE_TYPE: 8n, + DG_HASH_TYPE: 8n, + DOCUMENT_TYPE: 512n, + EC_BLOCK_NUMBER: 256n, + EC_SHIFT: 2n, + DG1_SHIFT: 0n, + AA_SIGNATURE_ALGO: 17n, + DG15_SHIFT: 64n, + DG15_BLOCK_NUMBER: 64n, + AA_SHIFT: 256n, }; const testFilePath = getNormalizedFullPath(this.hre.config.paths.root, "circuits/main/curve.circom"); - const result = parser.parseTemplateInputs(testFilePath, "RegisterIdentityBuilder", mainComponentData.parameters); + const visitor = new CircomFilesVisitor(testFilePath); + + const parser = getCircomParser(testFilePath); + + visitor.visit(parser.circuit()); + + const circomTemplateInputsVisitor = new CircomTemplateInputsVisitor( + testFilePath, + visitor.fileData.templates["RegisterIdentityBuilder"].context, + mainComponentData, + ); + + circomTemplateInputsVisitor.startParse(); + + const result = circomTemplateInputsVisitor.templateInputs; - expect(result["encapsulatedContent"].dimension).to.be.deep.equal([String(256n * 512n)]); - expect(result["dg1"].dimension).to.be.deep.equal(["1024"]); - expect(result["dg15"].dimension).to.be.deep.equal([String(64n * 512n)]); - expect(result["signedAttributes"].dimension).to.be.deep.equal(["1024"]); - expect(result["signature"].dimension).to.be.deep.equal(["32"]); - expect(result["pubkey"].dimension).to.be.deep.equal(["32"]); - expect(result["slaveMerkleInclusionBranches"].dimension).to.be.deep.equal(["80"]); + expect(result["encapsulatedContent"].dimension).to.be.deep.equal([256 * 512]); + expect(result["dg1"].dimension).to.be.deep.equal([1024]); + expect(result["dg15"].dimension).to.be.deep.equal([64 * 512]); + expect(result["signedAttributes"].dimension).to.be.deep.equal([1024]); + expect(result["signature"].dimension).to.be.deep.equal([32]); + expect(result["pubkey"].dimension).to.be.deep.equal([32]); + expect(result["slaveMerkleInclusionBranches"].dimension).to.be.deep.equal([80]); }); }); @@ -124,7 +135,7 @@ describe("CircomFilesParser", () => { expect(function () { parser.parse(fileContent, circuitPath, contentHash); - }).to.throw("Expression value must be of type bigint or bigint array (16:32)"); + }).to.throw("Failed to parse array parameter with index 0. Parameter: getValue() (16:32)"); }); }); }); From 2825eabdc97a552357ff7ce77855b51de19ada2d Mon Sep 17 00:00:00 2001 From: Kyryl R Date: Fri, 1 Nov 2024 08:47:38 +0200 Subject: [PATCH 2/5] Updated parser version --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3ad23a..cd34e70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "test/fixture-projects/*" ], "dependencies": { - "@distributedlab/circom-parser": "0.1.5", + "@distributedlab/circom-parser": "0.2.0", "@solarity/zkit": "0.2.6", "@solarity/zktype": "0.3.1", "@wasmer/wasi": "0.12.0", @@ -482,9 +482,10 @@ } }, "node_modules/@distributedlab/circom-parser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@distributedlab/circom-parser/-/circom-parser-0.1.5.tgz", - "integrity": "sha512-+IFqzYhIXQ1jnksBjuTXrzkF8PIGn/WMc+kJM0JyhVwLJrxPoE23+4rZFIgq3q3RWXRO94KIS49cyk+UnCRzWA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@distributedlab/circom-parser/-/circom-parser-0.2.0.tgz", + "integrity": "sha512-disAiWtGyvBaEayRALBbPYpVVHKFY+hbTN8Os4cMHID1ecvWqrgvMBkVswhq9nzunEOqbWxaCcUvWofRwjr7iQ==", + "license": "MIT", "dependencies": { "antlr4": "4.13.1-patch-1", "ejs": "3.1.10" diff --git a/package.json b/package.json index 4c9762e..752b042 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "node": ">=16" }, "dependencies": { - "@distributedlab/circom-parser": "0.1.5", + "@distributedlab/circom-parser": "0.2.0", "@solarity/zkit": "0.2.6", "@solarity/zktype": "0.3.1", "@wasmer/wasi": "0.12.0", From 9d454fe9ddcc2c8681ce27b7e5c24a19f24a8045 Mon Sep 17 00:00:00 2001 From: Kyryl R Date: Fri, 1 Nov 2024 11:47:34 +0200 Subject: [PATCH 3/5] Updated version. Fixed typo. Handled errors gracefully. Handled null values --- package-lock.json | 4 ++-- package.json | 2 +- .../dependencies/parser/CircomFilesParser.ts | 18 +++++++++++++----- .../dependencies/parser/CircomFilesVisitor.ts | 2 +- .../parser/CircomTemplateInputsVisitor.ts | 4 ++-- .../parser/circom-files-visitor.ts | 2 +- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd34e70..7b8786e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@solarity/hardhat-zkit", - "version": "0.4.11", + "version": "0.4.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@solarity/hardhat-zkit", - "version": "0.4.11", + "version": "0.4.12", "license": "MIT", "workspaces": [ "test/fixture-projects/*" diff --git a/package.json b/package.json index 752b042..111118d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@solarity/hardhat-zkit", - "version": "0.4.11", + "version": "0.4.12", "description": "The ultimate TypeScript environment for Circom development", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", diff --git a/src/core/dependencies/parser/CircomFilesParser.ts b/src/core/dependencies/parser/CircomFilesParser.ts index 1509097..6ad61bb 100644 --- a/src/core/dependencies/parser/CircomFilesParser.ts +++ b/src/core/dependencies/parser/CircomFilesParser.ts @@ -6,7 +6,7 @@ import { CircuitsCompileCache } from "../../../cache"; import { Reporter } from "../../../reporter"; import { VisitorError } from "../parser/VisitorError"; -import { CircomResolvedFile, InputData, ResolvedFileData } from "../../../types/core"; +import { CircomResolvedFile, ErrorType, InputData, ResolvedFileData } from "../../../types/core"; /** * A parser class for handling Circom files and extracting relevant data. @@ -61,8 +61,12 @@ export class CircomFilesParser { circomFilesVisitor.visit(context); - if (circomFilesVisitor.errors.length > 0) { - throw new VisitorError(circomFilesVisitor.errors); + const visitorErrors = circomFilesVisitor.errors.filter( + (error) => error.type !== ErrorType.ComplexAccessNotSupported, + ); + + if (visitorErrors.length > 0) { + throw new VisitorError(visitorErrors); } this._cache.set(contentHash, { parsedFileData: circomFilesVisitor.fileData }); @@ -97,8 +101,12 @@ export class CircomFilesParser { circomTemplateInputsVisitor.startParse(); - if (circomTemplateInputsVisitor.errors.length > 0) { - throw new VisitorError(circomTemplateInputsVisitor.errors); + const visitorErrors = circomTemplateInputsVisitor.errors.filter( + (error) => error.type !== ErrorType.ComplexAccessNotSupported, + ); + + if (visitorErrors.length > 0) { + throw new VisitorError(visitorErrors); } return circomTemplateInputsVisitor.templateInputs; diff --git a/src/core/dependencies/parser/CircomFilesVisitor.ts b/src/core/dependencies/parser/CircomFilesVisitor.ts index 27abd0d..00fbf12 100644 --- a/src/core/dependencies/parser/CircomFilesVisitor.ts +++ b/src/core/dependencies/parser/CircomFilesVisitor.ts @@ -74,7 +74,7 @@ export class CircomFilesVisitor extends CircomVisitor { } this.fileData.templates[ctx.ID().getText()] = { - parameters: parseSimpleIdentifierList(ctx._argNames), + parameters: ctx._argNames ? parseSimpleIdentifierList(ctx._argNames) : [], isCustom: !!ctx.CUSTOM(), parallel: !!ctx.PARALLEL(), context: ctx, diff --git a/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts b/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts index e75094f..eed0b7f 100644 --- a/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts +++ b/src/core/dependencies/parser/CircomTemplateInputsVisitor.ts @@ -477,7 +477,7 @@ export class CircomTemplateInputsVisitor extends CircomVisitor { break; default: this.errors.push({ - type: ErrorType.ReachedUnkownOperation, + type: ErrorType.ReachedUnknownOperation, context: ctx, fileIdentifier: this.fileIdentifier, message: `Invalid operation type ${ctx.ASSIGNMENT_WITH_OP().getText()} (${ctx.start.line}:${ctx.start.column})`, @@ -538,7 +538,7 @@ export class CircomTemplateInputsVisitor extends CircomVisitor { break; default: this.errors.push({ - type: ErrorType.ReachedUnkownOperation, + type: ErrorType.ReachedUnknownOperation, context: ctx, fileIdentifier: this.fileIdentifier, message: `Invalid operation type ${ctx.SELF_OP().getText()} (${ctx.start.line}:${ctx.start.column})`, diff --git a/src/types/core/dependencies/parser/circom-files-visitor.ts b/src/types/core/dependencies/parser/circom-files-visitor.ts index 0d6d330..aed9dfd 100644 --- a/src/types/core/dependencies/parser/circom-files-visitor.ts +++ b/src/types/core/dependencies/parser/circom-files-visitor.ts @@ -23,7 +23,7 @@ export enum ErrorType { ComplexAccessNotSupported, AssigneeNotDeclared, QUOOperationNotSupported, - ReachedUnkownOperation, + ReachedUnknownOperation, InvalidIncDecOperation, } From 13d7274aab4bed0bea9626c78e7243d75f5908a4 Mon Sep 17 00:00:00 2001 From: Kyryl R Date: Fri, 1 Nov 2024 12:19:05 +0200 Subject: [PATCH 4/5] Made filtering of errors simpler --- src/core/dependencies/parser/CircomFilesParser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/dependencies/parser/CircomFilesParser.ts b/src/core/dependencies/parser/CircomFilesParser.ts index 6ad61bb..da28b29 100644 --- a/src/core/dependencies/parser/CircomFilesParser.ts +++ b/src/core/dependencies/parser/CircomFilesParser.ts @@ -62,7 +62,7 @@ export class CircomFilesParser { circomFilesVisitor.visit(context); const visitorErrors = circomFilesVisitor.errors.filter( - (error) => error.type !== ErrorType.ComplexAccessNotSupported, + (error) => error.type === ErrorType.SignalDimensionResolution, ); if (visitorErrors.length > 0) { @@ -102,7 +102,7 @@ export class CircomFilesParser { circomTemplateInputsVisitor.startParse(); const visitorErrors = circomTemplateInputsVisitor.errors.filter( - (error) => error.type !== ErrorType.ComplexAccessNotSupported, + (error) => error.type === ErrorType.SignalDimensionResolution, ); if (visitorErrors.length > 0) { From 8782b7b0a4fc1f8ec5714b55b40170e0fee70bfb Mon Sep 17 00:00:00 2001 From: Kyryl R Date: Fri, 1 Nov 2024 12:51:25 +0200 Subject: [PATCH 5/5] Updated error filtering --- src/core/dependencies/parser/CircomFilesParser.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/dependencies/parser/CircomFilesParser.ts b/src/core/dependencies/parser/CircomFilesParser.ts index da28b29..074ee9a 100644 --- a/src/core/dependencies/parser/CircomFilesParser.ts +++ b/src/core/dependencies/parser/CircomFilesParser.ts @@ -62,7 +62,10 @@ export class CircomFilesParser { circomFilesVisitor.visit(context); const visitorErrors = circomFilesVisitor.errors.filter( - (error) => error.type === ErrorType.SignalDimensionResolution, + (error) => + error.type === ErrorType.InvalidPragmaVersion || + error.type === ErrorType.TemplateAlreadyUsed || + error.type === ErrorType.FailedToResolveMainComponentParameter, ); if (visitorErrors.length > 0) {