Skip to content
This repository was archived by the owner on Jun 30, 2025. It is now read-only.

Commit ca848d5

Browse files
committed
refactor: eliminate the parser
1 parent 18c8854 commit ca848d5

File tree

4 files changed

+73
-78
lines changed

4 files changed

+73
-78
lines changed

src/parser.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/processor.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import fs from 'fs';
2-
import { Parser } from './parser';
32
import { Config } from './types/config.t';
43
import { Validator } from './validator';
54
import { SourceUnit, FunctionDefinition, ContractDefinition } from 'solc-typed-ast';
65
import { NodeToProcess } from './types/solc-typed-ast.t';
6+
import { parseNodeNatspec } from './utils';
77

88
interface IWarning {
99
location: string;
@@ -13,12 +13,10 @@ interface IWarning {
1313
export class Processor {
1414
config: Config;
1515
validator: Validator;
16-
parser: Parser;
1716

1817
constructor(config: Config) {
1918
this.config = config;
2019
this.validator = new Validator(config);
21-
this.parser = new Parser();
2220
}
2321

2422
processSources(sourceUnits: SourceUnit[]): IWarning[] {
@@ -42,7 +40,7 @@ export class Processor {
4240
}
4341

4442
validateNatspec(node: NodeToProcess): string[] {
45-
const nodeNatspec = this.parser.parseNodeNatspec(node);
43+
const nodeNatspec = parseNodeNatspec(node);
4644
return this.validator.validate(node, nodeNatspec);
4745
}
4846

src/utils.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import fs from 'fs/promises';
22
import path from 'path';
33
import { ASTKind, ASTReader, SourceUnit, compileSol } from 'solc-typed-ast';
4+
import { Natspec, NatspecDefinition } from './types/natspec.t';
5+
import { NodeToProcess } from './types/solc-typed-ast.t';
46

57
export async function getProjectCompiledSources(rootPath: string, contractsPath: string, ignoredPaths: string[]): Promise<SourceUnit[]> {
68
// Fetch Solidity files from the specified directory
@@ -62,3 +64,49 @@ export async function getRemappings(rootPath: string): Promise<string[]> {
6264
return [];
6365
}
6466
}
67+
68+
export function parseNodeNatspec(node: NodeToProcess): Natspec {
69+
if (!node.documentation) {
70+
return { tags: [], params: [], returns: [] };
71+
}
72+
73+
let currentTag: NatspecDefinition | null = null;
74+
const result: Natspec = {
75+
tags: [],
76+
params: [],
77+
returns: [],
78+
};
79+
80+
const docText: string = typeof node.documentation === 'string' ? node.documentation : node.documentation.text;
81+
82+
docText.split('\n').forEach((line) => {
83+
const tagTypeMatch = line.match(/^\s*@(\w+)/);
84+
if (tagTypeMatch) {
85+
const tagName = tagTypeMatch[1];
86+
87+
if (tagName === 'inheritdoc') {
88+
const tagMatch = line.match(/^\s*@(\w+) (.*)$/);
89+
if (tagMatch) {
90+
currentTag = null;
91+
result.inheritdoc = { content: tagMatch[2] };
92+
}
93+
} else if (tagName === 'param' || tagName === 'return') {
94+
const tagMatch = line.match(/^\s*@(\w+) *(\w+) (.*)$/);
95+
if (tagMatch) {
96+
currentTag = { name: tagMatch[2], content: tagMatch[3].trim() };
97+
result[tagName === 'param' ? 'params' : 'returns'].push(currentTag);
98+
}
99+
} else {
100+
const tagMatch = line.match(/^\s*@(\w+) *(.*)$/);
101+
if (tagMatch) {
102+
currentTag = { name: tagName, content: tagMatch[2] };
103+
result.tags.push(currentTag);
104+
}
105+
}
106+
} else if (currentTag) {
107+
currentTag.content += '\n' + line;
108+
}
109+
});
110+
111+
return result;
112+
}

test/parser.test.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { ContractDefinition } from 'solc-typed-ast';
2-
import { Parser } from '../src/parser';
32
import { getFileCompiledSource } from './utils';
3+
import { parseNodeNatspec } from '../src/utils';
44
import { mockNatspec } from './mocks';
55

66
describe('Parser', () => {
7-
const parser: Parser = new Parser();
87
let contract: ContractDefinition;
98

109
describe('Contract', () => {
@@ -15,7 +14,7 @@ describe('Parser', () => {
1514

1615
it('should parse the inheritdoc tag', async () => {
1716
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionNoParams')!;
18-
const result = parser.parseNodeNatspec(node);
17+
const result = parseNodeNatspec(node);
1918

2019
expect(result).toEqual(
2120
mockNatspec({
@@ -32,7 +31,7 @@ describe('Parser', () => {
3231

3332
it('should parse constant', async () => {
3433
const node = contract.vStateVariables.find(({ name }) => name === 'SOME_CONSTANT')!;
35-
const result = parser.parseNodeNatspec(node);
34+
const result = parseNodeNatspec(node);
3635

3736
expect(result).toEqual(
3837
mockNatspec({
@@ -45,7 +44,7 @@ describe('Parser', () => {
4544

4645
it('should parse variable', async () => {
4746
const node = contract.vStateVariables.find(({ name }) => name === 'someVariable')!;
48-
const result = parser.parseNodeNatspec(node);
47+
const result = parseNodeNatspec(node);
4948

5049
expect(result).toEqual(
5150
mockNatspec({
@@ -58,7 +57,7 @@ describe('Parser', () => {
5857

5958
it('should parse modifier', async () => {
6059
const node = contract.vModifiers.find(({ name }) => name === 'someModifier')!;
61-
const result = parser.parseNodeNatspec(node);
60+
const result = parseNodeNatspec(node);
6261

6362
expect(result).toEqual(
6463
mockNatspec({
@@ -80,7 +79,7 @@ describe('Parser', () => {
8079

8180
it('should parse external function', async () => {
8281
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionNoParams')!;
83-
const result = parser.parseNodeNatspec(node);
82+
const result = parseNodeNatspec(node);
8483

8584
expect(result).toEqual(
8685
mockNatspec({
@@ -99,7 +98,7 @@ describe('Parser', () => {
9998

10099
it('should parse private function', async () => {
101100
const node = contract.vFunctions.find(({ name }) => name === '_viewPrivate')!;
102-
const result = parser.parseNodeNatspec(node);
101+
const result = parseNodeNatspec(node);
103102

104103
expect(result).toEqual(
105104
mockNatspec({
@@ -131,7 +130,7 @@ describe('Parser', () => {
131130

132131
it('should parse multiline descriptions', async () => {
133132
const node = contract.vFunctions.find(({ name }) => name === '_viewMultiline')!;
134-
const result = parser.parseNodeNatspec(node);
133+
const result = parseNodeNatspec(node);
135134

136135
expect(result).toEqual(
137136
mockNatspec({
@@ -147,7 +146,7 @@ describe('Parser', () => {
147146

148147
it('should parse multiple of the same tag', async () => {
149148
const node = contract.vFunctions.find(({ name }) => name === '_viewDuplicateTag')!;
150-
const result = parser.parseNodeNatspec(node);
149+
const result = parseNodeNatspec(node);
151150

152151
expect(result).toEqual(
153152
mockNatspec({
@@ -174,7 +173,7 @@ describe('Parser', () => {
174173

175174
it('should parse error', async () => {
176175
const node = contract.vErrors.find(({ name }) => name === 'SimpleError')!;
177-
const result = parser.parseNodeNatspec(node);
176+
const result = parseNodeNatspec(node);
178177

179178
expect(result).toEqual(
180179
mockNatspec({
@@ -190,7 +189,7 @@ describe('Parser', () => {
190189

191190
it('should parse event', async () => {
192191
const node = contract.vEvents.find(({ name }) => name === 'SimpleEvent')!;
193-
const result = parser.parseNodeNatspec(node);
192+
const result = parseNodeNatspec(node);
194193

195194
expect(result).toEqual(
196195
mockNatspec({
@@ -206,7 +205,7 @@ describe('Parser', () => {
206205

207206
it('should parse struct', async () => {
208207
const node = contract.vStructs.find(({ name }) => name === 'SimplestStruct')!;
209-
const result = parser.parseNodeNatspec(node);
208+
const result = parseNodeNatspec(node);
210209

211210
expect(result).toEqual(
212211
mockNatspec({
@@ -235,7 +234,7 @@ describe('Parser', () => {
235234
// TODO: Parse natspec for enums
236235
// it('should parse enum', async () => {
237236
// const node = contract.vEnums.find(({ name }) => name === 'SimpleEnum')!;
238-
// const result = parser.parseNodeNatspec(node);
237+
// const result = parseNodeNatspec(node);
239238

240239
// expect(result).toEqual(mockNatspec({
241240
// tags: [],
@@ -244,7 +243,7 @@ describe('Parser', () => {
244243

245244
it('should parse external function without parameters', async () => {
246245
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionNoParams')!;
247-
const result = parser.parseNodeNatspec(node);
246+
const result = parseNodeNatspec(node);
248247

249248
expect(result).toEqual(
250249
mockNatspec({
@@ -270,7 +269,7 @@ describe('Parser', () => {
270269

271270
it('should parse external function with parameters', async () => {
272271
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionWithParams')!;
273-
const result = parser.parseNodeNatspec(node);
272+
const result = parseNodeNatspec(node);
274273

275274
expect(result).toEqual(
276275
mockNatspec({
@@ -309,7 +308,7 @@ describe('Parser', () => {
309308

310309
it('should parse struct', async () => {
311310
const node = contract.vStructs.find(({ name }) => name === 'SimpleStruct')!;
312-
const result = parser.parseNodeNatspec(node);
311+
const result = parseNodeNatspec(node);
313312

314313
expect(result).toEqual(
315314
mockNatspec({
@@ -320,7 +319,7 @@ describe('Parser', () => {
320319

321320
it('should parse inheritdoc + natspec', async () => {
322321
const node = contract.vStateVariables.find(({ name }) => name === 'someVariable')!;
323-
const result = parser.parseNodeNatspec(node);
322+
const result = parseNodeNatspec(node);
324323

325324
expect(result).toEqual(
326325
mockNatspec({
@@ -339,21 +338,21 @@ describe('Parser', () => {
339338

340339
it('should not parse the inheritdoc tag with just 2 slashes', async () => {
341340
const node = contract.vStateVariables.find(({ name }) => name === 'SOME_CONSTANT')!;
342-
const result = parser.parseNodeNatspec(node);
341+
const result = parseNodeNatspec(node);
343342

344343
expect(result).toEqual(mockNatspec({}));
345344
});
346345

347346
it('should not parse regular comments as natspec', async () => {
348347
const node = contract.vFunctions.find(({ name }) => name === 'viewFunctionWithParams')!;
349-
const result = parser.parseNodeNatspec(node);
348+
const result = parseNodeNatspec(node);
350349

351350
expect(result).toEqual(mockNatspec({}));
352351
});
353352

354353
it('should parse natspec with multiple spaces', async () => {
355354
const node = contract.vFunctions.find(({ name }) => name === '_viewPrivate')!;
356-
const result = parser.parseNodeNatspec(node);
355+
const result = parseNodeNatspec(node);
357356

358357
expect(result).toEqual(
359358
mockNatspec({
@@ -381,14 +380,14 @@ describe('Parser', () => {
381380

382381
it('should not parse natspec with invalid number of slashes', async () => {
383382
const node = contract.vFunctions.find(({ name }) => name === '_viewInternal')!;
384-
const result = parser.parseNodeNatspec(node);
383+
const result = parseNodeNatspec(node);
385384

386385
expect(result).toEqual(mockNatspec({}));
387386
});
388387

389388
it('should parse block natspec with invalid formatting', async () => {
390389
const node = contract.vFunctions.find(({ name }) => name === '_viewBlockLinterFail')!;
391-
const result = parser.parseNodeNatspec(node);
390+
const result = parseNodeNatspec(node);
392391

393392
expect(result).toEqual(
394393
mockNatspec({
@@ -404,7 +403,7 @@ describe('Parser', () => {
404403

405404
it('should parse block natspec with invalid formatting', async () => {
406405
const node = contract.vFunctions.find(({ name }) => name === '_viewLinterFail')!;
407-
const result = parser.parseNodeNatspec(node);
406+
const result = parseNodeNatspec(node);
408407

409408
expect(result).toEqual(
410409
mockNatspec({

0 commit comments

Comments
 (0)