Skip to content

Commit a6d9180

Browse files
committed
doc example
1 parent 7357fb5 commit a6d9180

2 files changed

Lines changed: 75 additions & 20 deletions

File tree

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,67 @@
1+
// --8<-- [start:step-1-imports]
2+
import * as assert from "node:assert";
13
import { Language } from "@nomicfoundation/slang/language";
24
import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds";
5+
import { ContractDefinition, SourceUnit, SourceUnitMember, SourceUnitMembers } from "@nomicfoundation/slang/ast";
6+
// --8<-- [end:step-1-imports]
37

48
test("list contract names", () => {
5-
// ...or read a file using `fs.readFileSync` (or `fs.readFile`)
6-
const data = "contract Foo {} contract Bar {} contract Baz {}";
9+
// --8<-- [start:step-2-source]
10+
const source = `
11+
contract Foo {}
12+
contract Bar {}
13+
contract Baz {}
14+
`;
715

816
const language = new Language("0.8.0");
9-
const parseTree = language.parse(RuleKind.SourceUnit, data);
17+
const parseTree = language.parse(RuleKind.SourceUnit, source);
18+
assert.ok(parseTree.isValid);
19+
// --8<-- [end:step-2-source]
1020

11-
let contractNames = [];
12-
let cursor = parseTree.createTreeCursor();
21+
{
22+
// --8<-- [start:step-3-cursor]
23+
const contractNames = [];
24+
const cursor = parseTree.createTreeCursor();
1325

14-
while (cursor.goToNextRuleWithKinds([RuleKind.ContractDefinition])) {
15-
// You have to make sure you return the cursor to original position
16-
cursor.goToFirstChild();
17-
cursor.goToNextTokenWithKinds([TokenKind.Identifier]);
26+
while (cursor.goToNextRuleWithKind(RuleKind.ContractDefinition)) {
27+
// Descend down to the first child of the contract node:
28+
cursor.goToFirstChild();
1829

19-
// The currently pointed-to node is the name of the contract
20-
let tokenNode = cursor.node();
21-
if (tokenNode.kind !== TokenKind.Identifier) {
22-
throw new Error("Expected identifier");
30+
// Then move to the next identifier (the contract name):
31+
cursor.goToNextTokenWithKind(TokenKind.Identifier);
32+
33+
const identifier = cursor.node();
34+
assert.strictEqual(identifier.kind, TokenKind.Identifier);
35+
contractNames.push(identifier.text);
36+
37+
// You have to make sure you return the cursor to original position:
38+
cursor.goToParent();
2339
}
24-
contractNames.push(tokenNode.text);
2540

26-
cursor.goToParent();
41+
assert.deepEqual(contractNames, ["Foo", "Bar", "Baz"]);
42+
// --8<-- [end:step-3-cursor]
2743
}
2844

29-
expect(contractNames).toEqual(["Foo", "Bar", "Baz"]);
45+
{
46+
// --8<-- [start:step-4-ast]
47+
const rootNode = parseTree.tree();
48+
assert.strictEqual(rootNode.kind, RuleKind.SourceUnit);
49+
50+
const sourceUnit = new SourceUnit(rootNode);
51+
assert.ok(sourceUnit.members instanceof SourceUnitMembers);
52+
53+
const contractNames = [];
54+
55+
for (const member of sourceUnit.members.items) {
56+
assert.ok(member instanceof SourceUnitMember);
57+
58+
const contract = member.variant;
59+
assert.ok(contract instanceof ContractDefinition);
60+
61+
contractNames.push(contract.name.text);
62+
}
63+
64+
assert.deepEqual(contractNames, ["Foo", "Bar", "Baz"]);
65+
// --8<-- [end:step-4-ast]
66+
}
3067
});

documentation/public/user-guide/npm-package/how-to-parse-a-file/index.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,32 @@ Let's do that:
6363
--8<-- "crates/solidity/outputs/npm/tests/src/doc-examples/reconstruct-source.ts:step-2-assertion"
6464
```
6565

66-
### Example 2: List the top-level contracts and their names
66+
### Example 2: Reading Contracts using Cursors
6767

6868
The `Cursor` type provides procedural-style functions that allow you to navigate the source in a step-by-step manner. In addition to `goToNext`, we can go to the parent, first child, next sibling, etc., as well as nodes with a given kind.
6969

70-
To list the top-level contracts and their names, we need to visit the `ContractDefinition` rule nodes and then their `Identifier` children.
70+
Let's start with this piece of solidity source code:
7171

72-
Let's do that:
72+
```{ .ts }
73+
--8<-- "crates/solidity/outputs/npm/tests/src/doc-examples/list-contract-names.ts:step-1-imports"
74+
75+
--8<-- "crates/solidity/outputs/npm/tests/src/doc-examples/list-contract-names.ts:step-2-source"
76+
```
77+
78+
To list the top-level contracts and their names, we need to visit the `ContractDefinition` rule nodes and then their `Identifier` children:
79+
80+
```{ .ts }
81+
--8<-- "crates/solidity/outputs/npm/tests/src/doc-examples/list-contract-names.ts:step-3-cursor"
82+
```
83+
84+
### Example 3: Reading Contracts using AST Types
85+
86+
AST types are a set of TypeScript classes that provide a typed view of the untyped CST nodes.
87+
You can convert any untyped CST node to its corresponding AST type using their constructors.
88+
AST types are immutable, and are constructed on the fly, as they are used/accessed for the first time.
89+
90+
Let's try to perform the same task using AST types:
7391

7492
```{ .ts }
75-
--8<-- "crates/solidity/outputs/npm/tests/src/doc-examples/list-contract-names.ts"
93+
--8<-- "crates/solidity/outputs/npm/tests/src/doc-examples/list-contract-names.ts:step-4-ast"
7694
```

0 commit comments

Comments
 (0)