|
| 1 | +// --8<-- [start:step-1-imports] |
| 2 | +import * as assert from "node:assert"; |
1 | 3 | import { Language } from "@nomicfoundation/slang/language"; |
2 | 4 | import { RuleKind, TokenKind } from "@nomicfoundation/slang/kinds"; |
| 5 | +import { ContractDefinition, SourceUnit, SourceUnitMember, SourceUnitMembers } from "@nomicfoundation/slang/ast"; |
| 6 | +// --8<-- [end:step-1-imports] |
3 | 7 |
|
4 | 8 | 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 | + `; |
7 | 15 |
|
8 | 16 | 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] |
10 | 20 |
|
11 | | - let contractNames = []; |
12 | | - let cursor = parseTree.createTreeCursor(); |
| 21 | + { |
| 22 | + // --8<-- [start:step-3-cursor] |
| 23 | + const contractNames = []; |
| 24 | + const cursor = parseTree.createTreeCursor(); |
13 | 25 |
|
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(); |
18 | 29 |
|
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(); |
23 | 39 | } |
24 | | - contractNames.push(tokenNode.text); |
25 | 40 |
|
26 | | - cursor.goToParent(); |
| 41 | + assert.deepEqual(contractNames, ["Foo", "Bar", "Baz"]); |
| 42 | + // --8<-- [end:step-3-cursor] |
27 | 43 | } |
28 | 44 |
|
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 | + } |
30 | 67 | }); |
0 commit comments