2021-03-11 13:43:06 +00:00
|
|
|
import { Value, is, Position } from '@preserves/core';
|
2021-03-17 11:38:11 +00:00
|
|
|
import { ModulePath, Ref, Schema, $definitions, Definition, Alternative } from './gen/schema';
|
2021-03-11 09:56:49 +00:00
|
|
|
import { BASE } from './base';
|
2021-03-11 13:43:06 +00:00
|
|
|
import { SchemaSyntaxError } from './error';
|
2021-03-11 09:56:49 +00:00
|
|
|
|
|
|
|
export * from './gen/schema';
|
2021-03-09 14:59:40 +00:00
|
|
|
|
|
|
|
export type Input = Value<never>;
|
|
|
|
|
2021-03-09 15:45:57 +00:00
|
|
|
export function isValidToken(s: string, allowLeadingUnderscore = false): boolean {
|
|
|
|
if (allowLeadingUnderscore) {
|
|
|
|
return /^[a-zA-Z_][a-zA-Z_0-9]*$/.test(s);
|
|
|
|
} else {
|
|
|
|
return /^[a-zA-Z][a-zA-Z_0-9]*$/.test(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-09 14:59:40 +00:00
|
|
|
export const ANDSYM = Symbol.for('&');
|
|
|
|
export const DOT = Symbol.for('.');
|
|
|
|
export const DOTDOTDOT = Symbol.for('...');
|
|
|
|
export const EQUALS = Symbol.for('=');
|
2021-03-11 22:02:18 +00:00
|
|
|
export const INCLUDE = Symbol.for('include');
|
2021-03-09 14:59:40 +00:00
|
|
|
export const ORSYM = Symbol.for('/');
|
|
|
|
|
2021-03-11 16:59:40 +00:00
|
|
|
export type SchemaEnvEntry = { schemaModulePath: ModulePath } & (
|
|
|
|
({
|
2021-03-14 21:59:53 +00:00
|
|
|
typescriptModulePath: string | null, // null means it's "this module" in disguise
|
2021-03-11 16:59:40 +00:00
|
|
|
schema: Schema,
|
|
|
|
}) | ({
|
|
|
|
typescriptModulePath: string,
|
|
|
|
schema: null,
|
|
|
|
})
|
|
|
|
);
|
2021-03-11 09:56:49 +00:00
|
|
|
|
|
|
|
export type Environment = Array<SchemaEnvEntry>;
|
2021-03-09 14:59:40 +00:00
|
|
|
|
2021-03-11 16:59:40 +00:00
|
|
|
function modsymFor(e: SchemaEnvEntry): string {
|
|
|
|
return '_i_' + e.schemaModulePath.map(s => s.description!).join('$');
|
|
|
|
}
|
|
|
|
|
2021-03-11 13:43:06 +00:00
|
|
|
export function lookup<R>(namePos: Position | null,
|
|
|
|
name: Ref,
|
2021-03-11 09:56:49 +00:00
|
|
|
env: Environment,
|
2021-03-17 11:20:50 +00:00
|
|
|
kLocal: (p: Definition) => R,
|
2021-03-17 11:38:11 +00:00
|
|
|
kBase: (p: Alternative) => R,
|
2021-03-17 14:59:46 +00:00
|
|
|
kOther: (modId: string, modPath: string, p: Definition | null) => R): R
|
2021-03-11 09:56:49 +00:00
|
|
|
{
|
|
|
|
for (const e of env) {
|
|
|
|
if (is(e.schemaModulePath, Ref._.module(name)) ||
|
2021-03-14 21:59:53 +00:00
|
|
|
(e.typescriptModulePath === null && Ref._.module(name).length === 0))
|
2021-03-11 09:56:49 +00:00
|
|
|
{
|
2021-03-11 16:59:40 +00:00
|
|
|
if (e.schema === null) {
|
|
|
|
// It's an artificial module, not from a schema. Assume the identifier is present.
|
|
|
|
return kOther(modsymFor(e), e.typescriptModulePath, null);
|
|
|
|
} else {
|
2021-03-17 13:36:44 +00:00
|
|
|
const p = Schema._._field0(e.schema).get($definitions).get(Ref._.name(name));
|
2021-03-11 16:59:40 +00:00
|
|
|
if (p !== void 0) {
|
|
|
|
if (e.typescriptModulePath === null) {
|
|
|
|
return kLocal(p);
|
|
|
|
} else {
|
|
|
|
return kOther(modsymFor(e), e.typescriptModulePath, p);
|
|
|
|
}
|
2021-03-11 09:56:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-09 14:59:40 +00:00
|
|
|
|
2021-03-14 21:59:53 +00:00
|
|
|
if (Ref._.module(name).length === 0) {
|
2021-03-17 13:36:44 +00:00
|
|
|
const p = Schema._._field0(BASE).get($definitions).get(Ref._.name(name));
|
2021-03-17 11:38:11 +00:00
|
|
|
if (p !== void 0) return kBase(p as Alternative);
|
2021-03-09 14:59:40 +00:00
|
|
|
}
|
2021-03-11 09:56:49 +00:00
|
|
|
|
2021-03-11 13:43:06 +00:00
|
|
|
throw new SchemaSyntaxError(`Undefined reference: ${formatRef(name)}`, namePos);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function formatRef(r: Ref): string {
|
2021-03-14 21:59:53 +00:00
|
|
|
return [... r[0], r[1]].map(s => s.description!).join('.');
|
2021-03-09 14:59:40 +00:00
|
|
|
}
|