solgo

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2023 License: Apache-2.0 Imports: 6 Imported by: 0

README

Go Report Card License PkgGoDev

Solidity Parser in Golang

SolGo contains a Solidity parser written in Golang, using Antlr and AntlrGo for grammar parsing. The aim of this project is to provide a tool to parse Solidity source code into a structured format, enabling further analysis and manipulation within Go programs.

The parser is generated from a Solidity grammar file using Antlr, producing a lexer, parser, and listener in Golang using AntlrGo. This allows for the syntactic analysis of Solidity code, transforming it into a parse tree that can be traversed and manipulated.

This project is ideal for developers working with Solidity smart contracts who wish to leverage the power and efficiency of Golang for their analysis tools.

Note: This project is still in development and is not yet ready for use in production.

Why?

In my projects, I have a strong preference for using Golang or Rust over Javascript. I found myself needing to parse Solidity code and conduct some analysis on it. During my research, I discovered several projects that aimed to do this. However, they were either incomplete, not maintained, or not written in Go at all. This led me to the decision to create this project. The goal is to provide a Solidity parser in Golang that is as "complete as possible" and well-maintained. I'm excited to see how this project will develop and evolve.

This project will be integrated within unpack. I've found that for many deployed contracts, the source code is available, but the ABIs are not. This project will help bridge that gap.

ANTLR Grammar

We are using grammar files that are maintained by the Solidity team. Link to the grammar files can be found here

ANTLR Go

We are using the ANTLR4 Go runtime library to generate the parser. Repository can be found here.

Features

  • Syntactic Analysis: SolGo transforms Solidity code into a parse tree that can be traversed and manipulated, providing a detailed syntactic analysis of the code.
  • Listener Registration: SolGo allows for the registration of custom listeners that can be invoked as the parser walks the parse tree, enabling custom handling of parse events.
  • Error Handling: SolGo includes a SyntaxErrorListener which collects syntax errors encountered during parsing, providing detailed error information including line, column, message, severity, and context.
  • Contextual Parsing: SolGo provides a ContextualSolidityParser that maintains a stack of contexts, allowing for context-specific parsing rules.

Getting Started

To use SolGo, you will need to have Golang installed on your machine. You can then import SolGo into your Go programs like this:

import "github.com/txpull/solgo"

Usage

Parse Solidity Code and Retrieve Contract Information
package main

import "github.com/txpull/solgo"

var contract = `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Some additional comments that can be extracted

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";

contract MyToken is Initializable, ERC20Upgradeable, AccessControlUpgradeable, PausableUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;
}
`

func main() {
    parser, err := solgo.New(context.Background(), strings.NewReader(contract))
    if err != nil {
        panic(err)
    }

    // Register the contract information listener
    contractListener := NewContractListener(parser.GetParser())
    if err := parser.RegisterListener(ListenerContractInfo, contractListener); err != nil {
        panic(err)
    }

    if errs := parser.Parse(); len(errs) > 0 {
        for _, err := range errs {
            fmt.Println(err)
        }
        return
    }

    // Get the contract information from listener that is built for testing purposes
    fmt.Printf("%+v \n", contractListener.ToStruct())
}

Contributing

Contributions to SolGo are always welcome! If you have a feature request, bug report, or proposal for improvement, please open an issue. If you wish to contribute code, please fork the repository, make your changes, and submit a pull request.

License

SolGo is licensed under the Apache 2.0. See LICENSE for the full license text.

Acknowledgements

We would like to express our gratitude to the Solidity team for maintaining the Solidity grammar files, and to the Antlr and AntlrGo team for providing the powerful Antlr tool that makes this project possible.

Documentation

Overview

Package solgo provides a suite of tools for parsing and analyzing Solidity contracts. It includes a contextual parser that maintains a stack of contexts as it parses a contract, allowing it to keep track of the current context (e.g., within a contract definition, function definition, etc.). It also includes a contract listener that extracts information about contracts as they are parsed, including the contract name, implemented interfaces, imported contracts, pragmas, and comments. Additionally, it includes a syntax error listener that listens for syntax errors in contracts and categorizes them by severity. These tools can be used together to provide a comprehensive interface for working with Solidity contracts, making it easier to understand their structure and identify potential issues.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ContextualSolidityParser

type ContextualSolidityParser struct {
	*parser.SolidityParser // SolidityParser is the base parser from the Solidity parser.
	// contains filtered or unexported fields
}

ContextualSolidityParser is a wrapper around the SolidityParser that maintains a stack of contexts. This allows the parser to keep track of the current context (e.g., within a contract definition, function definition, etc.) as it parses a Solidity contract.

func (*ContextualSolidityParser) ContractDefinition

ContractDefinition is called when the parser enters a contract definition. It pushes "ContractDefinition" onto the context stack, calls the original ContractDefinition method, and then pops the context from the stack before returning.

func (*ContextualSolidityParser) CurrentContext

func (p *ContextualSolidityParser) CurrentContext() string

CurrentContext returns the current context, i.e., the context at the top of the stack. If the stack is empty, it returns an empty string.

func (*ContextualSolidityParser) PopContext

func (p *ContextualSolidityParser) PopContext()

PopContext pops the current context from the context stack. This should be called when the parser exits a rule.

func (*ContextualSolidityParser) PushContext

func (p *ContextualSolidityParser) PushContext(context string)

PushContext pushes a new context onto the context stack. This should be called when the parser enters a new rule.

type ContractInfo

type ContractInfo struct {
	Comments   []string // Comments associated with the contract
	License    string   // License information of the contract
	Pragmas    []string // Pragmas specified in the contract
	Imports    []string // Imported dependencies of the contract
	Name       string   // Name of the contract
	Implements []string // Interfaces implemented by the contract
}

ContractInfo contains information about a contract

type ContractListener

type ContractListener struct {
	*parser.BaseSolidityParserListener // BaseSolidityParserListener is the base listener from the Solidity parser.
	// contains filtered or unexported fields
}

ContractListener is a listener for the Solidity parser that extracts information about contracts, including the contract name, implemented interfaces, imported contracts, pragmas, and comments. It also extracts the SPDX license identifier if present. This listener is designed to be used in conjunction with the Solidity parser to provide a convenient interface for working with Solidity contracts.

func NewContractListener

func NewContractListener(parser *parser.SolidityParser) *ContractListener

NewContractListener creates a new ContractListener. It takes a SolidityParser as an argument.

func (*ContractListener) EnterContractDefinition

func (l *ContractListener) EnterContractDefinition(ctx *parser.ContractDefinitionContext)

EnterContractDefinition is called when the parser enters a contract definition. It extracts the contract name from the context and sets it to the contractName field.

func (*ContractListener) EnterEveryRule

func (l *ContractListener) EnterEveryRule(ctx antlr.ParserRuleContext)

EnterEveryRule is called when the parser enters any rule in the grammar. It is used to search for license and any comments that code has. ANTLR parser by default have comments disabled to be parsed as tokens, so we need to search for them manually using the CommonTokenStream.

func (*ContractListener) EnterImportDirective

func (l *ContractListener) EnterImportDirective(ctx *parser.ImportDirectiveContext)

EnterImportDirective is called when the parser enters an import directive. It extracts the import path from the context and adds it to the imports slice.

func (*ContractListener) EnterInheritanceSpecifier

func (l *ContractListener) EnterInheritanceSpecifier(ctx *parser.InheritanceSpecifierContext)

EnterInheritanceSpecifier is called when the parser enters an inheritance specifier. It extracts the name of the inherited contract/interface and adds it to the implements slice.

func (*ContractListener) EnterPragmaDirective

func (l *ContractListener) EnterPragmaDirective(ctx *parser.PragmaDirectiveContext)

EnterPragmaDirective is called when the parser enters a pragma directive. It extracts all pragma tokens from the context and adds them to the pragmas slice.

func (*ContractListener) EnterUsingDirective

func (l *ContractListener) EnterUsingDirective(ctx *parser.UsingDirectiveContext)

EnterUsingDirective is called when the parser enters a using directive. It extracts the name of the library and adds it to the libraries slice.

func (*ContractListener) GetComments

func (l *ContractListener) GetComments() []string

GetComments returns a slice of all comments in the contract.

func (*ContractListener) GetImplements

func (l *ContractListener) GetImplements() []string

GetImplements returns a slice of all interfaces that the contract implements.

func (*ContractListener) GetImports

func (l *ContractListener) GetImports() []string

GetImports returns a slice of all contracts that the contract imports.

func (*ContractListener) GetLicense

func (l *ContractListener) GetLicense() string

GetLicense returns the SPDX license identifier, if present.

func (*ContractListener) GetName

func (l *ContractListener) GetName() string

GetName returns the name of the contract.

func (*ContractListener) GetPragmas

func (l *ContractListener) GetPragmas() []string

GetPragmas returns a slice of all pragma directives in the contract.

func (*ContractListener) ToStruct

func (l *ContractListener) ToStruct() ContractInfo

GetInfoForTests returns a map of all information extracted from the contract. This is used for testing purposes only

type ListenerName

type ListenerName string

ListenerName represents the name of a listener.

const (
	ListenerAbi          ListenerName = "abi"
	ListenerContractInfo ListenerName = "contract_info"
	ListenerAst          ListenerName = "ast"
)

Predefined listener names.

type SeverityLevel

type SeverityLevel int

SeverityLevel represents the severity of a syntax error.

const (
	// SeverityHigh represents a high severity error.
	SeverityHigh SeverityLevel = iota
	// SeverityMedium represents a medium severity error.
	SeverityMedium
	// SeverityLow represents a low severity error.
	SeverityLow
)

type SolGo

type SolGo struct {
	// contains filtered or unexported fields
}

SolGo is a struct that encapsulates the functionality for parsing and analyzing Solidity contracts.

func New

func New(ctx context.Context, input io.Reader) (*SolGo, error)

New creates a new instance of SolGo. It takes a context and an io.Reader from which the Solidity contract is read. It initializes an input stream, lexer, token stream, and parser, and sets up error listeners.

func (*SolGo) GetAllListeners

func (s *SolGo) GetAllListeners() map[ListenerName]antlr.ParseTreeListener

func (*SolGo) GetInput

func (s *SolGo) GetInput() io.Reader

GetInput returns the raw input reader from which the Solidity contract is read.

func (*SolGo) GetInputStream

func (s *SolGo) GetInputStream() *antlr.InputStream

GetInputStream returns the ANTLR input stream which is used by the lexer.

func (*SolGo) GetLexer

func (s *SolGo) GetLexer() *parser.SolidityLexer

GetLexer returns the Solidity lexer which tokenizes the input stream.

func (*SolGo) GetListener

func (s *SolGo) GetListener(name ListenerName) (antlr.ParseTreeListener, error)

func (*SolGo) GetParser

func (s *SolGo) GetParser() *parser.SolidityParser

GetParser returns the Solidity parser which parses the token stream.

func (*SolGo) GetTokenStream

func (s *SolGo) GetTokenStream() *antlr.CommonTokenStream

GetTokenStream returns the stream of tokens produced by the lexer.

func (*SolGo) GetTree

func (s *SolGo) GetTree() antlr.ParseTree

GetTree returns the root of the parse tree that results from parsing the Solidity contract.

func (*SolGo) IsListenerRegistered

func (s *SolGo) IsListenerRegistered(name ListenerName) bool

func (*SolGo) Parse

func (s *SolGo) Parse() []SyntaxError

Parse initiates the parsing process. It walks the parse tree with all registered listeners and returns any syntax errors that were encountered during parsing.

func (*SolGo) RegisterListener

func (s *SolGo) RegisterListener(name ListenerName, listener antlr.ParseTreeListener) error

type SyntaxError

type SyntaxError struct {
	// Line is the line number where the error occurred.
	Line int
	// Column is the column number where the error occurred.
	Column int
	// Message is the error message.
	Message string
	// Severity is the severity level of the error.
	Severity SeverityLevel
	// Context is the context in which the error occurred.
	Context string
}

SyntaxError represents a syntax error in a Solidity contract.

type SyntaxErrorListener

type SyntaxErrorListener struct {
	// DefaultErrorListener is the base error listener from the ANTLR4 parser.
	*antlr.DefaultErrorListener
	// Errors is a slice of SyntaxErrors.
	Errors []SyntaxError
}

SyntaxErrorListener is a listener for syntax errors in Solidity contracts. It extends the DefaultErrorListener from the ANTLR4 parser.

func NewSyntaxErrorListener

func NewSyntaxErrorListener() *SyntaxErrorListener

NewSyntaxErrorListener creates a new SyntaxErrorListener.

func (*SyntaxErrorListener) SyntaxError

func (s *SyntaxErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException)

SyntaxError is called when a syntax error is encountered. It creates a SyntaxError with the line number, column number, error message, severity level, and context, and adds it to the Errors slice.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL