@stackpress/idea-parser
Version:
Parses ideas to AST and readable JSON.
214 lines (213 loc) • 8.13 kB
JavaScript
import Exception from './Exception.js';
export default class Compiler {
static array(token, references = false) {
return token.elements.map(element => this.data(element, references));
}
static data(token, references = false) {
if (token.type === 'ObjectExpression') {
return this.object(token, references);
}
else if (token.type === 'ArrayExpression') {
return this.array(token, references);
}
else if (token.type === 'Literal') {
return this.literal(token);
}
else if (token.type === 'Identifier') {
return this.identifier(token, references);
}
throw Exception.for('Invalid data token type');
}
static enum(token) {
if (token.kind !== 'enum') {
throw Exception.for('Invalid Enum');
}
const name = token.declarations?.[0].id?.name;
const options = {};
token.declarations[0].init.properties.forEach(property => {
options[property.key.name] = property.value.value;
});
return [name, options];
}
static final(token) {
const schema = this.schema(token, true);
delete schema.use;
delete schema.prop;
return schema;
}
static identifier(token, references = false) {
if (references && token.name in references) {
return references[token.name];
}
else if (references === false) {
return '${' + token.name + '}';
}
throw Exception.for(`Unknown reference ${token.name}`);
}
static literal(token) {
return token.value;
}
static model(token, references = false) {
const name = token.declarations[0].id?.name;
const mutable = token.mutable !== false;
const value = {};
token.declarations[0].init.properties.forEach(property => {
value[property.key.name] = this.data(property.value, references);
});
if (typeof value.columns !== 'object') {
throw Exception.for('Expecting a columns property');
}
const columns = [];
for (const name in value.columns) {
const column = value.columns[name];
column.name = name;
if (typeof column.type === 'string') {
column.required = !column.type.endsWith('?');
column.type = column.type.replace(/\?$/, '');
column.multiple = column.type.endsWith('[]');
column.type = column.type.replace(/\[\]$/, '');
}
columns.push({
type: column.type,
name: column.name,
required: column.required,
multiple: column.multiple,
attributes: column.attributes,
...column
});
}
value.columns = columns;
return [name, { name, mutable, ...value }];
}
static object(token, references = false) {
return Object.fromEntries(token.properties.map(property => [
property.key.name,
this.data(property.value, references)
]));
}
static plugin(token) {
if (token.kind !== 'plugin') {
throw Exception.for('Invalid Plugin');
}
const name = token.declarations?.[0].id?.name;
const value = {};
token.declarations[0].init.properties.forEach(property => {
value[property.key.name] = this.data(property.value);
});
return [name, value];
}
static prop(token, references = false) {
if (token.kind !== 'prop') {
throw Exception.for('Invalid Prop');
}
const name = token.declarations[0].id.name;
const value = {};
token.declarations[0].init.properties.forEach(property => {
value[property.key.name] = this.data(property.value, references);
});
return [name, value];
}
static schema(token, finalize = false) {
if (token.kind !== 'schema') {
throw Exception.for('Invalid Schema');
}
const schema = {};
const references = {};
const uses = token.body.filter(token => token.type === 'ImportDeclaration');
uses.forEach(use => {
schema.use = schema.use || [];
schema.use.push(this.use(use));
});
const declarations = token.body;
declarations.filter(declaration => declaration.kind).forEach(declaration => {
if (declaration.kind === 'enum') {
schema.enum = schema.enum || {};
const [key, value] = this.enum(declaration);
schema.enum[key] = value;
if (references[key]) {
throw Exception.for('Duplicate %s', key);
}
references[key] = value;
}
else if (declaration.kind === 'prop') {
schema.prop = schema.prop || {};
const [key, value] = this.prop(declaration, finalize ? references : false);
schema.prop[key] = value;
if (references[key]) {
throw Exception.for('Duplicate %s', key);
}
references[key] = value;
}
else if (declaration.kind === 'type') {
schema.type = schema.type || {};
const [key, value] = this.type(declaration, finalize ? references : false);
schema.type[key] = value;
if (references[key]) {
throw Exception.for('Duplicate %s', key);
}
references[key] = value;
}
else if (declaration.kind === 'model') {
schema.model = schema.model || {};
const [key, value] = this.model(declaration, finalize ? references : false);
schema.model[key] = value;
if (references[key]) {
throw Exception.for('Duplicate %s', key);
}
references[key] = value;
}
else if (declaration.kind === 'plugin') {
schema.plugin = schema.plugin || {};
const [key, value] = this.plugin(declaration);
schema.plugin[key] = value;
if (references[key]) {
throw Exception.for('Duplicate %s', key);
}
references[key] = value;
}
});
return schema;
}
static type(token, references = false) {
if (token.kind !== 'type') {
throw Exception.for('Invalid Type');
}
const name = token.declarations[0].id.name;
const mutable = token.mutable !== false;
const value = {};
token.declarations[0].init.properties.forEach(property => {
value[property.key.name] = this.data(property.value, references);
});
if (typeof value.columns !== 'object') {
throw Exception.for('Expecting a columns property');
}
const columns = [];
for (const name in value.columns) {
const column = value.columns[name];
column.name = name;
if (typeof column.type === 'string') {
column.required = !column.type.endsWith('?');
column.type = column.type.replace(/\?$/, '');
column.multiple = column.type.endsWith('[]');
column.type = column.type.replace(/\[\]$/, '');
}
columns.push({
type: column.type,
name: column.name,
required: column.required,
multiple: column.multiple,
attributes: column.attributes,
...column
});
}
value.columns = columns;
return [name, { name, mutable, ...value }];
}
static use(token) {
if (token.type !== 'ImportDeclaration') {
throw Exception.for('Invalid Import');
}
return token.source.value;
}
}
;