The extraction is interruptible now

master
Paul Loyd 2017-11-02 16:28:53 +03:00
parent 8ce9bc35f4
commit fa0f96ebea
3 changed files with 94 additions and 69 deletions

View File

@ -4,81 +4,116 @@ const assert = require('assert');
const {partition, log} = require('./utils');
function extract(node) {
if (!extractors[node.type]) {
const provide = (schema) => ({command: 'provide', schema});
function extract(node, schemas) {
const extractor = extractors[node.type];
if (!extractor) {
log(node);
return null;
}
return extractors[node.type](node);
const iter = extractor(node);
let result = null;
while (true) {
const {done, value} = iter.next(result);
if (done) {
return value;
}
if (!value) {
result = null;
} else if (value.command === 'provide') {
const {schema} = value;
schemas[schema.name] = schema;
} else if (Array.isArray(value)) {
result = value.map(val => extract(val, schemas));
} else {
result = extract(value);
}
}
}
const extractors = {
TypeAlias(node) {
return extract(node.right);
* TypeAlias(node) {
const schema = yield node.right;
schema.name = yield node.id;
yield provide(schema);
},
InterfaceDeclaration(node) {
return extract(node.body);
* InterfaceDeclaration(node) {
const schema = yield node.body;
schema.name = yield node.id;
yield provide(schema);
},
ClassDeclaration(node) {
return extract(node.body);
* ClassDeclaration(node) {
const schema = yield node.body;
schema.name = yield node.id;
yield provide(schema);
},
ClassBody(node) {
* ClassBody(node) {
return {
type: 'record',
fields: node.body.map(extract).filter(Boolean),
fields: (yield node.body).filter(Boolean),
};
},
ClassProperty(node) {
* ClassProperty(node) {
if (node.static) {
return null;
}
let type = node.leadingComments && extractLastPragma(node.leadingComments);
let type = node.leadingComments && (yield* extractLastPragma(node.leadingComments));
return {
name: extract(node.key),
type: type || extract(node.typeAnnotation),
name: yield node.key,
type: type || (yield node.typeAnnotation),
};
},
ClassMethod(node) {
* ClassMethod(node) {
return null;
},
ObjectTypeAnnotation(node) {
* ObjectTypeAnnotation(node) {
if (node.indexers.length > 0) {
// Allow functions, getters and setters.
const properties = node.properties.map(extract).filter(Boolean);
const properties = (yield node.properties).filter(Boolean);
assert.equal(properties.length, 0);
assert.equal(node.indexers.length, 1);
return {
type: 'map',
values: extract(node.indexers[0]),
values: yield node.indexers[0],
};
}
return {
type: 'record',
fields: node.properties.map(extract).filter(Boolean),
fields: (yield node.properties).filter(Boolean),
};
},
ObjectTypeProperty(node) {
* ObjectTypeProperty(node) {
let type = null;
if (node.leadingComments) {
type = extractLastPragma(node.leadingComments);
type = yield* extractLastPragma(node.leadingComments);
}
if (!type) {
type = extract(node.value);
type = yield node.value;
}
if (!type) {
@ -86,49 +121,49 @@ const extractors = {
}
return {
name: extract(node.key),
name: yield node.key,
type,
};
},
ObjectTypeIndexer(node) {
const key = extract(node.key);
* ObjectTypeIndexer(node) {
const key = yield node.key;
assert.equal(key, 'string');
return extract(node.value);
return yield node.value;
},
TypeAnnotation(node) {
return extract(node.typeAnnotation);
* TypeAnnotation(node) {
return yield node.typeAnnotation;
},
NumberTypeAnnotation(node) {
* NumberTypeAnnotation(node) {
return 'double';
},
StringTypeAnnotation(node) {
* StringTypeAnnotation(node) {
return 'string';
},
BooleanTypeAnnotation(node) {
* BooleanTypeAnnotation(node) {
return 'boolean';
},
ArrayTypeAnnotation(node) {
* ArrayTypeAnnotation(node) {
return {
type: 'array',
items: extract(node.elementType),
items: yield node.elementType,
};
},
UnionTypeAnnotation(node) {
* UnionTypeAnnotation(node) {
// TODO: flatten variants.
let [symbols, variants] = partition(node.types, isEnumSymbol);
symbols = symbols.map(unwrapEnumSymbol);
variants = variants.map(extract);
variants = yield variants;
if (symbols.length > 0) {
const enumeration = {
@ -146,23 +181,23 @@ const extractors = {
return variants;
},
NullableTypeAnnotation(node) {
return ['null', extract(node.typeAnnotation)];
* NullableTypeAnnotation(node) {
return ['null', yield node.typeAnnotation];
},
NullLiteralTypeAnnotation(node) {
* NullLiteralTypeAnnotation(node) {
return 'null';
},
StringLiteralTypeAnnotation(node) {
* StringLiteralTypeAnnotation(node) {
return {
type: 'enum',
symbols: [node.value],
};
},
GenericTypeAnnotation(node) {
const name = extract(node.id);
* GenericTypeAnnotation(node) {
const name = yield node.id;
// TODO: shadowing?
if (name === 'Buffer') {
@ -172,23 +207,29 @@ const extractors = {
return name;
},
FunctionTypeAnnotation(node) {
* FunctionTypeAnnotation(node) {
return null;
},
Identifier(node) {
* Identifier(node) {
return node.name;
},
CommentLine(node) {
* CommentLine(node) {
return extractPragma(node.value);
},
CommentBlock(node) {
* CommentBlock(node) {
return extractPragma(node.value);
},
};
function* extractLastPragma(comments) {
const pragmas = (yield comments).filter(Boolean);
return pragmas.length > 0 ? pragmas[pragmas.length - 1] : null;
}
function parsePragma(pragma) {
let [type, arg] = pragma.split(/\s+/);
@ -221,14 +262,6 @@ function parsePragma(pragma) {
return [type, arg];
}
function extractLastPragma(comments) {
const pragmas = comments
.map(extract)
.filter(Boolean);
return pragmas.length > 0 ? pragmas[pragmas.length - 1] : null;
}
function extractPragma(text) {
const marker = '$avro ';

View File

@ -1,21 +1,11 @@
'use strict';
const assert = require('assert');
const parse = require('./parser');
const visit = require('./visitor');
const extract = require('./extractor');
function collect(node, schemes) {
const scheme = extract(node);
const name = node.id.name;
assert(!schemes[name]);
scheme.name = name;
schemes[name] = scheme;
function collect(node, schemas) {
extract(node, schemas);
return false;
}
@ -29,11 +19,11 @@ const visitor = {
function generate(code) {
const ast = parse(code);
const schemes = Object.create({});
const schemas = Object.create({});
visit(ast, visitor, schemes);
visit(ast, visitor, schemas);
return schemes;
return schemas;
}
module.exports = generate;

View File

@ -1,3 +1,5 @@
'use strict';
function partition(iter, predicate) {
const left = [];
const right = [];