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:
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:
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: