Skip to content

Using the Parser#

Using the API directly provides us with a more fine-grained control over the parsing process. It allows us to parse not just the input as a top-level source unit, but also individual nonterminals like contracts, various definitions, and even expressions.

Parsing Source Files#

Let's start with this simple source file, that contains a single contract:

input.sol
contract Foo {}

We begin by creating a Parser object with a specified version. This is an entry point for our parser API. Then we can use it to parse the source file, specifying the top-level nonterminal to parse:

use semver::Version;
use slang_solidity::cst::{Node, NonterminalKind, TerminalKind};
use slang_solidity::parser::Parser;

let parser = Parser::create(Version::parse("0.8.0")?)?;

let parse_output = parser.parse(NonterminalKind::ContractDefinition, source);

Checking for Syntax Errors#

If the file has errors, we can get them from the ParseOutput type, and print them out:

for error in parse_output.errors() {
    eprintln!(
        "Error at byte offset {offset}: {message}",
        offset = error.text_range().start.utf8,
        message = error.message()
    );
}

Otherwise, we can check if input is valid using this helpful utility:

assert!(parse_output.is_valid());

Inspecting the Parse Tree#

Now, let's try to inspect the resulting CST, and iterate on its children:

let parse_tree = parse_output.tree();

let contract = parse_tree.as_nonterminal().unwrap();
assert_eq!(contract.kind, NonterminalKind::ContractDefinition);
assert_eq!(contract.children.len(), 7);

let children = &contract.children;
assert!(
    matches!(&children[0].node, Node::Terminal(t) if t.kind == TerminalKind::ContractKeyword)
);
assert!(matches!(&children[1].node, Node::Terminal(t) if t.kind == TerminalKind::Whitespace));
assert!(matches!(&children[2].node, Node::Terminal(t) if t.kind == TerminalKind::Identifier));
assert!(matches!(&children[3].node, Node::Terminal(t) if t.kind == TerminalKind::Whitespace));
assert!(matches!(&children[4].node, Node::Terminal(t) if t.kind == TerminalKind::OpenBrace));
assert!(
    matches!(&children[5].node, Node::Nonterminal(r) if r.kind == NonterminalKind::ContractMembers)
);
assert!(matches!(&children[6].node, Node::Terminal(t) if t.kind == TerminalKind::CloseBrace));

Additionally, we can convert the CST node back into the input string:

assert_eq!(contract.unparse(), "contract Foo {}");