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

import assert from "node:assert";
import { Parser } from "@nomicfoundation/slang/parser";
import {
  assertIsNonterminalNode,
  assertIsTerminalNode,
  NonterminalKind,
  TerminalKind,
} from "@nomicfoundation/slang/cst";

const parser = Parser.create("0.8.0");

const parseOutput = 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 (const error of parseOutput.errors) {
  console.error(`Error at byte offset ${error.textRange.start.utf8}: ${error.message}`);
}

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

assert(parseOutput.isValid());

Inspecting the Parse Tree#

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

const contract = parseOutput.tree;
assertIsNonterminalNode(contract, NonterminalKind.ContractDefinition);

const contractChildren = contract.children;
assert.equal(contractChildren.length, 7);

const [contractKeyword, firstSpace, contractName, secondSpace, openBrace, members, closeBrace] = contractChildren;

assertIsTerminalNode(contractKeyword!.node, TerminalKind.ContractKeyword, "contract");
assertIsTerminalNode(firstSpace!.node, TerminalKind.Whitespace, " ");
assertIsTerminalNode(contractName!.node, TerminalKind.Identifier, "Foo");
assertIsTerminalNode(secondSpace!.node, TerminalKind.Whitespace, " ");
assertIsTerminalNode(openBrace!.node, TerminalKind.OpenBrace, "{");
assertIsNonterminalNode(members!.node, NonterminalKind.ContractMembers);
assertIsTerminalNode(closeBrace!.node, TerminalKind.CloseBrace, "}");

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

const contractSource = contract.unparse();
assert.equal(contractSource, "contract Foo {}");