Includes in schema compiler
This commit is contained in:
parent
55e4222d68
commit
c75aaf4b18
|
@ -31,6 +31,7 @@
|
|||
"@types/minimatch": "^3.0.3",
|
||||
"@types/yargs": "^16.0.0",
|
||||
"chalk": "^4.1.0",
|
||||
"chokidar": "^3.5.1",
|
||||
"glob": "^7.1.6",
|
||||
"minimatch": "^3.0.4",
|
||||
"yargs": "^16.2.0"
|
||||
|
|
|
@ -43,6 +43,7 @@ export default [{
|
|||
external: [
|
||||
'@preserves/core',
|
||||
'chalk',
|
||||
'chokidar',
|
||||
'fs',
|
||||
'glob',
|
||||
'minimatch',
|
||||
|
|
|
@ -7,6 +7,7 @@ import yargs from 'yargs/yargs';
|
|||
import * as M from '../meta';
|
||||
import chalk from 'chalk';
|
||||
import { formatPosition, Position } from '@preserves/core';
|
||||
import chokidar from 'chokidar';
|
||||
|
||||
export type CommandLineArguments = {
|
||||
input: string;
|
||||
|
@ -84,8 +85,9 @@ export function run(options: CommandLineArguments): void {
|
|||
chalk.greenBright('successfully');
|
||||
console.log(chalk.gray(new Date().toISOString()) +
|
||||
` Processed ${r.inputFiles.length} file(s) ${errorSummary}. Waiting for changes.`);
|
||||
const watcher = fs.watch(r.base, { recursive: true }, (_event, filename) => {
|
||||
filename = path.join(r.base, filename);
|
||||
const watcher = chokidar.watch(r.base, {
|
||||
ignoreInitial: true,
|
||||
}).on('all', (_event, filename) => {
|
||||
if (minimatch(filename, options.input)) {
|
||||
watcher.close();
|
||||
runWatch();
|
||||
|
@ -134,7 +136,14 @@ export function runOnce(options: CommandLineArguments): CompilationResult {
|
|||
const outputFilePath = path.join(output, changeExt(relPath, '.ts'));
|
||||
try {
|
||||
const src = fs.readFileSync(inputFilePath, 'utf-8');
|
||||
const schema = readSchema(src, { name: inputFilePath });
|
||||
const schema = readSchema(src, {
|
||||
name: inputFilePath,
|
||||
readInclude(includePath: string): string {
|
||||
return fs.readFileSync(
|
||||
path.resolve(path.dirname(inputFilePath), includePath),
|
||||
'utf-8');
|
||||
},
|
||||
});
|
||||
const schemaPath = relPath.split('/').map(p => p.split('.')[0]).map(Symbol.for);
|
||||
return [{ inputFilePath, outputFilePath, schemaPath, schema }];
|
||||
} catch (e) {
|
||||
|
|
|
@ -90,9 +90,9 @@ export function compile(env: Environment, schema: Schema, options: CompilerOptio
|
|||
seq('... Array<', typeFor(unname(p[1])), '>'));
|
||||
}
|
||||
case M.$setof:
|
||||
return seq('_.KeyedSet<', typeFor(p[0]), '>');
|
||||
return seq('_.KeyedSet', anglebrackets(typeFor(p[0]), '_ptr'));
|
||||
case M.$dictof:
|
||||
return seq('_.KeyedDictionary', anglebrackets(typeFor(p[0]), typeFor(p[1])));
|
||||
return seq('_.KeyedDictionary', anglebrackets(typeFor(p[0]), typeFor(p[1]), '_ptr'));
|
||||
case M.$dict:
|
||||
return parens(seq(
|
||||
block(
|
||||
|
|
|
@ -19,6 +19,7 @@ export const ANDSYM = Symbol.for('&');
|
|||
export const DOT = Symbol.for('.');
|
||||
export const DOTDOTDOT = Symbol.for('...');
|
||||
export const EQUALS = Symbol.for('=');
|
||||
export const INCLUDE = Symbol.for('include');
|
||||
export const ORSYM = Symbol.for('/');
|
||||
|
||||
export type SchemaEnvEntry = { schemaModulePath: ModulePath } & (
|
||||
|
|
|
@ -44,47 +44,77 @@ function invalidPattern(name: string, item: Input, pos: Position | null): never
|
|||
throw new SchemaSyntaxError(`Invalid pattern in ${name}: ${stringify(item)}`, pos);
|
||||
}
|
||||
|
||||
export function readSchema(source: string, options?: ReaderOptions<never>): Schema {
|
||||
const toplevelTokens = new Reader<never>(source, { ... options ?? {}, includeAnnotations: true }).readToEnd();
|
||||
return parseSchema(toplevelTokens);
|
||||
export type SchemaReaderOptions = {
|
||||
readInclude?(includePath: string): string;
|
||||
};
|
||||
|
||||
function _readSchema(source: string, options?: ReaderOptions<never>): Array<Input> {
|
||||
return new Reader<never>(source, {
|
||||
... options ?? {},
|
||||
includeAnnotations: true
|
||||
}).readToEnd();
|
||||
}
|
||||
|
||||
export function parseSchema(toplevelTokens: Array<Input>): Schema {
|
||||
const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT);
|
||||
export function readSchema(source: string,
|
||||
options?: ReaderOptions<never> & SchemaReaderOptions): Schema
|
||||
{
|
||||
return parseSchema(_readSchema(source, options), options ?? {});
|
||||
}
|
||||
|
||||
export function parseSchema(toplevelTokens: Array<Input>,
|
||||
options: ReaderOptions<never> & SchemaReaderOptions): Schema
|
||||
{
|
||||
let version: M.Version | undefined = void 0;
|
||||
let pointer: M.PointerName = false;
|
||||
let definitions = new Dictionary<Pattern, never>();
|
||||
for (const clause of toplevelClauses) {
|
||||
if (!Array.isArray(clause)) {
|
||||
invalidClause(clause);
|
||||
} else if (clause.length >= 2 && is(clause[1], M.EQUALS)) {
|
||||
const name = peel(clause[0]);
|
||||
if (typeof name !== 'symbol') invalidClause(clause);
|
||||
if (!M.isValidToken(name.description!)) {
|
||||
throw new SchemaSyntaxError(preserves`Invalid definition name: ${name}`,
|
||||
position(clause[0]));
|
||||
|
||||
function process(toplevelTokens: Array<Input>): void {
|
||||
const toplevelClauses = splitBy(peel(toplevelTokens) as Array<Input>, M.DOT);
|
||||
for (const clause of toplevelClauses) {
|
||||
if (!Array.isArray(clause)) {
|
||||
invalidClause(clause);
|
||||
} else if (clause.length >= 2 && is(clause[1], M.EQUALS)) {
|
||||
const pos = position(clause[0]);
|
||||
const name = peel(clause[0]);
|
||||
if (typeof name !== 'symbol') invalidClause(clause);
|
||||
if (!M.isValidToken(name.description!)) {
|
||||
throw new SchemaSyntaxError(preserves`Invalid definition name: ${name}`, pos);
|
||||
}
|
||||
if (definitions.has(name)) {
|
||||
throw new SchemaSyntaxError(preserves`Duplicate definition: ${clause}`, pos);
|
||||
}
|
||||
definitions.set(name, parseDefinition(name, clause.slice(2)));
|
||||
} else if (clause.length === 2 && is(clause[0], M.$version)) {
|
||||
version = M.asVersion(peel(clause[1]));
|
||||
} else if (clause.length === 2 && is(clause[0], M.$pointer)) {
|
||||
const pos = position(clause[1]);
|
||||
const stx = peel(clause[1]);
|
||||
const quasiName = 'pointer name specification';
|
||||
pointer = M.asPointerName((stx === false) ? stx
|
||||
: (typeof stx === 'symbol') ? parseRef(quasiName, pos, stx)
|
||||
: invalidPattern(quasiName, stx, pos));
|
||||
} else if (clause.length === 2 && is(clause[0], M.INCLUDE)) {
|
||||
const pos = position(clause[1]);
|
||||
const path = peel(clause[1]);
|
||||
if (typeof path !== 'string') {
|
||||
throw new SchemaSyntaxError(preserves`Invalid include: ${clause}`, pos);
|
||||
}
|
||||
if (options.readInclude === void 0) {
|
||||
throw new SchemaSyntaxError(preserves`Cannot include files in schema`, pos);
|
||||
}
|
||||
process(_readSchema(options.readInclude(path), options));
|
||||
} else {
|
||||
invalidClause(clause);
|
||||
}
|
||||
if (definitions.has(name)) {
|
||||
throw new SchemaSyntaxError(preserves`Duplicate definition: ${clause}`,
|
||||
position(clause[0]));
|
||||
}
|
||||
definitions.set(name, parseDefinition(name, clause.slice(2)));
|
||||
} else if (clause.length === 2 && is(clause[0], M.$version)) {
|
||||
version = M.asVersion(peel(clause[1]));
|
||||
} else if (clause.length === 2 && is(clause[0], M.$pointer)) {
|
||||
const pos = position(clause[1]);
|
||||
const stx = peel(clause[1]);
|
||||
const quasiName = 'pointer name specification';
|
||||
pointer = M.asPointerName((stx === false) ? stx
|
||||
: (typeof stx === 'symbol') ? parseRef(quasiName, pos, stx)
|
||||
: invalidPattern(quasiName, stx, pos));
|
||||
} else {
|
||||
invalidClause(clause);
|
||||
}
|
||||
}
|
||||
|
||||
process(toplevelTokens);
|
||||
|
||||
if (version === void 0) {
|
||||
throw new SchemaSyntaxError("Schema: missing version declaration.", null);
|
||||
}
|
||||
|
||||
return M.asSchema(Record(M.$schema, [new Dictionary<Value>([
|
||||
[M.$version, version],
|
||||
[M.$pointer, pointer],
|
||||
|
|
Loading…
Reference in New Issue