flow2schema/src/scope.js

158 lines
3.7 KiB
JavaScript
Raw Normal View History

2017-11-18 12:38:34 +03:00
import type {Node} from '@babel/types';
import {invariant} from './utils';
import type Module from './module';
import type {Schema} from './schema';
import type {Query, Template, TemplateParam, ExternalInfo} from './query';
2017-11-02 20:21:56 +03:00
2017-11-16 15:17:15 +03:00
export default class Scope {
2017-11-18 12:38:34 +03:00
+parent: ?Scope;
+module: ?Module;
+scopeId: ?number;
_entries: Map<string, Query>;
static global(schemas: Schema[]) {
2017-11-04 22:13:08 +03:00
const global = new Scope(null, null);
for (const schema of schemas) {
2017-11-09 12:10:21 +03:00
global.addDefinition(schema, false);
2017-11-04 22:13:08 +03:00
}
return global;
2017-11-02 23:09:18 +03:00
}
2017-11-18 12:38:34 +03:00
constructor(parent: ?Scope, module: ?Module) {
2017-11-02 21:30:51 +03:00
this.parent = parent;
2017-11-04 22:13:08 +03:00
this.module = module;
this.scopeId = module && module.generateScopeId();
2017-11-18 12:38:34 +03:00
this._entries = new Map;
2017-11-02 23:09:18 +03:00
}
2017-11-18 12:38:34 +03:00
get namespace(): string {
invariant(this.module);
2017-11-04 22:13:08 +03:00
let namespace = this.module.namespace;
// Nested scopes.
2017-11-18 12:38:34 +03:00
if (this.scopeId != null && this.scopeId > 0) {
2017-11-04 22:13:08 +03:00
namespace += '._' + this.scopeId;
2017-11-02 23:09:18 +03:00
}
2017-11-04 22:13:08 +03:00
return namespace;
}
2017-11-18 12:38:34 +03:00
extend(module: ?Module = null): Scope {
2017-11-04 22:13:08 +03:00
return new Scope(this, module || this.module);
2017-11-02 20:21:56 +03:00
}
2017-11-18 12:38:34 +03:00
addDeclaration(name: string, node: Node, params: TemplateParam[]) {
invariant(!this._entries.has(name));
2017-11-13 21:11:18 +03:00
2017-11-18 12:38:34 +03:00
const entry = params.length > 0 ? {
type: 'template',
name,
params,
instances: [],
node,
scope: this,
} : {
type: 'declaration',
2017-11-09 12:10:21 +03:00
name,
node,
scope: this,
2017-11-13 21:11:18 +03:00
};
2017-11-18 12:38:34 +03:00
this._entries.set(name, entry);
2017-11-13 21:11:18 +03:00
}
2017-11-18 12:38:34 +03:00
addInstance(name: string, schema: Schema, params: Schema[]) {
const template = this._entries.get(name);
2017-11-13 21:11:18 +03:00
2017-11-18 12:38:34 +03:00
invariant(template);
invariant(template.type === 'template');
2017-11-13 21:11:18 +03:00
template.instances.push({params, schema});
2017-11-09 12:10:21 +03:00
}
2017-11-18 12:38:34 +03:00
addDefinition(schema: Schema, declared: boolean) {
const decl = this._entries.get(schema.name);
2017-11-13 21:11:18 +03:00
2017-11-09 12:10:21 +03:00
if (declared) {
2017-11-18 12:38:34 +03:00
invariant(decl);
invariant(decl.type === 'declaration');
2017-11-09 12:10:21 +03:00
} else {
2017-11-18 12:38:34 +03:00
invariant(!decl);
2017-11-09 12:10:21 +03:00
}
2017-11-18 12:38:34 +03:00
this._entries.set(schema.name, {
2017-11-09 12:10:21 +03:00
type: 'definition',
schema,
scope: this,
});
2017-11-02 20:21:56 +03:00
}
2017-11-18 12:38:34 +03:00
addImport(info: ExternalInfo) {
invariant(!this._entries.has(info.local));
2017-11-04 22:13:08 +03:00
2017-11-18 12:38:34 +03:00
this._entries.set(info.local, {
2017-11-09 12:10:21 +03:00
type: 'external',
info,
scope: this,
});
2017-11-04 22:13:08 +03:00
}
2017-11-18 12:38:34 +03:00
addExport(name: string, reference: string) {
invariant(this.module);
2017-11-04 22:13:08 +03:00
2017-11-09 12:10:21 +03:00
this.module.addExport(name, this, reference);
2017-11-04 22:13:08 +03:00
}
2017-11-18 12:38:34 +03:00
resolve(path: string): string {
invariant(this.module);
2017-11-04 22:13:08 +03:00
return this.module.resolve(path);
2017-11-02 20:21:56 +03:00
}
2017-11-18 12:38:34 +03:00
query(name: string, params: Schema[]): Query {
const entry = this._entries.get(name);
2017-11-02 20:21:56 +03:00
2017-11-13 21:11:18 +03:00
if (entry && entry.type === 'template') {
const augmented = entry.params.map((p, i) => params[i] || p.default);
const schema = findInstance(entry, augmented);
if (schema) {
return {
type: 'definition',
schema,
scope: entry.scope,
};
}
}
if (entry) {
return entry;
2017-11-02 20:21:56 +03:00
}
if (this.parent) {
2017-11-18 12:38:34 +03:00
return this.parent.query(name, params);
2017-11-02 20:21:56 +03:00
}
2017-11-04 22:13:08 +03:00
return {
type: 'unknown',
};
2017-11-02 20:21:56 +03:00
}
2017-11-04 22:13:08 +03:00
}
2017-11-18 12:38:34 +03:00
function findInstance(template: Template, queried: Schema[]): ?Schema {
2017-11-13 21:11:18 +03:00
for (const {schema, params} of template.instances) {
// TODO: compare complex structures.
const same = params.every((p, i) => p === queried[i]);
if (same) {
return schema;
}
}
return null;
}