Introduce intermediate representation

master
Paul Loyd 2017-11-28 16:35:28 +03:00
parent 777d8a0acd
commit 317f8e6e1c
46 changed files with 903 additions and 1029 deletions

View File

@ -16,3 +16,7 @@ module.use_strict=true
munge_underscores=true
include_warnings=true
unsafe.enable_getters_and_setters=true
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
suppress_comment= \\(.\\|\n\\)*\\$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowIssue

View File

@ -5,43 +5,6 @@
[![Windows Build](https://ci.appveyor.com/api/projects/status/github/loyd/flow2schema?branch=master&svg=true)](https://ci.appveyor.com/project/loyd/flow2schema)
[![Coverage Status](https://coveralls.io/repos/github/loyd/flow2schema/badge.svg?branch=master)](https://coveralls.io/r/loyd/flow2schema?branch=master)
Currently avro is the only supported target.
## Example
Input (`$ cat example.js`):
```javascript
export interface Foo {
foo: string,
// $avro long
bar: number,
opt: ?number,
baz: 'one' | 'two',
mix: 'one' | 'two' | number,
}
```
Output (`$ ./bin/flow2schema example.js`):
```json
[
{
"type": "record",
"fields": [
{"name": "foo", "type": "string"},
{"name": "bar", "type": "double"},
{"name": "opt", "type": ["null", "double"]},
{"name": "baz", "type": {"type": "enum", "symbols": ["one", "two"]}},
{
"name": "mix",
"type": ["double", {"type": "enum", "symbols": ["one", "two"]}]
}
],
"name": "Foo",
"namespace": "example"
}
]
```
## TODO
* Complete generics support.
* Errors and warnings.

26
declarations/wu.js Normal file
View File

@ -0,0 +1,26 @@
declare module 'wu' {
declare type DeepIterable<U> = Iterable<U | DeepIterable<U>>;
declare type Flat<T> = $Call<<U>(Iterable<U> | U) => U, T>;
declare type DeepFlat<T> = $Call<<U>(DeepIterable<U> | U) => U, T>;
declare export default class Wu<T> {
static <T>(Iterable<T>): Wu<T>;
tap(T => mixed): Wu<T>;
map<U>(T => U): Wu<U>;
filter(): Wu<$NonMaybeType<T>>;
filter(T => boolean): Wu<T>;
flatten(): Wu<DeepFlat<T>>;
flatten(true): Wu<DeepFlat<T>>;
flatten(false): Wu<Flat<T>>;
find(T => boolean): T | void;
pluck<K: $Keys<T>>(K): Wu<$ElementType<T, K>>;
toArray(): T[];
}
}

View File

@ -27,7 +27,8 @@
"babylon": "^7.0.0-beta.32",
"json-stringify-pretty-compact": "^1.0.4",
"optimist": "^0.6.1",
"resolve": "^1.5.0"
"resolve": "^1.5.0",
"wu": "^2.1.0"
},
"devDependencies": {
"@babel/cli": "^7.0.0-beta.32",

View File

@ -15,9 +15,9 @@ function run(path: string) {
}
try {
const {schemas} = collect(path);
const {types} = collect(path);
console.log(stringify(schemas, {maxLength: 100}));
console.log(stringify(types, {maxLength: 100}));
} catch (ex) {
console.error(ex.message);
console.error(ex.stack);

View File

@ -10,28 +10,24 @@ import declarationGroup from './declarations';
import Module from './module';
import Scope from './scope';
import Context from './context';
import {invariant, get, map} from './utils';
import {invariant} from './utils';
import type Parser from './parser';
import type {Schema, Type} from './schema';
type InstanceParam = {
name: string,
value: ?Type,
};
import type {Type, TypeId} from './types';
import type {TemplateParam} from './query';
const VISITOR = Object.assign({}, definitionGroup, declarationGroup);
export default class Collector {
+root: string;
+parser: Parser;
+schemas: Schema[];
+types: Type[];
_modules: Map<string, Module>;
_global: Scope;
constructor(parser: Parser, root: string = '.') {
this.root = root;
this.parser = parser;
this.schemas = [];
this.types = [];
this._modules = new Map;
this._global = Scope.global(globals);
}
@ -51,9 +47,9 @@ export default class Collector {
const ast = this.parser.parse(code);
// TODO: customize it.
const namespace = pathToNamespace(pathlib.relative('.', path));
const id = pathToId(path);
module = new Module(path, namespace);
module = new Module(id, path);
const scope = this._global.extend(module);
@ -66,7 +62,7 @@ export default class Collector {
}
}
_freestyle(root: Node, scope: Scope, params: InstanceParam[]) {
_freestyle(root: Node, scope: Scope, params: TemplateParam[]) {
const ctx = new Context(this, scope, params);
const iter = traverse(root);
@ -88,7 +84,7 @@ export default class Collector {
let result = scope.query(name, params);
// TODO: warning.
invariant(result.type !== 'unknown');
invariant(result.kind !== 'unknown');
// Resulting scope is always the best choice for waiting.
scope = result.scope;
@ -96,7 +92,7 @@ export default class Collector {
// It's only valid the sequence: E*[CT]?F,
// where E - external, C - declaration, T - template, F - definition.
switch (result.type) {
switch (result.kind) {
case 'external':
const modulePath = scope.resolve(result.info.path);
@ -110,12 +106,12 @@ export default class Collector {
result = module.query(imported, params);
if (result.type === 'definition') {
return result.schema;
if (result.kind === 'definition') {
return result.type;
}
// TODO: reexports.
invariant(result.type === 'declaration' || result.type === 'template');
invariant(result.kind === 'declaration' || result.kind === 'template');
scope = result.scope;
name = result.name;
@ -125,26 +121,26 @@ export default class Collector {
case 'template':
const tmplParams = [];
if (result.type === 'template') {
if (result.kind === 'template') {
for (const [i, p] of result.params.entries()) {
tmplParams.push({
name: p.name,
value: params[i] === undefined ? p.default : params[i],
value: params[i] === undefined ? p.value : params[i],
});
}
}
invariant(result.type === 'declaration' || result.type === 'template');
invariant(result.kind === 'declaration' || result.kind === 'template');
this._freestyle(result.node, scope, tmplParams);
result = scope.query(name, params);
invariant(result.type === 'definition');
invariant(result.kind === 'definition');
// Fallthrough.
case 'definition':
return result.schema;
return result.type;
}
invariant(false);
@ -157,14 +153,14 @@ export default class Collector {
}
}
function pathToNamespace(path: string): string {
const pathObj = pathlib.parse(path);
function pathToId(path: string): TypeId {
const relPath = pathlib.relative('.', path);
const pathObj = pathlib.parse(relPath);
return pathlib.format({
dir: pathObj.dir,
name: pathObj.name,
})
// TODO: replace invalid chars.
.split(pathlib.sep)
.join('.');
.split(pathlib.sep);
}

View File

@ -1,31 +1,23 @@
import wu from 'wu';
import type {Node} from '@babel/types';
import Scope from './scope';
import Collector from './collector';
import {invariant, get, map} from './utils';
import type {Schema, Type, ComplexType} from './schema';
import {invariant} from './utils';
import type {Type} from './types';
import type {TemplateParam, ExternalInfo} from './query';
type InstanceParam = {
name: string,
value: ?Type,
};
export default class Context {
_collector: Collector;
_scope: Scope;
_params: InstanceParam[];
_params: TemplateParam[];
constructor(collector: Collector, scope: Scope, params: InstanceParam[]) {
constructor(collector: Collector, scope: Scope, params: TemplateParam[]) {
this._collector = collector;
this._scope = scope;
this._params = params;
}
get namespace(): string {
return this._scope.namespace;
}
freestyle(node: Node) {
this._collector._freestyle(node, this._scope, this._params);
}
@ -34,26 +26,18 @@ export default class Context {
this._scope.addDeclaration(name, node, params || []);
}
define(name: string, type: ComplexType, declared: boolean = true): Schema {
// TODO: dirty support for intersections.
const _name = type.name != null ? type.name : name;
const schema: $FlowFixMe = Object.assign({}, type, {
name,
namespace: this._scope.namespace,
});
define(name: string, type: Type, declared: boolean = true): Type {
if (declared && this._params.length > 0) {
schema.name = generateGenericName(name, this._params);
const params = wu(this._params).pluck('value').toArray();
this._scope.addInstance(name, schema, map(this._params, get('value')));
this._scope.addInstance(name, type, params);
} else {
this._scope.addDefinition(schema, declared);
this._scope.addDefinition(name, type, declared);
}
this._collector.schemas.push(schema);
this._collector.types.push(type);
return schema;
return type;
}
external(external: ExternalInfo) {
@ -65,7 +49,7 @@ export default class Context {
}
query(name: string, params: ?(?Type)[]): Type {
const param = this._params.find(p => p.name === name);
const param = wu(this._params).find(p => p.name === name);
if (param) {
// TODO: warning about missing param.
@ -86,14 +70,3 @@ export default class Context {
this._scope = this._scope.parent;
}
}
function generateGenericName(base: string, params: InstanceParam[]): string {
let name = base + '_';
for (const {value} of params) {
invariant(typeof value === 'string');
name += '_' + value;
}
return name;
}

View File

@ -1,4 +1,6 @@
// flow#5376.
import wu from 'wu';
// @see flow#5376.
import type {
Block, ClassDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, Identifier,
ImportDeclaration, ImportDefaultSpecifier, ImportSpecifier, InterfaceDeclaration,
@ -12,7 +14,7 @@ import {
isStringLiteral, isTypeAlias, isVariableDeclaration,
} from '@babel/types';
import {invariant, map, filter} from './utils';
import {invariant} from './utils';
import Context from './context';
import type {ExternalInfo, TemplateParam} from './query';
@ -146,7 +148,10 @@ function extractCommonjsNamedExternals<+T: Node>(nodes: T[], path: string): Exte
path,
});
return map(filter(nodes, pred), make);
return wu(nodes)
.filter(pred)
.map(make)
.toArray();
}
/*
@ -201,7 +206,7 @@ function processDeclaration(ctx: Context, node: Declaration) {
// TODO: do it only for "all"-mode.
if (isClassDeclaration(node)) {
const methods = filter(node.body.body, isClassMethod);
const methods = wu(node.body.body).filter(isClassMethod).toArray();
for (const method of methods) {
ctx.freestyle(method);
@ -214,10 +219,13 @@ function processDeclaration(ctx: Context, node: Declaration) {
}
function extractTemplateParams(node: TypeParameterDeclaration): TemplateParam[] {
return map(node.params, param => ({
name: param.name,
default: null,
}));
return wu(node.params)
.map(param => ({
name: param.name,
// TODO: default params.
value: null,
}))
.toArray();
}
export default {

View File

@ -1,9 +1,11 @@
// flow#5376.
import wu from 'wu';
// @see flow#5376.
import type {
ArrayTypeAnnotation, ClassDeclaration, ClassProperty, Comment, FlowTypeAnnotation,
GenericTypeAnnotation, InterfaceDeclaration, IntersectionTypeAnnotation, TypeAlias,
UnionTypeAnnotation, NullableTypeAnnotation, ObjectTypeIndexer, ObjectTypeProperty,
StringLiteralTypeAnnotation,
StringLiteralTypeAnnotation, ObjectTypeAnnotation,
} from '@babel/types';
import {
@ -13,98 +15,104 @@ import {
import Context from './context';
import type {
Schema, Type, ReferenceType, ComplexType, RecordType, FixedType,
FieldType, ArrayType, MapType, UnionType, EnumType,
} from './schema';
Type, RecordType, Field, ArrayType, TupleType, MapType, UnionType, IntersectionType,
MaybeType, NumberType, StringType, BooleanType, LiteralType, ReferenceType,
} from './types';
import {isPrimitiveType, isComplexType, makeFullname, mergeTypes} from './schema';
import {invariant, compose, last, get, partition, map, filter, filterMap, compact} from './utils';
import {extractPragmas} from './pragmas';
import {invariant} from './utils';
function processTypeAlias(ctx: Context, node: TypeAlias) {
let type = makeType(ctx, node.right);
const {name} = node.id;
const type = makeType(ctx, node.right);
// TODO: support function aliases.
invariant(type != null);
// TODO: support top-level unions.
invariant(!(type instanceof Array));
invariant(type);
if (typeof type === 'string') {
type = {type: type};
}
ctx.define(node.id.name, type);
ctx.define(name, type);
}
// TODO: type params.
function processInterfaceDeclaration(ctx: Context, node: InterfaceDeclaration) {
let type = makeType(ctx, node.body);
const {name} = node.id;
const type = makeType(ctx, node.body);
invariant(type != null)
invariant(isComplexType(type));
invariant(type);
if (node.extends.length > 0) {
const types = [];
for (const extend of node.extends) {
const name = extend.id.name;
const type = ctx.query(name);
invariant(isComplexType(type));
types.push((type: $FlowFixMe));
}
types.push((type: $FlowFixMe));
[, type] = mergeTypes(types);
if (node.extends.length === 0) {
ctx.define(name, type);
return;
}
ctx.define(node.id.name, type);
const parts = [];
for (const extend of node.extends) {
const {name} = extend.id;
const type = ctx.query(name);
invariant(type.id);
parts.push({
kind: 'reference',
to: type.id,
});
}
parts.push(type);
ctx.define(name, {
kind: 'intersection',
parts,
});
}
// TODO: type params.
function processClassDeclaration(ctx: Context, node: ClassDeclaration) {
const props: $FlowFixMe = filter(node.body.body, isClassProperty);
const props: $FlowFixMe = wu(node.body.body).filter(isClassProperty).toArray();
let type = makeRecord(ctx, props);
const {name} = node.id;
const type = makeRecord(ctx, props);
if (node.superClass) {
// TODO: warning about expressions here.
invariant(isIdentifier(node.superClass));
const {name} = node.superClass;
const superSchema = ctx.query(name);
invariant(isComplexType(superSchema));
[, type] = mergeTypes([(superSchema: $FlowFixMe), (type: $FlowFixMe)]);
if (!node.superClass) {
ctx.define(name, type);
return;
}
ctx.define(node.id.name, type);
// TODO: warning about expressions here.
invariant(isIdentifier(node.superClass));
const base = ctx.query(node.superClass.name);
invariant(base.id);
const baseRef = {
kind: 'reference',
to: base.id,
};
ctx.define(name, {
kind: 'intersection',
parts: [baseRef, type],
});
}
function makeType(ctx: Context, node: FlowTypeAnnotation): ?Type {
switch (node.type) {
case 'NullLiteralTypeAnnotation':
return 'null';
return {kind: 'literal', value: null};
case 'BooleanTypeAnnotation':
return 'boolean';
return {kind: 'boolean'};
case 'NumberTypeAnnotation':
return 'double';
return {kind: 'number', repr: 'f64'};
case 'StringTypeAnnotation':
return 'string';
return {kind: 'string'};
case 'TypeAnnotation':
return makeType(ctx, node.typeAnnotation);
case 'NullableTypeAnnotation':
return makeNullable(ctx, node);
return makeMaybe(ctx, node);
case 'ObjectTypeAnnotation':
const map = makeMap(ctx, node.indexers);
const record = makeRecord(ctx, node.properties);
// TODO: warning about this.
invariant(!map || record.fields.length === 0);
return map || record;
return makeComplexType(ctx, node);
case 'ArrayTypeAnnotation':
return makeArrayType(ctx, node);
case 'UnionTypeAnnotation':
@ -112,50 +120,85 @@ function makeType(ctx: Context, node: FlowTypeAnnotation): ?Type {
case 'IntersectionTypeAnnotation':
return makeIntersection(ctx, node);
case 'StringLiteralTypeAnnotation':
return makeEnum(node);
return {kind: 'literal', value: node.value};
case 'GenericTypeAnnotation':
return makeReference(ctx, node);
case 'FunctionTypeAnnotation':
return null;
default:
invariant(false, `Unknown type: ${node.type}`);
invariant(false, `Unknown node: ${node.type}`);
}
}
function makeNullable(ctx: Context, node: NullableTypeAnnotation): ?UnionType {
function makeMaybe(ctx: Context, node: NullableTypeAnnotation): ?MaybeType {
const type = makeType(ctx, node.typeAnnotation);
if (type == null) {
if (!type) {
return null;
}
return ['null', type];
return {
kind: 'maybe',
value: type,
};
}
function makeComplexType(ctx: Context, node: ObjectTypeAnnotation): Type {
const maps = wu(node.indexers)
.map(node => makeMap(ctx, node))
.filter()
.toArray();
const record = makeRecord(ctx, node.properties);
if (maps.length === 1 && record.fields.length === 0) {
return maps[0];
}
if (maps.length === 0) {
return record;
}
const parts = record.fields.length > 0 ? [record, ...maps] : maps;
return {
kind: 'intersection',
parts,
};
}
function makeRecord<T: ObjectTypeProperty | ClassProperty>(ctx: Context, nodes: T[]): RecordType {
const fields = compact(map(nodes, node => makeField(ctx, node)));
const fields = wu(nodes)
.map(node => makeField(ctx, node))
.filter()
.toArray();
return {
type: 'record',
kind: 'record',
fields,
};
}
function makeField(ctx: Context, node: ObjectTypeProperty | ClassProperty): ?FieldType {
// $FlowFixMe
if (node.static) {
function makeField(ctx: Context, node: ObjectTypeProperty | ClassProperty): ?Field {
if ((node: $FlowIssue<3129>).static) {
return null;
}
let type = null;
if (node.leadingComments) {
const pragmas = extractPragmas(node.leadingComments);
const pragma = (wu: $FlowIssue<4431>)(node.leadingComments)
.pluck('value')
.map(extractPragmas)
.flatten()
.find(pragma => pragma.kind === 'type');
type = last(pragmas);
if (pragma) {
type = pragma.value;
}
}
if (type == null) {
if (!type) {
const value = isObjectTypeProperty(node) ? node.value : node.typeAnnotation;
// TODO: no type annotation for the class property.
@ -164,17 +207,10 @@ function makeField(ctx: Context, node: ObjectTypeProperty | ClassProperty): ?Fie
type = makeType(ctx, value);
}
if (type == null) {
if (!type) {
return null;
}
if (isComplexType(type) && type.type === 'record') {
const name = (type: $FlowFixMe).name;
ctx.define(name, type, false);
type = name;
}
// TODO: support optional fields.
// TODO: warning about computed properties.
invariant(isObjectTypeProperty(node) || !node.computed);
@ -182,30 +218,22 @@ function makeField(ctx: Context, node: ObjectTypeProperty | ClassProperty): ?Fie
return {
name: node.key.name,
type,
value: type,
required: node.optional == null || !node.optional,
};
}
function makeMap(ctx: Context, nodes: ObjectTypeIndexer[]): ?MapType {
if (nodes.length === 0) {
return null;
}
// TODO: what to do in this case?
invariant(nodes.length === 1);
const node = nodes[0];
invariant(makeType(ctx, node.key) === 'string');
function makeMap(ctx: Context, node: ObjectTypeIndexer): ?MapType {
const keys = makeType(ctx, node.key);
const values = makeType(ctx, node.value);
if (values == null) {
if (!(keys && values)) {
return null;
}
return {
type: 'map',
kind: 'map',
keys,
values,
};
}
@ -213,151 +241,68 @@ function makeMap(ctx: Context, nodes: ObjectTypeIndexer[]): ?MapType {
function makeArrayType(ctx: Context, node: ArrayTypeAnnotation): ?ArrayType {
const items = makeType(ctx, node.elementType);
if (items == null) {
if (!items) {
return null;
}
return {
type: 'array',
kind: 'array',
items,
};
}
function makeUnionType(ctx: Context, node: UnionTypeAnnotation): ?(UnionType | EnumType) {
// TODO: flatten variants.
// TODO: refactor it.
let [symbols, variants] = partition(node.types, isStringLiteralTypeAnnotation);
// $FlowFixMe
symbols = map(symbols, get('value'));
variants = compact(map(variants, node => makeType(ctx, node)));
if (symbols.length > 0) {
const enumeration: EnumType = {
type: 'enum',
symbols,
};
if (variants.length === 0) {
return enumeration;
}
variants.push(enumeration);
}
function makeUnionType(ctx: Context, node: UnionTypeAnnotation): ?UnionType {
const variants = wu(node.types)
.map(node => makeType(ctx, node))
.filter()
.toArray();
if (variants.length === 0) {
return null;
}
return variants;
}
function makeIntersection(ctx: Context, node: IntersectionTypeAnnotation): ?Type {
const types = [];
// TODO: refactor it.
for (const typeNode of node.types) {
const type = makeType(ctx, typeNode);
if (type == null) {
continue;
}
// TODO: support arbitrary types, not only references.
invariant(typeof type === 'string');
const queried = ctx.query(type);
invariant(isComplexType(queried));
types.push((queried: $FlowFixMe));
}
if (types.length === 0) {
return null;
}
const [name, intersection] = mergeTypes(types);
// TODO: dirty support for intersections.
(intersection: $FlowFixMe).name = name;
return intersection;
}
function makeEnum(node: StringLiteralTypeAnnotation): EnumType {
return {
type: 'enum',
symbols: [node.value],
kind: 'union',
variants,
};
}
function makeReference(ctx: Context, node: GenericTypeAnnotation): ReferenceType {
const {name} = node.id;
const params = node.typeParameters && map(node.typeParameters.params, n => makeType(ctx, n));
function makeIntersection(ctx: Context, node: IntersectionTypeAnnotation): ?Type {
// TODO: warning about nulls.
const parts = wu(node.types)
.map(node => makeType(ctx, node))
.filter()
.toArray();
const type = ctx.query(name, params);
if (typeof type === 'string') {
return type;
}
// TODO: generalized it.
if ((type: $FlowFixMe).$unwrap) {
invariant(typeof type.type === 'string');
return type.type;
}
invariant(isComplexType(type));
if (type.namespace === ctx.namespace) {
return (type: $FlowFixMe).name;
}
return makeFullname((type: $FlowFixMe));
}
function extractPragmas(comments: Comment[]): Type[] {
return filterMap(comments, compose(get('value'), extractPragma));
}
function extractPragma(text: string): ?Type {
const marker = '$avro ';
const value = text.trimLeft();
if (!value.startsWith(marker)) {
if (parts.length === 0) {
return null;
}
const pragma = value.slice(marker.length).trim();
if (parts.length === 1) {
return parts[0];
}
return parsePragma(pragma);
return {
kind: 'intersection',
parts,
};
}
function parsePragma(pragma: string): Type {
let [type, arg] = pragma.split(/\s+/);
function makeReference(ctx: Context, node: GenericTypeAnnotation): ?Type {
const {name} = node.id;
const params = node.typeParameters
&& wu(node.typeParameters.params).map(n => makeType(ctx, n)).toArray();
if (isPrimitiveType(type)) {
invariant(arg == null);
const type = ctx.query(name, params);
if (!type.id) {
return type;
}
if (type === 'fixed') {
arg = Number(arg);
invariant(Number.isInteger(arg));
return ({
type: 'fixed',
size: arg,
}: FixedType);
}
invariant(false);
return {
kind: 'reference',
to: type.id,
};
}
export default {

View File

@ -1,7 +1,7 @@
export default [
{
name: 'Buffer',
type: 'bytes',
$unwrap: true,
id: ['Buffer'],
kind: 'record',
fields: [],
},
];

View File

@ -2,24 +2,29 @@ import * as pathlib from 'path';
import * as resolve from 'resolve';
import type Scope from './scope';
import type {Schema, Type} from './schema';
import type {Type, TypeId} from './types';
import type {Query} from './query';
export default class Module {
+id: TypeId;
+path: string;
+namespace: string;
_scopeCount: number;
_exports: Map<?string, [Scope, string]>;
constructor(path: string, namespace: string) {
constructor(id: TypeId, path: string) {
this.id = id;
this.path = path;
this.namespace = namespace;
this._scopeCount = 0;
this._exports = new Map;
}
generateScopeId(): number {
return this._scopeCount++;
generateScopeId(): TypeId {
if (this._scopeCount === 0) {
++this._scopeCount;
return this.id;
}
return this.id.concat(String(this._scopeCount++));
}
addExport(name: ?string, scope: Scope, reference: string) {
@ -31,7 +36,7 @@ export default class Module {
if (!result) {
return {
type: 'unknown',
kind: 'unknown',
};
}

38
src/pragmas.js Normal file
View File

@ -0,0 +1,38 @@
import {invariant} from './utils';
import type {Type} from './types';
export type Pragma =
| TypePragma;
export type TypePragma = {
kind: 'type',
value: Type,
};
const PRAGMA_RE = /^\s*@repr\s+\{\s*(.+?)\s*\}\s*$/gm;
export function extractPragmas(text: string): Pragma[] {
const pragmas = [];
let match;
while ((match = PRAGMA_RE.exec(text))) {
const repr = match[1];
invariant(['i32', 'i64', 'u32', 'u64', 'f32', 'f64'].includes(repr));
const pragma = {
kind: 'type',
value: {
kind: 'number',
repr,
},
};
if (pragma) {
pragmas.push(pragma);
}
}
return pragmas;
}

View File

@ -1,7 +1,7 @@
import type {Node} from '@babel/types';
import type Scope from './scope';
import type {Schema, Type} from './schema';
import type {Type} from './types';
export type Query =
| Unknown
@ -11,18 +11,18 @@ export type Query =
| External;
export type Unknown = {
type: 'unknown',
kind: 'unknown',
};
export type Declaration = {
type: 'declaration',
kind: 'declaration',
name: string,
node: Node,
scope: Scope,
};
export type Template = {
type: 'template',
kind: 'template',
name: string,
params: TemplateParam[],
instances: Instance[],
@ -31,25 +31,25 @@ export type Template = {
};
export type Definition = {
type: 'definition',
schema: Schema,
kind: 'definition',
type: Type,
scope: Scope,
};
export type External = {
type: 'external',
kind: 'external',
info: ExternalInfo,
scope: Scope,
};
export type TemplateParam = {
name: string,
default: ?Type,
value: ?Type,
};
export type Instance = {
params: (?Type)[],
schema: Schema,
type: Type,
};
export type ExternalInfo = {

View File

@ -1,147 +0,0 @@
import {invariant} from './utils';
// @see flow#3912.
export type Schema =
| RecordType & Top
| EnumType & Top
| ArrayType & Top
| MapType & Top
// TODO: support top-level unions.
//| UnionType & Top
| FixedType & Top
| WrappedType & Top;
export type Top = {
name: string,
namespace?: string,
$unwrap?: boolean,
};
export type Type =
| ComplexType
| UnionType
| PrimitiveType
| ReferenceType;
export type WrappedType = {type: Type};
export type ComplexType =
| RecordType
| EnumType
| ArrayType
| MapType
// TODO: unions should be complex types.
//| UnionType & Top
| FixedType
| WrappedType;
export type PrimitiveType =
| 'null'
| 'boolean'
| 'int'
| 'long'
| 'float'
| 'double'
| 'bytes'
| 'string';
export type ReferenceType = string;
export type RecordType = {
type: 'record',
fields: FieldType[],
};
export type FieldType = {
name: string,
type: Type,
};
export type EnumType = {
type: 'enum',
symbols: string[],
};
export type ArrayType = {
type: 'array',
items: Type,
};
export type MapType = {
type: 'map',
values: Type,
};
export type UnionType = Type[];
export type FixedType = {
type: 'fixed',
size: number,
};
export function isPrimitiveType(type: Type): boolean %checks {
// Switch operator is not allowed in %checks functions.
return type === 'null'
|| type === 'int'
|| type === 'long'
|| type === 'float'
|| type === 'double'
|| type === 'bytes'
|| type === 'string'
|| type === 'boolean';
}
export function isComplexType(type: Type): boolean %checks {
return typeof type !== 'string' && !(type instanceof Array);
}
export function makeFullname(schema: Top): string {
invariant(schema.namespace != null);
return `${schema.namespace}.${schema.name}`;
}
export function mergeTypes<+T: ComplexType & {+name?: string}>(types: T[]): [string, ComplexType] {
invariant(types.length > 1);
if (types.length === 1) {
const type = types[0];
// TODO: anonymous?
invariant(type.name != null);
return [type.name, (type: $FlowFixMe)];
}
const map = new Map;
// TODO: overriding?
let name = '';
for (const type of types) {
// TODO: enums?
invariant(type.type === 'record');
for (const field of (type: $FlowFixMe).fields) {
const stored = map.get(field.name);
if (stored) {
// TODO: what about enums?
// TODO: improve checking.
invariant(stored.type === field.type);
continue;
}
map.set(field.name, field);
}
// TODO: anonymous?
name += '_' + (type.name != null ? type.name : 'unnamed');
}
const type = {
type: 'record',
fields: Array.from(map.values()),
};
return [name, (type: $FlowFixMe)];
}

View File

@ -1,46 +1,39 @@
import wu from 'wu';
import type {Node} from '@babel/types';
import {invariant} from './utils';
import {invariant, last} from './utils';
import type Module from './module';
import type {Schema, Type} from './schema';
import type {Type, TypeId} from './types';
import type {Query, Template, TemplateParam, ExternalInfo} from './query';
export default class Scope {
+id: TypeId;
+parent: ?Scope;
+module: ?Module;
+scopeId: ?number;
_entries: Map<string, Query>;
static global(schemas: Schema[]) {
static global(types: Type[]) {
const global = new Scope(null, null);
for (const schema of schemas) {
global.addDefinition(schema, false);
for (const type of types) {
invariant(type.id);
const name = last(type.id);
invariant(name != null);
global.addDefinition(name, type, false);
}
return global;
}
constructor(parent: ?Scope, module: ?Module) {
this.id = module ? module.generateScopeId() : [];
this.parent = parent;
this.module = module;
this.scopeId = module && module.generateScopeId();
this._entries = new Map;
}
get namespace(): string {
invariant(this.module);
let namespace = this.module.namespace;
// Nested scopes.
if (this.scopeId != null && this.scopeId > 0) {
namespace += '._' + this.scopeId;
}
return namespace;
}
extend(module: ?Module = null): Scope {
return new Scope(this, module || this.module);
}
@ -49,14 +42,14 @@ export default class Scope {
invariant(!this._entries.has(name));
const entry = params.length > 0 ? {
type: 'template',
kind: 'template',
name,
params,
instances: [],
node,
scope: this,
} : {
type: 'declaration',
kind: 'declaration',
name,
node,
scope: this,
@ -65,28 +58,34 @@ export default class Scope {
this._entries.set(name, entry);
}
addInstance(name: string, schema: Schema, params: (?Type)[]) {
addInstance(name: string, type: Type, params: (?Type)[]) {
const template = this._entries.get(name);
invariant(template);
invariant(template.type === 'template');
invariant(template.kind === 'template');
template.instances.push({params, schema});
const iname = generateGenericName(params);
type.id = this.id.concat(name, iname);
template.instances.push({params, type});
}
addDefinition(schema: Schema, declared: boolean) {
const decl = this._entries.get(schema.name);
addDefinition(name: string, type: Type, declared: boolean) {
const decl = this._entries.get(name);
if (declared) {
invariant(decl);
invariant(decl.type === 'declaration');
invariant(decl.kind === 'declaration');
} else {
invariant(!decl);
}
this._entries.set(schema.name, {
type: 'definition',
schema,
type.id = this.id.concat(name);
this._entries.set(name, {
kind: 'definition',
type,
scope: this,
});
}
@ -95,7 +94,7 @@ export default class Scope {
invariant(!this._entries.has(info.local));
this._entries.set(info.local, {
type: 'external',
kind: 'external',
info,
scope: this,
});
@ -116,14 +115,14 @@ export default class Scope {
query(name: string, params: (?Type)[]): Query {
const entry = this._entries.get(name);
if (entry && entry.type === 'template') {
const augmented = entry.params.map((p, i) => params[i] === undefined ? p.default : params[i]);
const schema = findInstance(entry, augmented);
if (entry && entry.kind === 'template') {
const augmented = entry.params.map((p, i) => params[i] === undefined ? p.value : params[i]);
const type = findInstance(entry, augmented);
if (schema) {
if (type) {
return {
type: 'definition',
schema,
kind: 'definition',
type,
scope: entry.scope,
};
}
@ -138,20 +137,46 @@ export default class Scope {
}
return {
type: 'unknown',
kind: 'unknown',
};
}
}
function findInstance(template: Template, queried: (?Type)[]): ?Schema {
for (const {schema, params} of template.instances) {
function findInstance(template: Template, queried: (?Type)[]): ?Type {
for (const {type, params} of template.instances) {
// TODO: compare complex structures.
const same = params.every((p, i) => p === queried[i]);
if (same) {
return schema;
return type;
}
}
return null;
}
function generateGenericName(params: (?Type)[]): TypeId {
return wu(params)
.map(type => {
invariant(type);
return getTypeName(type);
})
.toArray();
}
function getTypeName(type: Type): string {
switch (type.kind) {
case 'reference':
const name = last(type.to);
invariant(name != null);
return name;
case 'number':
return type.repr;
case 'string':
case 'boolean':
return type.kind;
default:
invariant(false);
}
}

84
src/types.js Normal file
View File

@ -0,0 +1,84 @@
export type Type =
| RecordType
| ArrayType
| TupleType
| MapType
| UnionType
| IntersectionType
| MaybeType
| NumberType
| StringType
| BooleanType
| LiteralType
| ReferenceType;
export type TypeId = string[];
export type BaseType = {
id?: TypeId,
};
export type RecordType = BaseType & {
kind: 'record',
fields: Field[],
};
export type Field = {
name: string,
value: Type,
required: boolean,
};
export type ArrayType = BaseType & {
kind: 'array',
items: Type,
};
export type TupleType = BaseType & {
kind: 'tuple',
items: Type[],
};
export type MapType = BaseType & {
kind: 'map',
keys: Type,
values: Type,
};
export type UnionType = BaseType & {
kind: 'union',
variants: Type[],
};
export type IntersectionType = BaseType & {
kind: 'intersection',
parts: Type[],
}
export type MaybeType = BaseType & {
kind: 'maybe',
value: Type,
};
export type NumberType = BaseType & {
kind: 'number',
repr: 'i32' | 'i64' | 'u32' | 'u64' | 'f32' | 'f64',
};
export type StringType = BaseType & {
kind: 'string',
};
export type BooleanType = BaseType & {
kind: 'boolean',
};
export type LiteralType = BaseType & {
kind: 'literal',
value: string | number | boolean | null | void,
};
export type ReferenceType = BaseType & {
kind: 'reference',
to: TypeId,
};

View File

@ -4,67 +4,6 @@ import * as assert from 'assert';
// @see flow#112.
export const invariant = assert.ok;
export function partition<T>(iter: Iterable<T>, predicate: T => boolean): [T[], T[]] {
const left = [];
const right = [];
for (const item of iter) {
(predicate(item) ? left : right).push(item);
}
return [left, right];
}
export function map<T, R>(iter: Iterable<T>, mapper: T => R): R[] {
const result = [];
for (const item of iter) {
result.push(mapper(item));
}
return result;
}
export function filter<T>(iter: Iterable<T>, predicate: T => boolean): T[] {
const result = [];
for (const item of iter) {
if (predicate(item)) {
result.push(item);
}
}
return result;
}
export function filterMap<T, R>(iter: Iterable<T>, mapper: T => ?R): R[] {
const result = [];
for (const item of iter) {
const it = mapper(item);
if (it != null) {
result.push(it);
}
}
return result;
}
export function compact<T>(iter: Iterable<?T>): T[] {
// $FlowFixMe
return filter(iter, Boolean);
}
// $FlowFixMe
export function get<T: Object, K: $Keys<T>>(key: K): T => $ElementType<T, K> {
return obj => obj[key];
}
export function compose<X, Y, Z>(a: X => Y, b: Y => Z): X => Z {
return x => b(a(x));
}
export function last<T>(list: T[]): T | void {
return list.length > 0 ? list[list.length - 1] : undefined;
}

View File

@ -13,8 +13,8 @@ function run(title) {
expected = JSON.parse(fs.readFileSync(title + '.json', 'utf8'));
});
it('should provide expected schemas', () => {
assert.deepEqual(actual.schemas, expected.schemas);
it('should provide expected types', () => {
assert.deepEqual(actual.types, expected.types);
});
}

View File

@ -1,13 +1,13 @@
type Type = {
a: number[],
a: string[],
};
interface Interface {
a: number[];
a: string[];
}
class Class {
a: number[];
a: string[];
}
export {Type, Interface, Class};

View File

@ -1,30 +1,30 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "arrays",
"id": ["arrays", "Type"],
"kind": "record",
"fields": [{
"name": "a",
"type": {"type": "array", "items": "double"}
"value": {"kind": "array", "items": {"kind": "string"}},
"required": true
}]
},
{
"type": "record",
"name": "Interface",
"namespace": "arrays",
"id": ["arrays", "Interface"],
"kind": "record",
"fields": [{
"name": "a",
"type": {"type": "array", "items": "double"}
"value": {"kind": "array", "items": {"kind": "string"}},
"required": true
}]
},
{
"type": "record",
"name": "Class",
"namespace": "arrays",
"id": ["arrays", "Class"],
"kind": "record",
"fields": [{
"name": "a",
"type": {"type": "array", "items": "double"}
"value": {"kind": "array", "items": {"kind": "string"}},
"required": true
}]
}
]

View File

@ -1,21 +1,22 @@
{
"schemas": [
"types": [
{
"type": "string",
"name": "Z",
"namespace": "disorder"
"id": ["disorder", "Z"],
"kind": "string"
},
{
"type": "record",
"name": "Y",
"namespace": "disorder",
"fields": [{"name": "z", "type": "Z"}]
"id": ["disorder", "Y"],
"kind": "record",
"fields": [
{"name": "z", "value": {"kind": "reference", "to": ["disorder", "Z"]}, "required": true}
]
},
{
"type": "record",
"name": "X",
"namespace": "disorder",
"fields": [{"name": "y", "type": "Y"}]
"id": ["disorder", "X"],
"kind": "record",
"fields": [
{"name": "y", "value": {"kind": "reference", "to": ["disorder", "Y"]}, "required": true}
]
}
]
}

View File

@ -1,3 +1,3 @@
{
"schemas": []
"types": []
}

View File

@ -1,30 +1,48 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "enums",
"id": ["enums", "Type"],
"kind": "record",
"fields": [{
"name": "a",
"type": {"type": "enum", "symbols": ["one", "two"]}
"value": {
"kind": "union",
"variants": [
{"kind": "literal", "value": "one"},
{"kind": "literal", "value": "two"}
]
},
"required": true
}]
},
{
"type": "record",
"name": "Interface",
"namespace": "enums",
"id": ["enums", "Interface"],
"kind": "record",
"fields": [{
"name": "a",
"type": {"type": "enum", "symbols": ["one", "two"]}
"value": {
"kind": "union",
"variants": [
{"kind": "literal", "value": "one"},
{"kind": "literal", "value": "two"}
]
},
"required": true
}]
},
{
"type": "record",
"name": "Class",
"namespace": "enums",
"id": ["enums", "Class"],
"kind": "record",
"fields": [{
"name": "a",
"type": {"type": "enum", "symbols": ["one", "two"]}
"value": {
"kind": "union",
"variants": [
{"kind": "literal", "value": "one"},
{"kind": "literal", "value": "two"}
]
},
"required": true
}]
}
]

View File

@ -1,73 +1,63 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "A",
"namespace": "externals.first",
"fields": [{"name": "a", "type": "boolean"}]
"id": ["externals", "first", "A"],
"kind": "record",
"fields": [{"name": "a", "value": {"kind": "boolean"}, "required": true}]
},
{
"type": "record",
"name": "B",
"namespace": "externals.first",
"fields": [{"name": "b", "type": "string"}]
"id": ["externals", "first", "B"],
"kind": "record",
"fields": [{"name": "b", "value": {"kind": "string"}, "required": true}]
},
{
"type": "record",
"name": "CC",
"namespace": "externals.first",
"fields": [{"name": "c", "type": "double"}]
"id": ["externals", "first", "CC"],
"kind": "record",
"fields": [{"name": "c", "value": {"kind": "number", "repr": "f64"}, "required": true}]
},
{
"type": "record",
"name": "D",
"namespace": "externals.first",
"fields": [{"name": "d", "type": "double"}]
"id": ["externals", "first", "D"],
"kind": "record",
"fields": [{"name": "d", "value": {"kind": "number", "repr": "f64"}, "required": true}]
},
{
"type": "record",
"name": "X",
"namespace": "externals",
"id": ["externals", "X"],
"kind": "record",
"fields": [
{"name": "a", "type": "externals.first.A"},
{"name": "b", "type": "externals.first.B"},
{"name": "c", "type": "externals.first.CC"},
{"name": "d", "type": "externals.first.D"}
{"name": "a", "value": {"kind": "reference", "to": ["externals", "first", "A"]}, "required": true},
{"name": "b", "value": {"kind": "reference", "to": ["externals", "first", "B"]}, "required": true},
{"name": "c", "value": {"kind": "reference", "to": ["externals", "first", "CC"]}, "required": true},
{"name": "d", "value": {"kind": "reference", "to": ["externals", "first", "D"]}, "required": true}
]
},
{
"type": "record",
"name": "N",
"namespace": "externals.second",
"fields": [{"name": "n", "type": "boolean"}]
"id": ["externals", "second", "N"],
"kind": "record",
"fields": [{"name": "n", "value": {"kind": "boolean"}, "required": true}]
},
{
"type": "record",
"name": "M",
"namespace": "externals.second",
"fields": [{"name": "m", "type": "string"}]
"id": ["externals", "second", "M"],
"kind": "record",
"fields": [{"name": "m", "value": {"kind": "string"}, "required": true}]
},
{
"type": "record",
"name": "KK",
"namespace": "externals.second",
"fields": [{"name": "k", "type": "double"}]
"id": ["externals", "second", "KK"],
"kind": "record",
"fields": [{"name": "k", "value": {"kind": "number", "repr": "f64"}, "required": true}]
},
{
"type": "record",
"name": "P",
"namespace": "externals.second",
"fields": [{"name": "p", "type": "double"}]
"id": ["externals", "second", "P"],
"kind": "record",
"fields": [{"name": "p", "value": {"kind": "number", "repr": "f64"}, "required": true}]
},
{
"type": "record",
"name": "Y",
"namespace": "externals",
"id": ["externals", "Y"],
"kind": "record",
"fields": [
{"name": "n", "type": "externals.second.N"},
{"name": "m", "type": "externals.second.M"},
{"name": "k", "type": "externals.second.KK"},
{"name": "p", "type": "externals.second.P"}
{"name": "n", "value": {"kind": "reference", "to": ["externals", "second", "N"]}, "required": true},
{"name": "m", "value": {"kind": "reference", "to": ["externals", "second", "M"]}, "required": true},
{"name": "k", "value": {"kind": "reference", "to": ["externals", "second", "KK"]}, "required": true},
{"name": "p", "value": {"kind": "reference", "to": ["externals", "second", "P"]}, "required": true}
]
}
]

View File

@ -4,7 +4,7 @@ type A<T, K> = {
};
type X = {
a: A<string, number>,
a: A<string, boolean>,
};
export {X};

View File

@ -1,19 +1,21 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "A__string_double",
"namespace": "generics",
"id": ["generics", "A", "string", "boolean"],
"kind": "record",
"fields": [
{"name": "t", "type": "string"},
{"name": "k", "type": "double"}
{"name": "t", "value": {"kind": "string"}, "required": true},
{"name": "k", "value": {"kind": "boolean"}, "required": true}
]
},
{
"type": "record",
"name": "X",
"namespace": "generics",
"fields": [{"name": "a", "type": "A__string_double"}]
"id": ["generics", "X"],
"kind": "record",
"fields": [{
"name": "a",
"value": {"kind": "reference", "to": ["generics", "A", "string", "boolean"]},
"required": true
}]
}
]
}

View File

@ -1,53 +1,57 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "A",
"namespace": "inheritance",
"fields": [{"name": "a", "type": "double"}]
"id": ["inheritance", "A"],
"kind": "record",
"fields": [{"name": "a", "value": {"kind": "number", "repr": "f64"}, "required": true}]
},
{
"type": "record",
"name": "B",
"namespace": "inheritance",
"fields": [
{"name": "a", "type": "double"},
{"name": "b", "type": "string"}
"id": ["inheritance", "B"],
"kind": "intersection",
"parts": [
{"kind": "reference", "to": ["inheritance", "A"]},
{
"kind": "record",
"fields": [{"name": "b", "value": {"kind": "string"}, "required": true}]
}
]
},
{
"type": "record",
"name": "C",
"namespace": "inheritance",
"fields": [
{"name": "a", "type": "double"},
{"name": "b", "type": "string"},
{"name": "c", "type": "boolean"}
"id": ["inheritance", "C"],
"kind": "intersection",
"parts": [
{"kind": "reference", "to": ["inheritance", "B"]},
{
"kind": "record",
"fields": [{"name": "c", "value": {"kind": "boolean"}, "required": true}]
}
]
},
{
"type": "record",
"name": "X",
"namespace": "inheritance",
"fields": [{"name": "x", "type": "double"}]
"id": ["inheritance", "X"],
"kind": "record",
"fields": [{"name": "x", "value": {"kind": "number", "repr": "f64"}, "required": true}]
},
{
"type": "record",
"name": "Y",
"namespace": "inheritance",
"fields": [
{"name": "x", "type": "double"},
{"name": "y", "type": "string"}
"id": ["inheritance", "Y"],
"kind": "intersection",
"parts": [
{"kind": "reference", "to": ["inheritance", "X"]},
{
"kind": "record",
"fields": [{"name": "y", "value": {"kind": "string"}, "required": true}]
}
]
},
{
"type": "record",
"name": "Z",
"namespace": "inheritance",
"fields": [
{"name": "x", "type": "double"},
{"name": "y", "type": "string"},
{"name": "z", "type": "boolean"}
"id": ["inheritance", "Z"],
"kind": "intersection",
"parts": [
{"kind": "reference", "to": ["inheritance", "Y"]},
{
"kind": "record",
"fields": [{"name": "z", "value": {"kind": "boolean"}, "required": true}]
}
]
}
]

View File

@ -1,66 +1,64 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "A",
"namespace": "intersections",
"fields": [{"name": "a", "type": "double"}]
},
{
"type": "record",
"name": "B",
"namespace": "intersections",
"fields": [{"name": "b", "type": "string"}]
},
{
"type": "record",
"name": "X",
"namespace": "intersections",
"id": ["intersections", "A"],
"kind": "record",
"fields": [
{"name": "a", "type": "double"},
{"name": "b", "type": "string"}
{"name": "a", "value": {"kind": "number", "repr": "f64"}, "required": true}
]
},
{
"type": "record",
"name": "C",
"namespace": "intersections",
"fields": [{"name": "c", "type": "boolean"}]
},
{
"type": "record",
"name": "_A_B_C",
"namespace": "intersections",
"id": ["intersections", "B"],
"kind": "record",
"fields": [
{"name": "a", "type": "double"},
{"name": "b", "type": "string"},
{"name": "c", "type": "boolean"}
{"name": "b", "value": {"kind": "string"}, "required": true}
]
},
{
"type": "record",
"name": "Y",
"namespace": "intersections",
"fields": [
{"name": "y", "type": "_A_B_C"}
"id": ["intersections", "X"],
"kind": "intersection",
"parts": [
{"kind": "reference", "to": ["intersections", "A"]},
{"kind": "reference", "to": ["intersections", "B"]}
]
},
{
"type": "record",
"name": "_A_C",
"namespace": "intersections",
"id": ["intersections", "C"],
"kind": "record",
"fields": [
{"name": "a", "type": "double"},
{"name": "c", "type": "boolean"}
{"name": "c", "value": {"kind": "boolean"}, "required": true}
]
},
{
"type": "record",
"name": "Z",
"namespace": "intersections",
"fields": [
{"name": "z", "type": "_A_C"}
]
"id": ["intersections", "Y"],
"kind": "record",
"fields": [{
"name": "y",
"value": {
"kind": "intersection",
"parts": [
{"kind": "reference", "to": ["intersections", "A"]},
{"kind": "reference", "to": ["intersections", "B"]},
{"kind": "reference", "to": ["intersections", "C"]}
]
},
"required": true
}]
},
{
"id": ["intersections", "Z"],
"kind": "record",
"fields": [{
"name": "z",
"value": {
"kind": "intersection",
"parts": [
{"kind": "reference", "to": ["intersections", "A"]},
{"kind": "reference", "to": ["intersections", "C"]}
]
},
"required": true
}]
}
]
}

View File

@ -1,9 +1,9 @@
type Type = {
[string]: number,
[string]: boolean,
};
interface Interface {
[string]: number;
[string]: boolean;
}
export {Type, Interface};

View File

@ -1,16 +1,16 @@
{
"schemas": [
"types": [
{
"type": "map",
"name": "Type",
"namespace": "maps",
"values": "double"
"id": ["maps", "Type"],
"kind": "map",
"keys": {"kind": "string"},
"values": {"kind": "boolean"}
},
{
"type": "map",
"name": "Interface",
"namespace": "maps",
"values": "double"
"id": ["maps", "Interface"],
"kind": "map",
"keys": {"kind": "string"},
"values": {"kind": "boolean"}
}
]
}

View File

@ -1,40 +1,34 @@
type Type = {
// $avro int
// @repr {i32}
a: number,
/* $avro long */
/* @repr {i64} */
b: number,
// $avro float
// @repr {f32}
c: number,
// $avro double
// @repr {f64}
d: number,
/* $avro fixed 10 */
e: Buffer,
};
interface Interface {
// $avro int
// @repr {i32}
a: number;
/* $avro long */
/* @repr {i64} */
b: number;
// $avro float
// @repr {f32}
c: number;
// $avro double
// @repr {f64}
d: number;
/* $avro fixed 10 */
e: Buffer;
}
class Class {
// $avro int
// @repr {i32}
a: number;
/* $avro long */
/* @repr {i64} */
b: number;
// $avro float
// @repr {f32}
c: number;
// $avro double
// @repr {f64}
d: number;
/* $avro fixed 10 */
e: Buffer;
}
export {Type, Interface, Class};

View File

@ -1,39 +1,33 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "pragmas",
"id": ["pragmas", "Type"],
"kind": "record",
"fields": [
{"name": "a", "type": "int"},
{"name": "b", "type": "long"},
{"name": "c", "type": "float"},
{"name": "d", "type": "double"},
{"name": "e", "type": {"type": "fixed", "size": 10}}
{"name": "a", "value": {"kind": "number", "repr": "i32"}, "required": true},
{"name": "b", "value": {"kind": "number", "repr": "i64"}, "required": true},
{"name": "c", "value": {"kind": "number", "repr": "f32"}, "required": true},
{"name": "d", "value": {"kind": "number", "repr": "f64"}, "required": true}
]
},
{
"type": "record",
"name": "Interface",
"namespace": "pragmas",
"id": ["pragmas", "Interface"],
"kind": "record",
"fields": [
{"name": "a", "type": "int"},
{"name": "b", "type": "long"},
{"name": "c", "type": "float"},
{"name": "d", "type": "double"},
{"name": "e", "type": {"type": "fixed", "size": 10}}
{"name": "a", "value": {"kind": "number", "repr": "i32"}, "required": true},
{"name": "b", "value": {"kind": "number", "repr": "i64"}, "required": true},
{"name": "c", "value": {"kind": "number", "repr": "f32"}, "required": true},
{"name": "d", "value": {"kind": "number", "repr": "f64"}, "required": true}
]
},
{
"type": "record",
"name": "Class",
"namespace": "pragmas",
"id": ["pragmas", "Class"],
"kind": "record",
"fields": [
{"name": "a", "type": "int"},
{"name": "b", "type": "long"},
{"name": "c", "type": "float"},
{"name": "d", "type": "double"},
{"name": "e", "type": {"type": "fixed", "size": 10}}
{"name": "a", "value": {"kind": "number", "repr": "i32"}, "required": true},
{"name": "b", "value": {"kind": "number", "repr": "i64"}, "required": true},
{"name": "c", "value": {"kind": "number", "repr": "f32"}, "required": true},
{"name": "d", "value": {"kind": "number", "repr": "f64"}, "required": true}
]
}
]

View File

@ -3,7 +3,6 @@ type Type = {
b: number,
c: boolean,
d: null,
e: Buffer,
};
interface Interface {
@ -11,7 +10,6 @@ interface Interface {
b: number;
c: boolean;
d: null;
e: Buffer;
};
class Class {
@ -19,7 +17,6 @@ class Class {
b: number;
c: boolean;
d: null;
e: Buffer;
}
export {Type, Interface, Class};

View File

@ -1,39 +1,33 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "primitives",
"id": ["primitives", "Type"],
"kind": "record",
"fields": [
{"name": "a", "type": "string"},
{"name": "b", "type": "double"},
{"name": "c", "type": "boolean"},
{"name": "d", "type": "null"},
{"name": "e", "type": "bytes"}
{"name": "a", "value": {"kind": "string"}, "required": true},
{"name": "b", "value": {"kind": "number", "repr": "f64"}, "required": true},
{"name": "c", "value": {"kind": "boolean"}, "required": true},
{"name": "d", "value": {"kind": "literal", "value": null}, "required": true}
]
},
{
"type": "record",
"name": "Interface",
"namespace": "primitives",
"id": ["primitives", "Interface"],
"kind": "record",
"fields": [
{"name": "a", "type": "string"},
{"name": "b", "type": "double"},
{"name": "c", "type": "boolean"},
{"name": "d", "type": "null"},
{"name": "e", "type": "bytes"}
{"name": "a", "value": {"kind": "string"}, "required": true},
{"name": "b", "value": {"kind": "number", "repr": "f64"}, "required": true},
{"name": "c", "value": {"kind": "boolean"}, "required": true},
{"name": "d", "value": {"kind": "literal", "value": null}, "required": true}
]
},
{
"type": "record",
"name": "Class",
"namespace": "primitives",
"id": ["primitives", "Class"],
"kind": "record",
"fields": [
{"name": "a", "type": "string"},
{"name": "b", "type": "double"},
{"name": "c", "type": "boolean"},
{"name": "d", "type": "null"},
{"name": "e", "type": "bytes"}
{"name": "a", "value": {"kind": "string"}, "required": true},
{"name": "b", "value": {"kind": "number", "repr": "f64"}, "required": true},
{"name": "c", "value": {"kind": "boolean"}, "required": true},
{"name": "d", "value": {"kind": "literal", "value": null}, "required": true}
]
}
]

View File

@ -1,35 +1,55 @@
{
"schemas": [
"types": [
{
"type": "string",
"name": "A",
"namespace": "references"
"id": ["references", "A"],
"kind": "string"
},
{
"type": "record",
"name": "Type",
"namespace": "references",
"id": ["references", "Type"],
"kind": "record",
"fields": [
{"name": "a", "type": "A"},
{"name": "b", "type": {"type": "array", "items": "A"}}
{
"name": "a",
"value": {"kind": "reference", "to": ["references", "A"]},
"required": true
},
{
"name": "b",
"value": {"kind": "array", "items": {"kind": "reference", "to": ["references", "A"]}},
"required": true
}
]
},
{
"type": "record",
"name": "Interface",
"namespace": "references",
"id": ["references", "Interface"],
"kind": "record",
"fields": [
{"name": "a", "type": "A"},
{"name": "b", "type": {"type": "array", "items": "A"}}
{
"name": "a",
"value": {"kind": "reference", "to": ["references", "A"]},
"required": true
},
{
"name": "b",
"value": {"kind": "array", "items": {"kind": "reference", "to": ["references", "A"]}},
"required": true
}
]
},
{
"type": "record",
"name": "Class",
"namespace": "references",
"id": ["references", "Class"],
"kind": "record",
"fields": [
{"name": "a", "type": "A"},
{"name": "b", "type": {"type": "array", "items": "A"}}
{
"name": "a",
"value": {"kind": "reference", "to": ["references", "A"]},
"required": true
},
{
"name": "b",
"value": {"kind": "array", "items": {"kind": "reference", "to": ["references", "A"]}},
"required": true
}
]
}
]

View File

@ -1,73 +1,68 @@
{
"schemas": [
"types": [
{
"type": "boolean",
"name": "X",
"namespace": "scopes._2"
"id": ["scopes", "2", "X"],
"kind": "boolean"
},
{
"type": "string",
"name": "Z",
"namespace": "scopes._1"
"id": ["scopes", "1", "Z"],
"kind": "string"
},
{
"type": "record",
"name": "Y",
"namespace": "scopes._2",
"id": ["scopes", "2", "Y"],
"kind": "record",
"fields": [
{"name": "x", "type": "X"},
{"name": "z", "type": "scopes._1.Z"}
{"name": "x", "value": {"kind": "reference", "to": ["scopes", "2", "X"]}, "required": true},
{"name": "z", "value": {"kind": "reference", "to": ["scopes", "1", "Z"]}, "required": true}
]
},
{
"type": "double",
"name": "X",
"namespace": "scopes._3"
"id": ["scopes", "3", "X"],
"kind": "number",
"repr": "f64"
},
{
"type": "record",
"name": "Y",
"namespace": "scopes._3",
"id": ["scopes", "3", "Y"],
"kind": "record",
"fields": [
{"name": "x", "type": "X"},
{"name": "z", "type": "scopes._1.Z"}
{"name": "x", "value": {"kind": "reference", "to": ["scopes", "3", "X"]}, "required": true},
{"name": "z", "value": {"kind": "reference", "to": ["scopes", "1", "Z"]}, "required": true}
]
},
{
"type": "string",
"name": "X",
"namespace": "scopes._4"
"id": ["scopes", "4", "X"],
"kind": "string"
},
{
"type": "record",
"name": "Y",
"namespace": "scopes._4",
"id": ["scopes", "4", "Y"],
"kind": "record",
"fields": [
{"name": "x", "type": "X"},
{"name": "z", "type": "scopes._1.Z"}
{"name": "x", "value": {"kind": "reference", "to": ["scopes", "4", "X"]}, "required": true},
{"name": "z", "value": {"kind": "reference", "to": ["scopes", "1", "Z"]}, "required": true}
]
},
{
"type": "double",
"name": "X",
"namespace": "scopes._1"
"id": ["scopes", "1", "X"],
"kind": "number",
"repr": "f64"
},
{
"type": "record",
"name": "Y",
"namespace": "scopes._1",
"fields": [{"name": "x", "type": "X"}]
"id": ["scopes", "1", "Y"],
"kind": "record",
"fields": [
{"name": "x", "value": {"kind": "reference", "to": ["scopes", "1", "X"]}, "required": true}
]
},
{
"type": "string",
"name": "X",
"namespace": "scopes"
"id": ["scopes", "X"],
"kind": "string"
},
{
"type": "record",
"name": "Y",
"namespace": "scopes",
"fields": [{"name": "x", "type": "X"}]
"id": ["scopes", "Y"],
"kind": "record",
"fields": [
{"name": "x", "value": {"kind": "reference", "to": ["scopes", "X"]}, "required": true}
]
}
]
}

View File

@ -1,25 +1,30 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Buffer",
"namespace": "shadowing._1",
"id": ["shadowing", "1", "Buffer"],
"kind": "record",
"fields": []
},
{
"type": "record",
"name": "Y",
"namespace": "shadowing._1",
"id": ["shadowing", "1", "Y"],
"kind": "record",
"fields": [
{"name": "y", "type": "Buffer"}
{
"name": "y",
"value": {"kind": "reference", "to": ["shadowing", "1", "Buffer"]},
"required": true
}
]
},
{
"type": "record",
"name": "X",
"namespace": "shadowing",
"id": ["shadowing", "X"],
"kind": "record",
"fields": [
{"name": "x", "type": "bytes"}
{
"name": "x",
"value": {"kind": "reference", "to": ["Buffer"]},
"required": true
}
]
}
]

View File

@ -3,7 +3,7 @@ type Type = {
foo(): void,
b: number,
b: boolean,
bar: () => void,
};
@ -13,7 +13,7 @@ interface Interface {
foo(): void;
b: number;
b: boolean;
bar: () => void;
}
@ -25,7 +25,7 @@ class Class {
get bar() {}
set bar(a) {}
b: number;
b: boolean;
baz: () => void;
}

View File

@ -1,30 +1,27 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "skipFunctions",
"id": ["skipFunctions", "Type"],
"kind": "record",
"fields": [
{"name": "a", "type": "string"},
{"name": "b", "type": "double"}
{"name": "a", "value": {"kind": "string"}, "required": true},
{"name": "b", "value": {"kind": "boolean"}, "required": true}
]
},
{
"type": "record",
"name": "Interface",
"namespace": "skipFunctions",
"id": ["skipFunctions", "Interface"],
"kind": "record",
"fields": [
{"name": "a", "type": "string"},
{"name": "b", "type": "double"}
{"name": "a", "value": {"kind": "string"}, "required": true},
{"name": "b", "value": {"kind": "boolean"}, "required": true}
]
},
{
"type": "record",
"name": "Class",
"namespace": "skipFunctions",
"id": ["skipFunctions", "Class"],
"kind": "record",
"fields": [
{"name": "a", "type": "string"},
{"name": "b", "type": "double"}
{"name": "a", "value": {"kind": "string"}, "required": true},
{"name": "b", "value": {"kind": "boolean"}, "required": true}
]
}
]

View File

@ -1,16 +1,18 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Test",
"namespace": "typeInMethod",
"id": ["typeInMethod", "Test"],
"kind": "record",
"fields": []
},
{
"type": "record",
"name": "X",
"namespace": "typeInMethod._1",
"fields": [{"name": "t", "type": "typeInMethod.Test"}]
"id": ["typeInMethod", "1", "X"],
"kind": "record",
"fields": [{
"name": "t",
"value": {"kind": "reference", "to": ["typeInMethod", "Test"]},
"required": true
}]
}
]
}

View File

@ -1,16 +1,13 @@
type Type = {
a: string | number,
b: ?string,
a: string | boolean,
};
interface Interface {
a: string | number;
b: ?string;
a: string | boolean | number;
}
class Class {
a: string | number;
b: ?string;
a: string | boolean;
}
export {Type, Interface, Class};

View File

@ -1,47 +1,44 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "unions",
"id": ["unions", "Type"],
"kind": "record",
"fields": [
{
"name": "a",
"type": ["string", "double"]
},
{
"name": "b",
"type": ["null", "string"]
"value": {
"kind": "union",
"variants": [{"kind": "string"}, {"kind": "boolean"}]
},
"required": true
}
]
},
{
"type": "record",
"name": "Interface",
"namespace": "unions",
"id": ["unions", "Interface"],
"kind": "record",
"fields": [
{
"name": "a",
"type": ["string", "double"]
},
{
"name": "b",
"type": ["null", "string"]
"value": {
"kind": "union",
"variants": [{"kind": "string"}, {"kind": "boolean"}, {"kind": "number", "repr": "f64"}]
},
"required": true
}
]
},
{
"type": "record",
"name": "Class",
"namespace": "unions",
"id": ["unions", "Class"],
"kind": "record",
"fields": [
{
"name": "a",
"type": ["string", "double"]
},
{
"name": "b",
"type": ["null", "string"]
"value": {
"kind": "union",
"variants": [{"kind": "string"}, {"kind": "boolean"}]
},
"required": true
}
]
}

View File

@ -1,13 +1,13 @@
type Type = {
a: 'one' | 'two' | number,
a: 'one' | 'two' | string,
};
interface Interface {
a: 'one' | 'two' | number;
a: 'one' | 'two' | string;
}
class Class {
a: 'one' | 'two' | number;
a: 'one' | 'two' | string;
}
export {Type, Interface, Class};

View File

@ -1,39 +1,51 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "unionsAndEnums",
"id": ["unionsAndEnums", "Type"],
"kind": "record",
"fields": [{
"name": "a",
"type": [
"double",
{"type": "enum", "symbols": ["one", "two"]}
]
"value": {
"kind": "union",
"variants": [
{"kind": "literal", "value": "one"},
{"kind": "literal", "value": "two"},
{"kind": "string"}
]
},
"required": true
}]
},
{
"type": "record",
"name": "Interface",
"namespace": "unionsAndEnums",
"id": ["unionsAndEnums", "Interface"],
"kind": "record",
"fields": [{
"name": "a",
"type": [
"double",
{"type": "enum", "symbols": ["one", "two"]}
]
"value": {
"kind": "union",
"variants": [
{"kind": "literal", "value": "one"},
{"kind": "literal", "value": "two"},
{"kind": "string"}
]
},
"required": true
}]
},
{
"type": "record",
"name": "Class",
"namespace": "unionsAndEnums",
"id": ["unionsAndEnums", "Class"],
"kind": "record",
"fields": [{
"name": "a",
"type": [
"double",
{"type": "enum", "symbols": ["one", "two"]}
]
"value": {
"kind": "union",
"variants": [
{"kind": "literal", "value": "one"},
{"kind": "literal", "value": "two"},
{"kind": "string"}
]
},
"required": true
}]
}
]

View File

@ -1,3 +1,3 @@
{
"schemas": []
"types": []
}

View File

@ -1,27 +1,24 @@
{
"schemas": [
"types": [
{
"type": "record",
"name": "Type",
"namespace": "valueAsType",
"id": ["valueAsType", "Type"],
"kind": "record",
"fields": [
{"name": "a", "type": {"type": "enum", "symbols": ["one"]}}
{"name": "a", "value": {"kind": "literal", "value": "one"}, "required": true}
]
},
{
"type": "record",
"name": "Interface",
"namespace": "valueAsType",
"id": ["valueAsType", "Interface"],
"kind": "record",
"fields": [
{"name": "a", "type": {"type": "enum", "symbols": ["one"]}}
{"name": "a", "value": {"kind": "literal", "value": "one"}, "required": true}
]
},
{
"type": "record",
"name": "Class",
"namespace": "valueAsType",
"id": ["valueAsType", "Class"],
"kind": "record",
"fields": [
{"name": "a", "type": {"type": "enum", "symbols": ["one"]}}
{"name": "a", "value": {"kind": "literal", "value": "one"}, "required": true}
]
}
]