Disorder!

master
Paul Loyd 2017-11-03 00:38:55 +03:00
parent e2c0e79751
commit c13eafb9b3
3 changed files with 92 additions and 19 deletions

View File

@ -7,13 +7,13 @@ const pathlib = require('path');
const extractors = require('./extractors');
const Command = require('./commands');
const Scope = require('./scope');
const {log} = require('./utils');
const {consume, log} = require('./utils');
class Collector {
constructor(parser) {
this.parser = parser;
this.schemas = [];
this.scope = null;
this.waiters = Object.create(null);
}
collect(path) {
@ -25,13 +25,13 @@ class Collector {
// TODO: customize it.
const namespace = pathToNamespace(path);
this.scope = global.extend(namespace);
const scope = global.extend(namespace);
this._visit(ast.program);
this._visit(ast.program, scope);
}
// Given the AST output of babylon parse, walk through in a depth-first order.
_visit(root) {
_visit(root, scope) {
let stack;
let parent;
let keys = [];
@ -55,7 +55,9 @@ class Collector {
const {type} = node;
if (type && type in extractors) {
this._collect(node);
const task = this._collect(node, scope);
this._schedule(task);
continue;
}
@ -68,11 +70,11 @@ class Collector {
} while (stack);
}
_collect(node) {
* _collect(node, scope) {
const extractor = extractors[node.type];
if (!extractor) {
this._visit(node);
this._visit(node, scope);
return null;
}
@ -92,37 +94,78 @@ class Collector {
} else if (value instanceof Command) {
switch (value.name) {
case 'provide':
assert(this.scope);
this.scope.addSchema(value.data);
this.schemas.push(value.data);
const schema = value.data;
scope.addSchema(schema);
this.schemas.push(schema);
this._wakeup(schema.name);
break;
case 'query':
const info = this.scope.query(value.data);
let info;
while (!(info = scope.query(value.data))) {
yield value.data;
}
// TODO: support externals.
assert(info.type, 'local');
result = info.schema;
break;
case 'enter':
this.scope = this.scope.extend();
scope = scope.extend();
break;
case 'exit':
assert(this.scope);
this.scope = this.scope.parent;
assert(scope.parent);
scope = scope.parent;
break;
case 'namespace':
result = this.scope.namespace;
result = scope.namespace;
break;
default:
assert(false);
}
} else if (Array.isArray(value)) {
result = value.map(val => this._collect(val));
result = [];
for (const val of value) {
result.push(yield* this._collect(val, scope));
}
} else {
assert(isNode(value));
result = this._collect(value);
result = yield* this._collect(value, scope);
}
}
}
_schedule(task) {
const {done, value} = task.next();
if (done) {
return;
}
const waiters = this.waiters[value] = this.waiters[value] || [];
waiters.push(task);
}
_wakeup(name) {
if (!this.waiters[name]) {
return;
}
const tasks = this.waiters[name];
delete this.waiters[name];
for (const task of tasks) {
this._schedule(task);
}
}
}
function isNode(it) {

View File

@ -3,7 +3,7 @@
const assert = require('assert');
const {provide, query, enter, exit, namespace} = require('./commands');
const {partition, log} = require('./utils');
const {partition} = require('./utils');
const extractors = {
* TypeAlias(node) {

30
tests/disorder.js Normal file
View File

@ -0,0 +1,30 @@
type X = {
y: Y,
};
type Y = {
z: Z,
};
type Z = string;
// ###
[
{
type: 'string',
name: 'Z',
namespace: 'disorder',
},
{
type: 'record',
name: 'Y',
namespace: 'disorder',
fields: [{name: 'z', type: 'Z'}],
},
{
type: 'record',
name: 'X',
namespace: 'disorder',
fields: [{name: 'y', type: 'Y'}],
},
]