Skip to content

Commit 9b8a33e

Browse files
committed
Add findReferences.
1 parent 0759e9b commit 9b8a33e

9 files changed

+465
-168
lines changed

LICENSE

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2018 Observable, Inc.
2+
3+
Permission to use, copy, modify, and/or distribute this software for any purpose
4+
with or without fee is hereby granted, provided that the above copyright notice
5+
and this permission notice appear in all copies.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
11+
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
12+
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
13+
THIS SOFTWARE.

index.js

+2-165
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,2 @@
1-
import {tokTypes as tt, Parser} from "acorn";
2-
3-
const SCOPE_FUNCTION = 2;
4-
const SCOPE_ASYNC = 4;
5-
const SCOPE_GENERATOR = 8;
6-
const CellParser = Parser.extend(observable);
7-
8-
export function parseCell(input) {
9-
return CellParser.parse(input);
10-
}
11-
12-
function observable(Parser) {
13-
return class extends Parser {
14-
constructor(...options) {
15-
super(...options);
16-
this.O_function = 0;
17-
this.O_async = false;
18-
this.O_generator = false;
19-
}
20-
enterScope(flags) {
21-
if (flags & SCOPE_FUNCTION) ++this.O_function;
22-
return super.enterScope.apply(this, arguments);
23-
}
24-
exitScope() {
25-
if (this.currentScope() & SCOPE_FUNCTION) --this.O_function;
26-
return super.exitScope.apply(this, arguments);
27-
}
28-
parseForIn(node) {
29-
if (this.O_function === 1 && node.await) this.O_async = true;
30-
return super.parseForIn.apply(this, arguments);
31-
}
32-
parseAwait() {
33-
if (this.O_function === 1) this.O_async = true;
34-
return super.parseAwait.apply(this, arguments);
35-
}
36-
parseYield() {
37-
if (this.O_function === 1) this.O_generator = true;
38-
return super.parseYield.apply(this, arguments);
39-
}
40-
parseImport(node) {
41-
this.next();
42-
node.specifiers = this.parseImportSpecifiers();
43-
if (this.type === tt._with) {
44-
this.next();
45-
node.injections = this.parseImportSpecifiers();
46-
}
47-
this.expectContextual("from");
48-
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected();
49-
return this.finishNode(node, "ImportDeclaration");
50-
}
51-
parseImportSpecifiers() {
52-
const nodes = [];
53-
let first = true;
54-
this.expect(tt.braceL);
55-
while (!this.eat(tt.braceR)) {
56-
if (first) {
57-
first = false;
58-
} else {
59-
this.expect(tt.comma);
60-
if (this.afterTrailingComma(tt.braceR)) break;
61-
}
62-
const node = this.startNode();
63-
node.view = this.eatContextual("viewof");
64-
if (!node.view) node.mutable = this.eatContextual("mutable");
65-
node.imported = this.parseIdent();
66-
if (this.eatContextual("as")) {
67-
node.local = this.parseIdent();
68-
} else {
69-
this.checkUnreserved(node.imported);
70-
node.local = node.imported;
71-
}
72-
this.checkLVal(node.local, "let");
73-
nodes.push(this.finishNode(node, "ImportSpecifier"));
74-
}
75-
return nodes;
76-
}
77-
parseExprAtom() {
78-
return this.parseMaybeKeywordExpression("viewof", "ViewExpression")
79-
|| this.parseMaybeKeywordExpression("mutable", "MutableExpression")
80-
|| super.parseExprAtom.apply(this, arguments);
81-
}
82-
parseTopLevel(node) {
83-
const lookahead = CellParser.tokenizer(this.input);
84-
let token = lookahead.getToken();
85-
let body = null;
86-
let id = null;
87-
88-
this.strict = true;
89-
this.enterScope(SCOPE_FUNCTION | SCOPE_ASYNC | SCOPE_GENERATOR);
90-
91-
// An import?
92-
if (token.type === tt._import) {
93-
body = this.parseImport(this.startNode());
94-
}
95-
96-
// A non-empty cell?
97-
else if (token.type !== tt.eof) {
98-
99-
// A named cell?
100-
if (token.type === tt.name) {
101-
if (token.value === "viewof" || token.value === "mutable") {
102-
token = lookahead.getToken();
103-
if (token.type !== tt.name) {
104-
lookahead.unexpected();
105-
}
106-
}
107-
token = lookahead.getToken();
108-
if (token.type === tt.eq) {
109-
id = this.parseMaybeKeywordExpression("viewof", "ViewExpression")
110-
|| this.parseMaybeKeywordExpression("mutable", "MutableExpression")
111-
|| this.parseIdent();
112-
token = lookahead.getToken();
113-
this.expect(tt.eq);
114-
}
115-
}
116-
117-
// A block?
118-
if (token.type === tt.braceL) {
119-
body = this.parseBlock();
120-
}
121-
122-
// An expression?
123-
// Possibly a function or class declaration?
124-
else {
125-
body = this.parseExpression();
126-
if (id === null && (body.type === "FunctionExpression" || body.type === "ClassExpression")) {
127-
id = body.id;
128-
}
129-
}
130-
}
131-
132-
this.expect(tt.eof);
133-
node.id = id;
134-
node.async = this.O_async;
135-
node.generator = this.O_generator;
136-
node.body = body;
137-
return this.finishNode(node, "Cell");
138-
}
139-
toAssignable(node) {
140-
return node.type === "MutableExpression" ? node : super.toAssignable.apply(this, arguments);
141-
}
142-
checkUnreserved(node) {
143-
if (node.name ==="viewof" || node.name === "mutable") {
144-
this.raise(node.start, `Unexpected keyword '${node.name}'`);
145-
}
146-
return super.checkUnreserved(node);
147-
}
148-
checkLVal(expr, bindingType, checkClashes) {
149-
return expr.type === "MutableExpression"
150-
? super.checkLVal.call(this, expr.id, bindingType, checkClashes)
151-
: super.checkLVal.apply(this, arguments);
152-
}
153-
unexpected(pos) {
154-
this.raise(pos != null ? pos : this.start, this.type === tt.eof ? "Unexpected end of input" : "Unexpected token");
155-
}
156-
parseMaybeKeywordExpression(keyword, type) {
157-
if (this.isContextual(keyword)) {
158-
const node = this.startNode();
159-
this.next();
160-
node.id = this.parseIdent();
161-
return this.finishNode(node, type);
162-
}
163-
}
164-
};
165-
}
1+
export {parseCell} from "./parse.js";
2+
export {findReferences} from "./references.js";

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.0.0",
44
"name": "acorn-observable",
55
"scripts": {
6-
"test": "tape -r esm 'index.test.js'",
6+
"test": "tape -r esm 'test/**/*-test.js'",
77
"precommit": "eslint . && yarn test"
88
},
99
"bin": {
@@ -17,7 +17,8 @@
1717
"cjs": true
1818
},
1919
"dependencies": {
20-
"acorn": "^6.0.4"
20+
"acorn": "^6.0.4",
21+
"acorn-walk": "^6.1.0"
2122
},
2223
"devDependencies": {
2324
"eslint": "^4.14.0",

parse.js

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import {tokTypes as tt, Parser} from "acorn";
2+
3+
const SCOPE_FUNCTION = 2;
4+
const SCOPE_ASYNC = 4;
5+
const SCOPE_GENERATOR = 8;
6+
const CellParser = Parser.extend(observable);
7+
8+
export function parseCell(input) {
9+
return CellParser.parse(input);
10+
}
11+
12+
function observable(Parser) {
13+
return class extends Parser {
14+
constructor(...options) {
15+
super(...options);
16+
this.O_function = 0;
17+
this.O_async = false;
18+
this.O_generator = false;
19+
}
20+
enterScope(flags) {
21+
if (flags & SCOPE_FUNCTION) ++this.O_function;
22+
return super.enterScope.apply(this, arguments);
23+
}
24+
exitScope() {
25+
if (this.currentScope() & SCOPE_FUNCTION) --this.O_function;
26+
return super.exitScope.apply(this, arguments);
27+
}
28+
parseForIn(node) {
29+
if (this.O_function === 1 && node.await) this.O_async = true;
30+
return super.parseForIn.apply(this, arguments);
31+
}
32+
parseAwait() {
33+
if (this.O_function === 1) this.O_async = true;
34+
return super.parseAwait.apply(this, arguments);
35+
}
36+
parseYield() {
37+
if (this.O_function === 1) this.O_generator = true;
38+
return super.parseYield.apply(this, arguments);
39+
}
40+
parseImport(node) {
41+
this.next();
42+
node.specifiers = this.parseImportSpecifiers();
43+
if (this.type === tt._with) {
44+
this.next();
45+
node.injections = this.parseImportSpecifiers();
46+
}
47+
this.expectContextual("from");
48+
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected();
49+
return this.finishNode(node, "ImportDeclaration");
50+
}
51+
parseImportSpecifiers() {
52+
const nodes = [];
53+
let first = true;
54+
this.expect(tt.braceL);
55+
while (!this.eat(tt.braceR)) {
56+
if (first) {
57+
first = false;
58+
} else {
59+
this.expect(tt.comma);
60+
if (this.afterTrailingComma(tt.braceR)) break;
61+
}
62+
const node = this.startNode();
63+
node.view = this.eatContextual("viewof");
64+
if (!node.view) node.mutable = this.eatContextual("mutable");
65+
node.imported = this.parseIdent();
66+
if (this.eatContextual("as")) {
67+
node.local = this.parseIdent();
68+
} else {
69+
this.checkUnreserved(node.imported);
70+
node.local = node.imported;
71+
}
72+
this.checkLVal(node.local, "let");
73+
nodes.push(this.finishNode(node, "ImportSpecifier"));
74+
}
75+
return nodes;
76+
}
77+
parseExprAtom() {
78+
return this.parseMaybeKeywordExpression("viewof", "ViewExpression")
79+
|| this.parseMaybeKeywordExpression("mutable", "MutableExpression")
80+
|| super.parseExprAtom.apply(this, arguments);
81+
}
82+
parseTopLevel(node) {
83+
const lookahead = CellParser.tokenizer(this.input);
84+
let token = lookahead.getToken();
85+
let body = null;
86+
let id = null;
87+
88+
this.strict = true;
89+
this.enterScope(SCOPE_FUNCTION | SCOPE_ASYNC | SCOPE_GENERATOR);
90+
91+
// An import?
92+
if (token.type === tt._import) {
93+
body = this.parseImport(this.startNode());
94+
}
95+
96+
// A non-empty cell?
97+
else if (token.type !== tt.eof) {
98+
99+
// A named cell?
100+
if (token.type === tt.name) {
101+
if (token.value === "viewof" || token.value === "mutable") {
102+
token = lookahead.getToken();
103+
if (token.type !== tt.name) {
104+
lookahead.unexpected();
105+
}
106+
}
107+
token = lookahead.getToken();
108+
if (token.type === tt.eq) {
109+
id = this.parseMaybeKeywordExpression("viewof", "ViewExpression")
110+
|| this.parseMaybeKeywordExpression("mutable", "MutableExpression")
111+
|| this.parseIdent();
112+
token = lookahead.getToken();
113+
this.expect(tt.eq);
114+
}
115+
}
116+
117+
// A block?
118+
if (token.type === tt.braceL) {
119+
body = this.parseBlock();
120+
}
121+
122+
// An expression?
123+
// Possibly a function or class declaration?
124+
else {
125+
body = this.parseExpression();
126+
if (id === null && (body.type === "FunctionExpression" || body.type === "ClassExpression")) {
127+
id = body.id;
128+
}
129+
}
130+
}
131+
132+
this.expect(tt.eof);
133+
node.id = id;
134+
node.async = this.O_async;
135+
node.generator = this.O_generator;
136+
node.body = body;
137+
return this.finishNode(node, "Cell");
138+
}
139+
toAssignable(node) {
140+
return node.type === "MutableExpression" ? node : super.toAssignable.apply(this, arguments);
141+
}
142+
checkUnreserved(node) {
143+
if (node.name ==="viewof" || node.name === "mutable") {
144+
this.raise(node.start, `Unexpected keyword '${node.name}'`);
145+
}
146+
return super.checkUnreserved(node);
147+
}
148+
checkLVal(expr, bindingType, checkClashes) {
149+
return expr.type === "MutableExpression"
150+
? super.checkLVal.call(this, expr.id, bindingType, checkClashes)
151+
: super.checkLVal.apply(this, arguments);
152+
}
153+
unexpected(pos) {
154+
this.raise(pos != null ? pos : this.start, this.type === tt.eof ? "Unexpected end of input" : "Unexpected token");
155+
}
156+
parseMaybeKeywordExpression(keyword, type) {
157+
if (this.isContextual(keyword)) {
158+
const node = this.startNode();
159+
this.next();
160+
node.id = this.parseIdent();
161+
return this.finishNode(node, type);
162+
}
163+
}
164+
};
165+
}

0 commit comments

Comments
 (0)