def visitIdentifier(self, ctx: SolidityParser.IdentifierContext): name: str = ctx.name.text if name.startswith(cfg.reserved_name_prefix) or name.startswith(f'_{cfg.reserved_name_prefix}'): raise SyntaxException(f'Identifiers must not start with reserved prefix _?{cfg.reserved_name_prefix}', ctx, self.code) elif name.endswith(cfg.reserved_conflict_resolution_suffix): raise SyntaxException(f'Identifiers must not end with reserved suffix {cfg.reserved_name_prefix}', ctx, self.code) return ast.Identifier(name)
def visitAnnotatedTypeName(self, ctx: SolidityParser.AnnotatedTypeNameContext): pa = None hom = Homomorphism.NON_HOMOMORPHIC if ctx.privacy_annotation is not None: pa = self.visit(ctx.privacy_annotation) if ctx.homomorphism is not None: hom = self.visit(ctx.homomorphism) if not (isinstance(pa, ast.AllExpr) or isinstance(pa, ast.MeExpr) or isinstance(pa, IdentifierExpr)): raise SyntaxException('Privacy annotation can only be me | all | Identifier', ctx.privacy_annotation, self.code) if isinstance(pa, ast.AllExpr) and hom != Homomorphism.NON_HOMOMORPHIC: raise SyntaxException('Public types cannot be homomorphic', ctx.homomorphism, self.code) return ast.AnnotatedTypeName(self.visit(ctx.type_name), pa, hom)
def visitElementaryTypeName(self, ctx: SolidityParser.ElementaryTypeNameContext): t = ctx.getText() if t == 'address': return ast.AddressTypeName() elif t == 'address payable': return ast.AddressPayableTypeName() elif t == 'bool': return ast.BoolTypeName() elif t.startswith('int'): return ast.IntTypeName(t) elif t.startswith('uint'): return ast.UintTypeName(t) elif t == 'var': raise SyntaxException(f'Use of unsupported var keyword', ctx, self.code) else: raise SyntaxException(f"Use of unsupported type '{t}'.", ctx, self.code)
def visitHomomorphismAnnotation(self, ctx:SolidityParser.HomomorphismAnnotationContext): t = ctx.getText() for h in Homomorphism: if h.type_annotation == t: return h else: raise SyntaxException(f'Unsupported homomorphism {t}', ctx, self.code)
def visitVersionPragma(self, ctx: SolidityParser.VersionPragmaContext): version = ctx.ver.getText().strip() spec = NpmSpec(version) name = self.handle_field(ctx.name) if name == 'zkay' and Version(cfg.zkay_version) not in spec: raise SyntaxException(f'Contract requires a different zkay version.\n' f'Current version is {cfg.zkay_version} but pragma zkay mandates {version}.', ctx.ver, self.code) elif name != 'zkay' and spec != cfg.zkay_solc_version_compatibility: # For backwards compatibility with older zkay versions assert name == 'solidity' raise SyntaxException(f'Contract requires solidity version {spec}, which is not compatible ' f'with the current zkay version (requires {cfg.zkay_solc_version_compatibility}).', ctx.ver, self.code) return f'{name} {version}'
def visitFunctionCallExpr(self, ctx: SolidityParser.FunctionCallExprContext): func = self.visit(ctx.func) args = self.handle_field(ctx.args) if isinstance(func, IdentifierExpr): if func.idf.name == 'reveal': if len(args) != 2: raise SyntaxException(f'Invalid number of arguments for reveal: {args}', ctx.args, self.code) return ReclassifyExpr(args[0], args[1], None) elif func.idf.name in self.rehom_expressions: name = func.idf.name homomorphism = self.rehom_expressions[name] if len(args) != 1: raise SyntaxException(f'Invalid number of arguments for {name}: {args}', ctx.args, self.code) return RehomExpr(args[0], homomorphism) return FunctionCallExpr(func, args)
def visitAnnotatedTypeName(self, ctx: SolidityParser.AnnotatedTypeNameContext): pa = None if ctx.privacy_annotation is not None: pa = self.visit(ctx.privacy_annotation) if not (isinstance(pa, ast.AllExpr) or isinstance(pa, ast.MeExpr) or isinstance(pa, IdentifierExpr)): raise SyntaxException('Privacy annotation can only be me | all | Identifier', ctx.privacy_annotation, self.code) return ast.AnnotatedTypeName(self.visit(ctx.type_name), pa)
def handle_fdef(self, ctx): if isinstance(ctx, SolidityParser.ConstructorDefinitionContext): idf, ret_params = None, None else: idf, ret_params = self.visit(ctx.idf), self.handle_field(ctx.return_parameters) if '$' in idf.name: raise SyntaxException('$ is not allowed in zkay function identifiers', ctx.idf, self.code) params, mods, body = self.handle_field(ctx.parameters), self.handle_field(ctx.modifiers), self.visit(ctx.body) return ast.ConstructorOrFunctionDefinition(idf, params, mods, ret_params, body)
def visitStringLiteralExpr(self, ctx: SolidityParser.StringLiteralExprContext): s = ctx.getText() # Remove quotes if s.startswith('"'): s = s[1:-1].replace('\\"', '"') else: s = s[2:-2] raise SyntaxException('Use of unsupported string literal expression', ctx, self.code)
def visitContractDefinition(self, ctx: SolidityParser.ContractDefinitionContext): identifier = self.visit(ctx.idf) if '$' in identifier.name: raise SyntaxException('$ is not allowed in zkay contract identifiers', ctx.idf, self.code) parts = [self.visit(c) for c in ctx.parts] state_vars = [p for p in parts if isinstance(p, StateVariableDeclaration)] cfdefs = [p for p in parts if isinstance(p, ast.ConstructorOrFunctionDefinition)] constructors = [p for p in cfdefs if p.is_constructor] functions = [p for p in cfdefs if p.is_function] enums = [p for p in parts if isinstance(p, ast.EnumDefinition)] return ContractDefinition(identifier, state_vars, constructors, functions, enums)
def visitFunctionCallExpr(self, ctx: SolidityParser.FunctionCallExprContext): func = self.visit(ctx.func) args = self.handle_field(ctx.args) if isinstance(func, IdentifierExpr): if func.idf.name == 'reveal': if len(args) != 2: raise SyntaxException(f'Invalid number of arguments for reveal: {args}', ctx.args, self.code) return ReclassifyExpr(args[0], args[1]) return FunctionCallExpr(func, args)
def visitAssignmentExpr(self, ctx: SolidityParser.AssignmentExprContext): if not self.is_expr_stmt(ctx): raise SyntaxException('Assignments are only allowed as statements', ctx, self.code) lhs = self.visit(ctx.lhs) rhs = self.visit(ctx.rhs) assert ctx.op.text[-1] == '=' op = ctx.op.text[:-1] if ctx.op.text != '=' else '' if op: # If the assignment contains an additional operator -> replace lhs = rhs with lhs = lhs 'op' rhs rhs = FunctionCallExpr(BuiltinFunction(op).override(line=ctx.op.line, column=ctx.op.column), [self.visit(ctx.lhs), rhs]) rhs.line = ctx.rhs.start.line rhs.column = ctx.rhs.start.column + 1 return ast.AssignmentStatement(lhs, rhs, op)
def _handle_crement_expr(self, ctx, kind: str): if not self.is_expr_stmt(ctx): raise SyntaxException(f'{kind}-crement expressions are only allowed as statements', ctx, self.code) op = '+' if ctx.op.text == '++' else '-' one = NumberLiteralExpr(1) one.line = ctx.op.line one.column = ctx.op.column + 1 fct = FunctionCallExpr(BuiltinFunction(op).override(line=ctx.op.line, column=ctx.op.column), [self.visit(ctx.expr), one]) fct.line = ctx.op.line fct.column = ctx.op.column + 1 return ast.AssignmentStatement(self.visit(ctx.expr), fct, f'{kind}{ctx.op.text}')
def visitExpressionStatement(self, ctx: SolidityParser.ExpressionStatementContext): e = self.visit(ctx.expr) if isinstance(e, ast.Statement): return e else: # handle require if isinstance(e, FunctionCallExpr): f = e.func if isinstance(f, IdentifierExpr): if f.idf.name == 'require': if len(e.args) != 1: raise SyntaxException(f'Invalid number of arguments for require: {e.args}', ctx.expr, self.code) return ast.RequireStatement(e.args[0]) assert isinstance(e, ast.Expression) return ExpressionStatement(e)
def visitEnumDefinition(self, ctx:SolidityParser.EnumDefinitionContext): idf = self.visit(ctx.idf) if '$' in idf.name: raise SyntaxException('$ is not allowed in zkay enum identifiers', ctx.idf, self.code) values = [self.visit(v) for v in ctx.values] return ast.EnumDefinition(idf, values)
def visitEnumValue(self, ctx:SolidityParser.EnumValueContext): idf = self.visit(ctx.idf) if '$' in idf.name: raise SyntaxException('$ is not allowed in zkay enum value identifiers', ctx.idf, self.code) return ast.EnumValue(idf)
def visitIndexExpr(self, ctx: SolidityParser.IndexExprContext): arr = self.visit(ctx.arr) if not isinstance(arr, ast.LocationExpr): raise SyntaxException(f'Expression cannot be indexed', ctx.arr, self.code) index = self.visit(ctx.index) return IndexExpr(arr, index)