preserves/implementations/javascript/packages/schema/src/meta.ts

128 lines
4.2 KiB
TypeScript
Raw Normal View History

import { is, Position } from '@preserves/core';
2021-03-18 10:15:10 +00:00
import * as M from './gen/schema';
import { SchemaSyntaxError } from './error';
2021-03-22 11:29:27 +00:00
import { isJsKeyword } from './compiler/jskw';
2021-03-11 09:56:49 +00:00
export * from './gen/schema';
2021-03-09 14:59:40 +00:00
export type Input = M._val;
2021-03-09 14:59:40 +00:00
2021-03-21 21:02:34 +00:00
export function isValidToken(s: string): boolean {
return /^[a-zA-Z][a-zA-Z_0-9]*$/.test(s);
}
export function isValidJsId(s: string): boolean {
2021-03-22 11:29:27 +00:00
return /^[$_a-zA-Z][$_a-zA-Z0-9]*$/.test(s) && !isJsKeyword(s);
2021-03-21 21:02:34 +00:00
}
export function jsId(v: string, kf?: () => string): string {
2021-03-22 11:29:27 +00:00
return jsToken(v.replace('$', '$$').replace('*', '$STAR$'), kf);
}
export function jsToken(s: string, kf?: () => string): string {
2021-03-21 21:02:34 +00:00
if (isValidJsId(s)) return s;
if (isValidJsId('$' + s)) return '$' + s;
if (kf !== void 0) return kf();
2021-03-22 11:29:27 +00:00
throw new Error(`Internal error: jsToken needs to be completed (${s})`);
2021-03-09 15:45:57 +00:00
}
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-18 10:15:10 +00:00
export type SchemaEnvEntry = { schemaModulePath: M.ModulePath } & (
2021-03-11 16:59:40 +00:00
({
2021-03-14 21:59:53 +00:00
typescriptModulePath: string | null, // null means it's "this module" in disguise
2021-03-18 10:15:10 +00:00
schema: M.Schema,
2021-03-11 16:59:40 +00:00
}) | ({
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('$');
}
export function lookup<R>(namePos: Position | null,
2021-03-18 10:15:10 +00:00
name: M.Ref,
2021-03-11 09:56:49 +00:00
env: Environment,
2021-03-18 10:15:10 +00:00
kLocal: (p: M.Definition) => R,
kOther: (modId: string, modPath: string, p: M.Definition | null) => R): R
2021-03-11 09:56:49 +00:00
{
for (const e of env) {
2021-03-22 11:13:34 +00:00
if (is(e.schemaModulePath, name.module) ||
(e.typescriptModulePath === null && name.module.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-22 11:13:34 +00:00
const p = e.schema.definitions.get(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
throw new SchemaSyntaxError(`Undefined reference: ${formatRef(name)}`, namePos);
}
2021-03-18 10:15:10 +00:00
export function formatRef(r: M.Ref): string {
2021-03-22 11:13:34 +00:00
return [... r.module, r.name].map(s => s.description!).join('.');
2021-03-09 14:59:40 +00:00
}
2021-03-18 10:15:10 +00:00
2021-03-22 11:13:34 +00:00
export function unnamePattern(p: M.NamedPattern): M.Pattern {
return (p._variant === 'named') ? M.Pattern.SimplePattern(p.value.pattern) : p.value;
2021-03-18 10:15:10 +00:00
}
2021-03-22 11:13:34 +00:00
export function unnameSimplePattern(p: M.NamedSimplePattern): M.SimplePattern {
return (p._variant === 'named') ? p.value.pattern : p.value;
}
export function promoteNamedSimplePattern(p: M.NamedSimplePattern): M.NamedPattern {
return (p._variant === 'named') ? p : M.NamedPattern.anonymous(M.Pattern.SimplePattern(p.value));
}
export function nameFor(p: M.NamedSimplePattern | M.NamedPattern) : string | undefined {
return (p._variant === 'named') ? p.value.name.description! : void 0;
}
export function anonymousSimplePattern(p: M.SimplePattern): M.NamedPattern {
return M.NamedPattern.anonymous(M.Pattern.SimplePattern(p));
}
2021-03-22 11:13:34 +00:00
export function addNameIfAbsent(p: M.NamedSimplePattern, k: M._val): M.NamedSimplePattern {
if (p._variant === 'named') {
return p;
} else {
const s = namelike(k);
if (s !== void 0) {
2021-03-23 15:59:44 +00:00
return M.NamedSimplePattern.named(M.NamedSimplePattern_({
name: Symbol.for(s),
pattern: p.value
}));
} else {
return p;
}
}
}
2021-03-18 10:15:10 +00:00
export function namelike(x: Input): string | undefined {
if (typeof x === 'string') return x;
2021-03-19 22:42:43 +00:00
if (typeof x === 'symbol') return x.description!;
2021-03-18 10:15:10 +00:00
if (typeof x === 'number') return '' + x;
2021-03-19 22:42:43 +00:00
if (typeof x === 'boolean') return '' + x;
2021-03-18 10:15:10 +00:00
return void 0;
}