Skip to content
This repository was archived by the owner on Jul 9, 2021. It is now read-only.

Commit 02382c1

Browse files
committed
Parser and Unparser
1 parent f156ebf commit 02382c1

File tree

10 files changed

+357
-0
lines changed

10 files changed

+357
-0
lines changed

packages/sol-meta/bin/sol-meta.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env node
2+
require('../lib/src/cli.js')

packages/sol-meta/src/astToMock.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
import { ASTNode } from 'solidity-parser-antlr';
3+
4+
export function astToMock(ast: ASTNode): ASTNode {
5+
return ast;
6+
}

packages/sol-meta/src/cli.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env node
2+
// We need the above pragma since this script will be run as a command-line tool.
3+
4+
import { logUtils } from '@0xproject/utils';
5+
import * as _ from 'lodash';
6+
import 'source-map-support/register';
7+
import * as yargs from 'yargs';
8+
9+
import { Compiler } from './compiler';
10+
11+
const DEFAULT_CONTRACTS_LIST = '*';
12+
const SEPARATOR = ',';
13+
14+
(async () => {
15+
const argv = yargs
16+
.option('contracts-dir', {
17+
type: 'string',
18+
description: 'path of contracts directory to compile',
19+
})
20+
.option('contracts', {
21+
type: 'string',
22+
description: 'comma separated list of contracts to compile',
23+
})
24+
.help().argv;
25+
const contracts = _.isUndefined(argv.contracts)
26+
? undefined
27+
: argv.contracts === DEFAULT_CONTRACTS_LIST
28+
? DEFAULT_CONTRACTS_LIST
29+
: argv.contracts.split(SEPARATOR);
30+
const opts = {
31+
contractsDir: argv.contractsDir,
32+
artifactsDir: argv.artifactsDir,
33+
contracts,
34+
};
35+
const compiler = new Compiler(opts);
36+
await compiler.compileAsync();
37+
})().catch(err => {
38+
logUtils.log(err);
39+
process.exit(1);
40+
});

packages/sol-meta/src/compiler.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import fs = require('fs');
2+
import { parse } from './parser';
3+
import { unparse } from './unparser';
4+
5+
export interface CompilerOptions {
6+
contractsDir: string;
7+
contracts: string;
8+
}
9+
10+
export class Compiler {
11+
12+
private readonly opts: CompilerOptions;
13+
14+
constructor(opts?: CompilerOptions) {
15+
this.opts = opts || {
16+
contractsDir: '',
17+
contracts: '',
18+
} ;
19+
}
20+
21+
public async compileAsync() {
22+
console.log(this.opts.contracts[0]);
23+
const source = fs.readFileSync(this.opts.contracts[0], 'utf8');
24+
try {
25+
const ast = parse(source);
26+
console.log(unparse(ast));
27+
} catch (e) {
28+
console.log(e);
29+
}
30+
console.log('Compiling');
31+
}
32+
}

packages/sol-meta/src/index.ts

Whitespace-only changes.

packages/sol-meta/src/parser.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import * as parser from 'solidity-parser-antlr';
2+
3+
export const parse = (source) => parser.parse(source, {});

packages/sol-meta/src/transform.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { ASTNode } from 'solidity-parser-antlr';
2+
3+
const visitor = {
4+
5+
// If a function is not public, add a wrapper to make it public
6+
FunctionDefinition: func =>
7+
func.visbility,
8+
9+
}
10+
11+
export function transform(ast: ASTNode): ASTNode {
12+
return (visitor[ast.type] || (a => a))(ast);
13+
}

packages/sol-meta/src/unparser.ts

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import { ASTNode } from 'solidity-parser-antlr';
2+
3+
const stresc = (s: string) => `\"${s}\"`;
4+
const indent = (s: string) => `\t${s.replace(/\n/g, '\n\t')}`;
5+
const block = (s: string) => `{\n${indent(s)}\n}`;
6+
const unparen = (s: string) => s.replace(/^\((.*)\)$/, '$1');
7+
8+
const visitor = {
9+
// Source level
10+
11+
SourceUnit: ({children}) =>
12+
children.map(unparse).join('\n'),
13+
14+
PragmaDirective: ({ name, value }) =>
15+
`pragma ${name} ${value};`,
16+
17+
ImportDirective: ({ path, symbolAliases }) =>
18+
`import `+
19+
(symbolAliases ? `{${symbolAliases.map(([from, to]) =>
20+
from + (to ? ` as ${to}` : '')
21+
).join(', ')}} from `: '') +
22+
`${stresc(path)};`,
23+
24+
ContractDefinition: ({name, kind, baseContracts, subNodes}) =>
25+
`${kind} ${name} ${(baseContracts.length > 0 ? 'is ' : '')}` +
26+
baseContracts.map(unparse).join(', ') +
27+
block('\n' + subNodes.map(unparse).join('\n\n')),
28+
29+
InheritanceSpecifier: ({baseName: {namePath}}) =>
30+
namePath,
31+
32+
// Contract level
33+
34+
UsingForDeclaration: ({typeName, libraryName}) =>
35+
`using ${libraryName} for ${unparse(typeName)};`,
36+
37+
StateVariableDeclaration: ({variables}) =>
38+
variables.map(unparse).join(', ') + ';',
39+
40+
StructDefinition: ({name, members}) =>
41+
`struct ${name} ${block(members.map(unparse).join(';\n') + ';')}`,
42+
43+
EnumDefinition: ({name, members}) =>
44+
`enum ${name} ${block(members.map(unparse).join(',\n'))}`,
45+
46+
EnumValue: ({name}) =>
47+
name,
48+
49+
EventDefinition: ({ name, parameters }) =>
50+
`event ${name}${unparse(parameters)};`,
51+
52+
ModifierDefinition: ({name, parameters, body}) =>
53+
`modifier ${name}${Array.isArray(parameters) ? '' : unparse(parameters)} ${unparse(body)}`,
54+
// Note: when there is no parameter block, instead of an ASTNode there is a []
55+
56+
FunctionDefinition: ({visibility, name, parameters, body, modifiers, isConstructor, stateMutability}) =>
57+
(isConstructor ? 'constructor' : `function ${name}`) +
58+
unparse(parameters) + ' ' +
59+
(visibility && visibility != 'default' ? visibility + ' ' : '') + (stateMutability || '') + '\n' +
60+
indent(modifiers.map(unparse).join('\n')) + '\n' +
61+
(body ? unparse(body) : ';'),
62+
63+
ParameterList: ({parameters}) =>
64+
`(${parameters.map(unparse).join(', ')})`,
65+
66+
Parameter: ({typeName, name, storageLocation}) =>
67+
`${unparse(typeName)} ${storageLocation || ''} ${name || ''}`,
68+
69+
ModifierInvocation: ({name, arguments: args}) =>
70+
`${name}(${args.map(unparse).join(', ')})`,
71+
72+
// Statements
73+
74+
Block: ({statements}) =>
75+
block(statements.map(unparse).join('\n')),
76+
77+
VariableDeclarationStatement: ({variables, initialValue}) =>
78+
variables.map(unparse) +
79+
(initialValue ? ` = ${unparse(initialValue)};` : ';'),
80+
81+
ExpressionStatement: ({expression}) =>
82+
`${unparse(expression)};`,
83+
84+
EmitStatement: ({eventCall}) =>
85+
`emit ${unparen(unparse(eventCall))};`,
86+
87+
ReturnStatement: ({expression}) =>
88+
`return ${expression ? unparse(expression) : ''};`,
89+
90+
BreakStatement: ({}) =>
91+
`break;`,
92+
93+
ContinueStatement: ({}) =>
94+
`continue;`,
95+
96+
ThrowStatement: ({}) =>
97+
`throw;`,
98+
99+
IfStatement: ({condition, trueBody, falseBody}) =>
100+
`if (${unparse(condition)})\n${unparse(trueBody)}` +
101+
(falseBody ? `else\n${unparse(falseBody)}` : ''),
102+
103+
ForStatement: ({initExpression: i, conditionExpression: c, loopExpression: l, body}) =>
104+
`for (${unparse(i).replace(';','')}; ${unparse(c)}; ${unparse(l).replace(';','')}) ${unparse(body)}`,
105+
106+
InlineAssemblyStatement: ({language, body}) => // TODO language
107+
`assembly ${unparse(body)}`,
108+
109+
// Types
110+
111+
ElementaryTypeName: ({name}) =>
112+
name,
113+
114+
UserDefinedTypeName: ({namePath}) =>
115+
namePath,
116+
117+
ArrayTypeName: ({baseTypeName, length}) =>
118+
`${unparse(baseTypeName)}[${length ? unparse(length) : ''}]`,
119+
120+
Mapping: ({keyType, valueType}) =>
121+
`mapping (${unparse(keyType)} => ${unparse(valueType)})`,
122+
123+
// Expressions
124+
125+
Identifier: ({ name }) =>
126+
name,
127+
128+
BooleanLiteral: ({ value }) =>
129+
value ? 'true' : 'false',
130+
131+
NumberLiteral: ({number, subdenomination}) => // TODO subdenomination
132+
number,
133+
134+
StringLiteral: ({value}) =>
135+
stresc(value),
136+
137+
FunctionCall: ({expression, arguments: args, names}) => // TODO: names
138+
`(${unparse(expression)}(${args.map(unparse).join(', ')}))`,
139+
140+
Conditional: ({condition, trueExpression, falseExpression}) =>
141+
`(${unparse(condition)} ? ${unparse(trueExpression)} : ${unparse(falseExpression)})`,
142+
143+
UnaryOperation: ({operator, subExpression, isPrefix}) =>
144+
`(${isPrefix ? operator : ''}${unparse(subExpression)}${isPrefix ? '' : operator})`,
145+
146+
BinaryOperation: ({operator, left, right}) =>
147+
`(${unparse(left)} ${operator} ${unparse(right)})`,
148+
149+
MemberAccess: ({expression, memberName}) =>
150+
`(${unparse(expression)}.${memberName})`,
151+
152+
IndexAccess: ({base, index}) =>
153+
`(${unparse(base)}[${unparse(index)}])`,
154+
155+
ElementaryTypeNameExpression: ({typeName}) =>
156+
`(${unparse(typeName)})`,
157+
158+
VariableDeclaration: ({typeName, name, visibility, isDeclaredConst, isIndexed, expression}) =>
159+
`${unparse(typeName)} ` +
160+
(isIndexed ? 'indexed ' : '') +
161+
(visibility && visibility != 'default' ? visibility + ' ' : '') +
162+
(isDeclaredConst ? 'constant ' : '') +
163+
`${name}` +
164+
(expression ? ` = ${unparse(expression)}` : ''),
165+
166+
NewExpression: ({typeName}) =>
167+
`(new ${unparse(typeName)})`,
168+
169+
TupleExpression: ({components}) =>
170+
`[${components.map(unparse).join(', ')}]`,
171+
172+
// Assembly
173+
174+
AssemblyBlock: ({operations}) =>
175+
block(operations.map(unparse).join('\n')),
176+
177+
AssemblyAssignment: ({names, expression}) =>
178+
`${names.map(unparse).join(', ')} := ${unparse(expression)}`,
179+
180+
AssemblyLocalDefinition: ({names, expression}) =>
181+
`let ${names.map(unparse).join(', ')} := ${unparse(expression)}`,
182+
183+
AssemblyCall: ({functionName, arguments: args}) =>
184+
args.length == 0 ?
185+
functionName :
186+
`${functionName}(${args.map(unparse).join(', ')})`,
187+
188+
AssemblyIf: ({condition, body}) =>
189+
`if ${unparse(condition)} ${unparse(body)}`,
190+
191+
AssemblyFor: ({pre, condition, post, body}) =>
192+
`for ${[pre, condition, post, body].map(unparse).join(' ')}`,
193+
194+
AssemblySwitch: ({expression, cases}) =>
195+
`switch ${unparse(expression)}\n${cases.map(unparse).join('\n')}`,
196+
197+
AssemblyCase: ({value, block}) =>
198+
`case ${unparse(value)} ${unparse(block)}`
199+
200+
DecimalNumber: ({ value }) =>
201+
value,
202+
203+
HexNumber: ({ value }) =>
204+
value,
205+
}
206+
207+
export function unparse(ast: ASTNode): string {
208+
return (visitor[ast.type] || (a => {
209+
console.log(a);
210+
console.trace();
211+
return `<${a.type}>`;
212+
}))(ast);
213+
}

packages/sol-meta/test/dump

Whitespace-only changes.

packages/sol-meta/test/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as chai from 'chai';
2+
import 'mocha';
3+
import * as glob from 'glob';
4+
import * as fs from 'fs';
5+
import * as path from 'path';
6+
import { parse } from '../src/parser';
7+
import { unparse } from '../src/unparser';
8+
9+
const expect = chai.expect;
10+
11+
const promisify = (func) => (...args) =>
12+
new Promise((resolve, reject) =>
13+
func(...args, (error, result) =>
14+
error ? reject(error) : resolve(result)));
15+
16+
const findContracts = searchPath =>
17+
glob.sync(searchPath).map(file => ({
18+
name: path.basename(file, '.sol') + ` (${file})`,
19+
source: fs.readFileSync(file, 'utf8')
20+
}));
21+
22+
const contracts = findContracts('../contracts/src/**/*.sol');
23+
24+
describe('Parser', () => {
25+
26+
it('should have test contracts', () => {
27+
expect(contracts).to.have.lengthOf.above(10);
28+
});
29+
30+
contracts.forEach(({name, source}) =>
31+
it(`should parse ${name}`, () => {
32+
parse(source);
33+
})
34+
);
35+
36+
});
37+
38+
describe.only('Unparser', () => {
39+
40+
contracts.forEach(({name, source}) =>
41+
it(`should unparse ${name}`, () => {
42+
const ast = parse(source);
43+
const src = unparse(ast) ;
44+
const ast2 = parse(src);
45+
//expect(ast2).to.deep.equal(ast);
46+
})
47+
);
48+
})

0 commit comments

Comments
 (0)